מנעולים מפוזרים
1) מדוע (ומתי) יש צורך במנעולים מבוזרים
חסימה מבוזרת היא מנגנון המבטיח הדרה הדדית לקטע קריטי בין כמה צומתי אשכול. משימות טיפוסיות:- בחירות מנהיג למשימת הרקע/צל.
- הגבלה של שחקן יחיד על משאב משותף (תנועת קבצים, תרשים הגירה, צעד תשלום בלעדי).
- עיבוד רציף של הצבירה (ארנק/סדר) אם לא ניתן להשיג אידמפוטנטיות/הזמנה אחרת.
- אם אתה יכול לעשות אידמפוטנט Upsert, CAS (השוואה וקביעה) או הזמנה לכל מפתח.
- אם המשאב מאפשר פעולות קומוטטיביות (CRDT, counters).
- אם הבעיה נפתרת על ידי עסקה בחנות אחת.
2) מודל איום ומאפיינים
כשלים וסיבוכים:- רשת: עיכובים, מחיצה, אובדן מנות.
- תהליכים: השהיית ג 'י-סי, עצירת העולם, התרסקות אחרי לכידת מנעול.
- זמן: סחף השעון והעתקתו שוברים גישות TTL.
- תהליך ה ”זומבי” אחרי הרשת עשוי לחשוב שהוא עדיין הבעלים של הטירה.
- בטיחות: לא יותר מבעלים אחד תקף (בטיחות).
- הישרדות: המנעול משוחרר כאשר הבעלים נכשל (לביאה).
- צדק: אין צום.
- עצמאות השעון: תקינות אינה תלויה בקיר-שעון (או פיצוי על-ידי אסימונים).
3) מודלים עיקריים
3. 1 חכירה (מנעול שכור)
המנעול מונפק באמצעות טי-טי-אל. הבעלים מחויב לחדש אותו לפני התפוגה (פעימות לב/שמירה).
ריסוק קליטה עצמית.
סיכונים: אם הבעלים ”תקוע” וממשיך לעבוד, אבל איבד את הארכה, בעלות כפולה עלולה להתעורר.
3. 2 אסימון סיף
עם כל לכידה מוצלחת, מספר גדל באופן חד משמעי מונפק. צרכני משאבים (מסד נתונים, תור, אחסון קבצים) בדקו את הסימן ודחו את הפעולות עם המספר הישן.
זה מאוד חשוב עבור TTL/חכירה ומחיצות רשת - זה מגן מפני הבעלים ”הישן”.
3. 3 מנעולי קוורום (מערכות CP)
נעשה שימוש בקונצנזוס מבוזר (Raft/Paxos; etcd/OdKeeper/Consul), השיא משויך ליומן קונצנזוס.
בנוסף, ערבויות אבטחה חזקות.
מינוס: רגישות למניין (כאשר הוא אבוד, הישרדות היא עלובה).
3. 4 מנעולי AP (זיכרון/מטמון + שכפול)
לדוגמה, אשכול רדיס. זמינות גבוהה ומהירות, אבל ללא ערבויות אבטחה חזקות למחיצות רשת. דורש גידור בצד של החבורה.
4) פלטפורמות ותבניות
4. 1 etcd/שומרת/קונסול (מומלץ למנעולים חזקים)
צמתים Ephemeral (ZK) או sessions/leases (etcd): המפתח קיים בזמן ההפעלה.
הפעלה נשארת; אובדן מניין = ההפעלה פגה = המנעול משוחרר.
צומתי רצף (ZK 'EPHEMERAL _ SEQUENTIAL) עבור תור ההמתנה.
go cli, _:= clientv3. New(...)
lease, _:= cli. Grant(ctx, 10) // 10s lease sess, _:= concurrency. NewSession(cli, concurrency. WithLease(lease. ID))
m:= concurrency. NewMutex(sess, "/locks/orders/42")
if err:= m. Lock(ctx); err!= nil { / handle / }
defer m. Unlock(ctx)
4. 2 redis (מסודר)
קלאסי - ”הגדרת ערך מפתח NX PX ttl”.
בעיות:- שכפול/פיילובר עשוי לאפשר בעלים בו זמנית.
- נעילה מחודשת ממספר מקרים מפחיתה את הסיכון, אך אינה מחסלת; שנוי במחלוקת בסביבות עם רשת לא אמינה.
זה בטוח יותר להשתמש ברדיס כשכבת קואורדינציה מהירה, אבל תמיד משלים את הסיוף במשאב המטרה.
דוגמה (Lua-Unlock):lua
-- release only if value matches if redis. call("GET", KEYS[1]) == ARGV[1] then return redis. call("DEL", KEYS[1])
else return 0 end
4. 3 מנעולי DB
מנעולי ייעוץ PostgreSQL: ננעלים בתוך אשכול Postgres (תהליך/הפעלה).
זה טוב כשכל החלקים הקריטיים כבר נמצאים באותו מסד נתונים.
sql
SELECT pg_try_advisory_lock(42); -- take
SELECT pg_advisory_unlock(42); -- let go
4. 4 מנעולי קובץ/ענן
S3/GCS + object metadata lock עם 'If-Match' (Etag) CAS.
מתאים לגיבויים/נדידה.
5) עיצוב מנעול בטיחות
5. זהות בעלים 1
Store 'ovner _ id' (# pid # start _ time node) + אסימון אקראי לאימות.
לפתוח שוב ושוב לא צריך להסיר מנעול של מישהו אחר.
5. 2 TTL וסיומת
TTL <T_fail_detect (זמן גילוי פגמים) ו ־ p99 של פעולת החתך הקריטי xserve.
חידוש - באופן מחזורי (לדוגמה, כל TTL/3), עם תאריך יעד.
5. 3 אסימון גידור על חבורה
סעיף שינוי המשאב החיצוני חייב לעבור ”סיף _ טוקן”.
Sink (DB/cache/storage) 'last _ token' ודוחה את אלה הקטנים יותר:sql
UPDATE wallet
SET balance = balance +:delta, last_token =:token
WHERE id =:id AND:token > last_token;
5. 4 תור המתנה וצדק
ב ־ ZK - 'EPHEMERAL _ SEQUENTIAL' ומשקיפים: הלקוח ממתין לשחרור של קודמו הקרוב.
ב ־ etcd - מפתחות עם revision/versioning; סדר על ידי 'mod _ revision'.
5. 5 התנהגות של מוח מפוצל
גישת CP: ללא מניין, אתה לא יכול לנעול - עדיף לעמוד מאשר לשבור את הבטיחות.
גישת AP: יש צורך בהתקדמות באיים מחולקים.
6) בחירות למנהיג
ב-etcd/ZK, ”המנהיג” הוא מפתח אפמרי בלעדי; השאר נרשמים לשינויים.
מנהיג כותב פעימות לב; הפסד - בחירה מחדש.
נלווה את כל הפעולות המובילות באות סיוף (Era/Revision number).
7) שגיאות ועיבוד
הלקוח לקח את המנעול, אבל לקרוס לעבוד = הנורמה, אף אחד לא יסבול; TTL/הפעלה ישוחרר.
המנעול פג באמצע העבודה:- כלב שמירה חובה: אם ההארכה נכשלת, לקטוע את החלק הקריטי ולהתגלגל בחזרה/לפצות.
- בלי ”סיום מאוחר יותר”: בלי מנעול, לא ניתן להמשיך את החלק הקריטי.
הפסקה ארוכה (GC/stop-the-world) = הסיומת לא התרחשה, והשנייה נעלה את המנעול. זרם העבודה חייב לזהות את אובדן הבעלות (ערוץ שמירה) ולהפיל.
8) דדלוקי, סדר עדיפויות והיפוך
דדלוקי בעולם מפוזר הם נדירים (בדרך כלל יש טירה אחת), אבל אם יש כמה טירות, לדבוק בסדר אחד של לקיחה (הזמנת מנעול).
היפוך עדיפות: בעלים בעדיפות נמוכה מחזיק משאב בעוד בעדיפות גבוהה אחד לחכות. פתרונות: מגבלות TTL, קדימות (אם העסק מאפשר), זיוף המשאב.
צום: השתמש בתרי המתנה (ZK-תת סדר צמתים) להגינות.
9) יכולת תצפית
מדדים:- ”lock _ eque _ total 'status' out 'time' error”
- ”lock _ hold _ seconds _ p50, p95, p99”
- 'fincing _ token _ value' (מונוטוני)
- 'חכירה _ renew _ fall _ total&fost
- 'split _ brain _ mented _ total' (מספר הניסיונות נדחה בשל היעדר מניין)
- ”קדימות _ סה” כ ”,” חכה _ תור _ lenfoss
- 'Lock _ name', 'בעלים _ id',' token ',' tl', 'tl', 'ניסיון', 'wait _ time _ ms',' path '(trach ZK),' mod _ revision '(etcd).
- ”תרכוש את החלק הקריטי = = שחרור” מסתכם בתוצאה.
- Growth 'lease _ renew _ fall _ total'.
- ”Lock _ hold _ seconds [p99]”> SLO.
- מנעולי ”יתום” (ללא פעימות לב).
- רשימות המתנה נפוחות.
10) מחקרי מקרה
10. 1 מנעול רדיס מאובטח עם סיף (פסאודו)
1. אנו מאחסנים את המזנון בחנות אמינה (לדוגמה, Postgres/etcd).
2. אם 'SET NX PX' הוא מוצלח, אנו קוראים/מעלים את האסימון ומבצעים את כל השינויים במשאב
python acquire token = db. next_token ("locks/orders/42") # monotone ok = redis. set("locks:orders:42", owner, nx=True, px=ttl_ms)
if not ok:
raise Busy()
critical op guarded by token db. exec("UPDATE orders SET... WHERE id=:id AND:token > last_token",...)
release (compare owner)
10. 2 etcd Mutex + כלב שמירה (Go)
go ctx, cancel:= context. WithCancel(context. Background())
sess, _:= concurrency. NewSession(cli, concurrency. WithTTL(10))
m:= concurrency. NewMutex(sess, "/locks/job/cleanup")
if err:= m. Lock(ctx); err!= nil { /... / }
// Watchdog go func() {
<-sess. Done ()//loss of session/quorum cancel ()//stop working
}()
doCritical (ctx )//must respond to ctx. Done()
_ = m. Unlock(context. Background())
_ = sess. Close()
10. 3 מנהיגות ב ־ ZK (Java, אוצר)
java
LeaderSelector selector = new LeaderSelector(client, "/leaders/cron", listener);
selector. autoRequeue();
selector. start(); // listener. enterLeadership() с try-finally и heartbeat
10. נעילה מייעצת 4 postgres עם תאריך יעד (יישום SQL +)
sql
SELECT pg_try_advisory_lock(128765); -- attempt without blocking
-- if false --> return via backoff + jitter
11) ספרי משחק (ימי משחק)
אובדן מניין: בטל 1-2 צמתים etcd = הניסיון לקחת את המנעול לא צריך לעבור.
GC-pause/stop-the-world: לעכב באופן מלאכותי את זרימת הבעלים.
פיצול מוח: הדמיה של הפרדת הרשת בין הבעלים לבין הצד של הטירה = הבעלים החדשים מקבלים אסימון סיף גבוה יותר, הישן נדחה על ידי הכחול.
skew/drift: קח את השעון מהבעלים (עבור Redis/lease) * ודא כי התקינות מובטחת על ידי אסימונים/צ 'קים.
Crash לפני השחרור: process crash # lock משוחרר באמצעות TTL/session.
12) אנטי דפוסים
נעילת TL נקייה ללא גידור בעת גישה למשאב חיצוני.
הסתמכו על זמן מקומי עבור תקינות (לא HLC/Signing).
הפצה של מנעולים דרך אדון רדיס אחד בסביבה עם פליאובר וללא אישור של העתקים.
קטע קריטי אינסופי (TTL ”לדורות”).
הסרת מנעול של מישהו אחר מבלי לבדוק "בעלים _ id'/אסימון.
חוסר גיבוי + עצבנות = סערת ניסיונות.
מנעול גלובלי אחד ”לכל דבר” - שק של קונפליקטים; כריתת מפתח היא טובה יותר.
13) רשימת מימושים
[ ] סוג משאב מוגדר וניתן לוותר על תור/תור/אידמפוטנטיות CAS.
מנגנון [ ] נבחר: etcd/ZK/Console for CP; רדיס/מטמון - רק עם סיף.
[ ] מיושם: "בעלים _ id', TTL + הרחבה, כלב שמירה, נכון לפתוח.
[ ] המשאב החיצוני בודק את אסימון הסיוף (מונוטוני).
[ ] יש אסטרטגיית מנהיגות וכישלון.
[ ] הגדרות מדדים, התראות, כריתת אסימונים ותיקונים.
[ ] Backoff + jiter ולרכוש פסקי זמן מסופקים.
[ ] ימי משחק: מניין, מוח מפוצל, ג 'י-סי פאוס, שעון דק.
[ ] תיעוד הנוהל לנקיטת מספר מנעולים (במקרה הצורך).
[ תוכנית ] מה לעשות כשהמנעול לא זמין.
14) FAQ
Q: האם ”סט NX PX” Redis נעילה מספיק?
א ': רק אם המשאב יבדוק את אות הסיף. אחרת, עם חלוקת רשת, שני בעלים אפשריים.
Q: מה לבחור ”כברירת מחדל”?
א. לערבויות קפדניות - etcd/Zookeeper/Consul (CP). למשימות קלות בתוך מסד נתונים אחד - מנעולים מייעצים Postgres. רדיס - רק עם סיף.
Q: איזה TTL לשים?
A: "TTL with p99 פרק זמן קריטי x 2" וקצר מספיק כדי לנקות במהירות "זומבים. "חידוש - כל 'TL/3'.
קיו: כיצד להימנע מצום?
A: תור המתנה לפי סדר (ZK sequential) או אלגוריתם הגינות; מגבלה של ניסיונות ותכנון הוגן.
קיו: האם אני זקוק לסנכרון זמן?
א ": למען התקינות - אין (השתמש בסיף). לחיזוי תפעולי, כן (NTP/PTP), אבל לא להסתמך על קיר שעון עבור היגיון מנעול.
15) סיכומים
מנעולים מבוזרים אמינים בנויים על רמות קוורום (etcd/ZK/Consul) עם חכירה + kepalive, והם בהכרח משלימים על ידי סיוף אסימון ברמת המשאב המשתנה. כל גישות TTL/רדיס ללא גידור הן סיכון מפוצל מוח. חשוב תחילה על סיבתיות ועל אידמפוטנטיות, השתמש במנעולים שבהם זה בלתי אפשרי בלעדיהם, מדידה, מבחני תקלות - ו ”הקטעים הקריטיים” שלך יישארו קריטיים רק במשמעות, לא במספר האירועים.