עבודת אלילים וקורסים
1) מדוע יש צורך בעבודת אלילים
Pagination מגביל את כמות המידע המועבר על ידי הלקוח, מקטין את העומס על אחסון/רשתות, וקובע דרך דטרמיניסטית ”ללכת” דרך האוסף. במערכות אמיתיות, הפגינציה היא לא רק ”עמוד = 1 & גבול = 50”, אלא גם קבוצה של חוזי פרוטוקול ואינווריאנטים עקביים.
מטרות טיפוסיות:- איחוי ושליטה בזיכרון לכל בקשה.
- ניווט יציב בעת שינוי נתונים (הכנס/מחק).
- היכולת להמשיך ממקום (חידוש).
- מטמון וטעינה (הטפה).
- הגנה מפני התעללות (קצב מגביל, תרגיל גב).
2) מודלים לפגינציה
2. 1 קיזוז/הגבלה (מעומד)
הרעיון: ”לדלג על שורות N, להחזיר את M.”
יתרונות: פשטות, תואמת כמעט לכל SQL/NOSQL.
אסירים:- הידרדרות ליניארית: OFSETs גדולים מביאים לסריקה מלאה/דילוג-עלות.
- אי יציבות בזמן הכנסת/מחיקות בין הבקשות (offsets ”לצוף”).
- קשה להבטיח ”התחדשות” מדויקת.
sql
SELECT
FROM orders
ORDER BY created_at DESC, id DESC
OFFSET 1000 LIMIT 50;
2. 2 סמן/Keyset/Seek-Pagination
הרעיון: "תמשיך עם המפתח של קיי. "הסמן הוא המיקום בסט הממוין.
מקצוענים:- O (1) גישה להמשיך עם אינדקס.
- יציבות במהלך שינויים באוסף.
- האיחור הכי טוב ב ”עמודים עמוקים”.
- אנחנו צריכים מפתחות מוגדרים, ייחודיים ומונוטוניים.
- קשה יותר ליישם ולהדיח.
sql
-- Resumption after steam (created_at, id) = (:last_ts,:last_id)
SELECT
FROM orders
WHERE (created_at, id) < (:last_ts,:last_id)
ORDER BY created_at DESC, id DESC
LIMIT 50;
2. 3 אסימוני המשך
הרעיון: השרת מחזיר אסימון אטום שבו מקודדים את ”המיקום” (ואולי גם את מצב השברים/המסננים). הלקוח אינו מבין את הפנימיות ופשוט מחזיר אסימון לדף הבא.
יתרונות: גמישות, היכולת לשנות את התוכנית מבלי לשבור את API.
חסרונות: ניהול לכל החיים, תאימות עם פיקדונות.
2. 4 לוחות זמנים ולוגיקה
מבוסס זמן: ”כל הרשומות עד T”, חותמת זמן סמן (מתאים לחוטים אפנד בלבד).
Log-sequence/offset-based: crowser - offset in the log (קיזוז קפקא, journal seq).
תעודות זהות מונוטוניות גלובליות: Snowflake/UUIDv7 כמפתחות אפשריים לחיפוש יציב.
3) עיצוב קורסים ואסימונים
3. 1 מאפייני סמן טובים
הלקוח הוא עצמאי בפורמט.
חתימת HMAC למניעת זיוף/מניפולציה.
הקשר: כולל מיון, מסננים, סכימה, דייר/שבר.
חיים שלמים: TTL ו ”לא חוזר” כאשר שינוי אינדקסים/זכויות גישה.
גודל: קומפקטי (<= 1-2 KB) המתאים לכתובת.
3. 2 תבנית אסימון
הערימה המומלצת: JSON = דחיסה (zstd/deflate) # Base64URL = HMAC.
מבנה מטען (דוגמה):json
{
"v": 3 ,//token version
"sort": ["created_at:desc","id:desc"],
"pos": {"created_at":"2025-10-30T12:34:56. 789Z","id":"987654321"},
"filters": {"user_id":"42","status":["paid","shipped"]},
"tenant": "eu-west-1",
"shards": [{"s ": "a, "" hi":"..."}] ,//optional: cross-shard context
"issued_at": "2025-10-31T14:00:00Z",
"ttl_sec": 3600
}
'mac = HMAC (סוד, מטען) "נוסף בחלק העליון והכל מקודד לאות מחרוזת אחת.
3. 3 בטיחות
סימן (HMAC/SHA-256).
הצפנה אופטית (AES-GCM) בנוכחות ערכים רגישים (PII).
אימות שרת: גרסה, TTL, סמכות משתמש (RBAC/ABAC).
4) עקביות וחריצות
4. מיון יציב 1
השתמש בדטרמיניזם מלא: ”סדר על ידי DESC, id DESC”.
סוג מפתח חייב להיות ייחודי (הוסף "id' כשובר שוויון).
האינדקס חייב להתאים לאינדקס הכיסוי.
4. 2 תמונות ובידוד
עבור דפים שאינם ג 'מבו, השתמש בתצלום קריא עקבי (MVCC/txid).
אם הצילום אינו מעשי (יקר/הרבה נתונים), גבש חוזה: "הסמן מחזיר אלמנטים רק לפני המיקום. "זה טבעי לשידורי חדשות.
4. 3 הכנסות/מחיקות בין העמודים
מודל חיפוש ממזער ”שכפולים/השמטות”.
מחיקת מסמך/התנהגות שינוי: ”חורים” נדירים בין דפים מותרים, אך לא ”אחורה בזמן”.
5) תחבולות אינדקס ותעודת זהות
אינדקסים מורכבים מסודרים היטב: (created_at DESC, id DESC).
תעודות זהות מונוטוניות: Snowflake/UUIDv7 נותנים פקודה בזמן.
מפתחות חמים: הפצה על ידי מקש-שבר (לדוגמה, "tenant _ id'," region ") ומיון בתוך השבר.
גנרטורים לזיהוי: להימנע מהתנגשויות ו ”החלשת שעון” - סינכרון זמן, ”רגרסיה” במהלך קפיצות NTP.
6) עבודת אלילים חוצת שברים
6. 1 תרשימים
בקשות מקבילות לכל הרסיסים, קורסי חיפוש מקומיים, ואז מיזוג קיי-ווי על הצביר.
פר-שרד קורסורס: האסימון מכיל עמדות על כל שבר.
מגביל מאוורר-out-limit את מספר הרסיסים לכל צעד (קצב מגביל/זמן).
6. 2 אסימונים לריבוי שברים
מערך חנויות '@ shard _ id, last_pos}'. בשלב הבא, קורות חיים עבור כל רסיס פעיל ולהחזיק שוב, נותן את הדף ממוין גלובלית.
7) חוזי פרוטוקול
7. 1 מנוחה
בקשה:
GET /v1/orders? limit=50&cursor=eyJ2IjoiMyIsInNvcnQiOiJjcmVh... (opaque)
תשובה:
json
{
"items": [ /... / ],
"page": {
"limit": 50,
"next_cursor": "eyJ2IjozLCJwb3MiOiJjcmVh...==",
"has_more": true
}
}
המלצות:
- 'limit' with upper lock (לדוגמה, מקסימום = 200).
- 'next _ correzor' חסר אם 'has _ more = שקר'.
- GET idempotence, cachability של תגובות ללא ”הבא _ הסמן” (העמוד הראשון עם מסננים קבועים וצילום).
7. 2 GraphQL (גישה ממסר)
חוזה אופייני לחיבור:graphql type Query {
orders(first: Int, after: String, filter: OrderFilter): OrderConnection!
}
type OrderConnection {
edges: [OrderEdge!]!
pageInfo: PageInfo!
}
type OrderEdge {
node: Order!
cursor: String! // opaque
}
type PageInfo {
hasNextPage: Boolean!
endCursor: String
}
'cursor' must להיות אטום וחתום; אל תשתמש ב ”Base64 גולמי” ללא HMAC.
7. 3 GRPC
השתמש ב- ”page _ size” ו- ”page _ token”:proto message ListOrdersRequest {
string filter = 1;
int32 page_size = 2;
string page_token = 3; // opaque
}
message ListOrdersResponse {
repeated Order items = 1;
string next_page_token = 2; // opaque bool has_more = 3;
}
7. 4 אשכולות ושקעי אינטרנט
עבור קלטות רציפות: סמן כ ”קזז/t שנראה לאחרונה”.
תמיכה 'reson _ from' במהלך החיבור מחדש:json
{ "action":"subscribe", "topic":"orders", "resume_from":"2025-10-31T12:00:00Z#987654321" }
8) מטמון, הטפה, CDN
ETag/IF-NOne-מתאים לדף הראשון עם מסננים יציבים.
Cache-Control עם TTL קצר (לדוגמה, 5-30 S) עבור רשימות ציבוריות.
Prefetch: החזר ”next _ colressor” ורמזים (”Link: rel =” next'), הלקוח יכול לטעון מראש את העמוד הבא.
וריאציות: חשוב על ”סינון/מיון/מיקום/דייר” כמפתח החלק של המטמון.
9) ניהול טעינה והגבלה
קשר עליון 'לימיט', למשל. 200.
server-side backpressure: אם זמן הבקשה הוא> תקציב, צמצם את ה ”גבול” בתגובה (וספר ללקוח במפורש את גודל הדף עצמו).
דרג מגבלות למשתמש/אסימון/דייר.
הפסקה אקספוננציאלית, בקשות חוזרות ונשנות.
10) היבטים UX
גלילה נגד מספור: גלילה אינסופית * corsors; מספר עמודים * (אך הסבר את חוסר הדיוק בעת עדכון הנתונים).
חזור למקום: אחסן את מחסנית סמן הלקוח.
עמודים ריקים: אם 'יש _ עוד = false', אל תציג את הכפתור More.
גבולות יציבים: הצג את ה "סך הכל" המדויק רק אם הוא זול (אחרת הוא משוער "גישה _ סה" כ ").
11) בדיקות ומקרי קצה
רשימות בדיקה:- מיון יציב: פריטים עם אותו 't' לא ”למצמץ”.
- הכנסת/מחיקה - כפילויות אינן מופיעות בצומת העמוד.
- שינוי מסננים בין דפים: יש לדחות את הטוקן כמיושן/לא מתאים.
- Token TL: שגיאה תקפה לאחר פקיעתה.
- עומק גדול: Latency לא לגדול לינארית.
- ריבוי רסיסים: סדר מיזוג נכון, היעדר רסיסי רעב ”איטיים”.
python
Generate N entries with random inserts between calls
Verify that all pages are merged = = whole ordered fetch
12) יכולת תצפית ו ־ SLO
מדדים:- 'רשימה _ בקשה _ latency _ ms' (P50/P95/P99) על ידי אורך עמוד.
- 'search _ index _ hit _ ratio' (הפרופורציה של הבקשות שנותרו על ידי אינדקס הכיסוי).
- 'next _ corressor _ invalid _ rate' (אימות/שגיאות TTL/חתימה).
- 'merge _ fanout' (מספר רסיסים מעורבים בכל עמוד).
- 'duplicates _ on _ bridary' ו- 'gap _ on _ bridary' (גילוי בטלמטריה של הלקוח).
- Correlate 'הסמן _ id' ביומנים, מטען מסכה.
- תוחלת התגיות: ”page _ size”, ”source _ shards”, ”db _ index _ used”.
- זמינות: 99. 9% בשיטות ”הרשימה”.
- Latency: P95 <200 ms for 'page _ size <= 50' במטען מקומי.
- שגיאת סימון: <0. אחוז אחד ממספר השיחות הכולל.
13) נדידה וכושר גישוש
אפשר 'v' באסימון ותומך בגרסאות ישנות יותר של N שבועות.
בעת שינוי מפתחות סוג - לשלוח שגיאה ”רכה” '409 קונפליקט' עם קדימון לבצע רישום טרי ללא סמן.
מקרה קטסטרופלי (שאגת כל האסימונים): שינוי "signing _ key _ id' ודחיית אסימונים ישנים.
14) דוגמאות למימוש
14. 1 טוקן דור (פסאודו-קוד)
python payload = json. dumps({...}). encode()
compressed = zlib. compress(payload)
mac = hmac_sha256(signing_key, compressed)
token = base64url_encode(mac + compressed)
14. 2 אימות אסימון
python raw = base64url_decode(token)
mac, compressed = raw[:32], raw[32:]
assert mac == hmac_sha256(signing_key, compressed)
payload = json. loads(zlib. decompress(compressed))
assert now() - payload["issued_at"] < payload["ttl_sec"]
assert payload["filters"] == req. filters
14. 3 בקש שאילתה בעזרת מפתח מרוכב
sql
-- Page # 1
SELECT FROM feed
WHERE tenant_id =:t
ORDER BY ts DESC, id DESC
LIMIT:limit;
-- Next pages, continued after (ts0, id0)
SELECT FROM feed
WHERE tenant_id =:t
AND (ts <:ts0 OR (ts =:ts0 AND id <:id0))
ORDER BY ts DESC, id DESC
LIMIT:limit;
15) בטיחות וציות
אין לכלול שדות גולמיים באסימונים שמהם ניתן להפיק PII.
תחתום ותגביל את טי-טי-אל.
נסה להפוך אסימונים לבלתי נסבלים בין המשתמשים (כתוב ”תת/דייר/תפקידים/” בפקודה ובבדיקה במהלך אימות).
יומן רק חשיש סמלי.
16) שגיאות תכופות ותבניות אנטי
Base64 (id) כסמן: קל לזייף/להרים, שובר את החוזה בעת שינוי הסוג.
אין שובר שוויון: "סדר על ידי DESC" ללא "id' = = שכפולים/קפיצות.
שינוי מסננים בין דפים מבלי לבטל את האסימון.
איטי ובלתי צפוי.
אסימונים ללא גרסה וטי-טי-אל.
יישום רשימת מיני 17)
1. להגדיר את הסוג ולהוסיף שובר שוויון ייחודי.
2. צור אינדקס עבור סדר זה.
3. בחר מודל: לחפש אסימון אטום +.
4. יישום חתימת אסימונים (ובמידת הצורך, הצפנה).
5. הנח את ה-TTL ו-Versioning.
6. נוסחה ומסמך "יש _ more", "next _ corress' חוזים.
7. תן דעתך לתכנית חוצה שברים (אם יש צורך) ומיזוג קיי-ווי.
8. הוספת מדדים, התראות, ו-SLOs.
9. לכסות את גבולות הדף מבוסס הרכוש עם בדיקות.
10. תאר את אסטרטגיית ההגירה של אסימונים.
18) המלצות קצרות לבחירת גישה
ספריות/חיפושים היכן ש ”מספר הדף” ו ”סך המשוער” חשובים: נניח ”OFSET/LIMIT” + cache; דו "ח שסך הוא משוער.
הזנות, אנליטיקה, רשימות עמוקות, RPS גבוה: סמן/לחפש בלבד.
אוספים שארדיים/מבוזרים: אסימון מיזוג לשבר פר.
אשכולות/מרכז לבקרת מחלות: קיזוזים/טי עם קורות חיים.
19) דוגמה של הסכם API (סיכום)
'קבל/V1/פריטים? גבול = 50 & סמן = "..
התשובה תמיד כוללת דף. להגביל ”, דף”. has_more', העמוד אופציונלי. next_cursor'.
הסמן אטום, חתום, עם TTL.
המיון הוא דטרמיניסטי 'סדר על ידי created_at DESC, id DESC'.
הגדרת התנהגות שינוי פריטים לא ”לחזור” ביחס לסמן.
מטריות ושגיאות הן סטנדרטיות: "inflid _ cordore", "infied _ crowsor", "mismatch _ filters'.
מאמר זה מספק עקרונות ארכיטקטוניים ותבניות מוכנות לעיצוב הפגנות אשר נותרות מהירות, צפויות ומאובטחות אפילו בנתונים גדולים, שארדי ומשתנים באופן פעיל.