GH GambleHub

分布式鎖定

1)為什麼(以及何時)需要分布式鎖定

分布式鎖定是一種機制,可確保群集多個節點之間臨界部分的互操作性。典型任務:
  • 背景任務/sheduler的領導力(領導力)。
  • 將單一執行者限制為共享資源(移動文件、遷移模式、獨家支付步驟)。
  • 如果無法實現等效性/排序,則順序處理單元(wallet/order)。
當最好不要使用鎖時:
  • 如果可以進行等效的upsert,CAS(匹配和設置)或按鍵排隊(按鍵排列)。
  • 如果資源允許交換操作(CRDT,計數器)。
  • 如果問題由單個存儲中的事務解決。

2)威脅模型和屬性

故障和復雜性:
  • 網絡:延遲、分離(分區)、數據包丟失。
  • 過程:GC暫停,停止世界,鎖定後崩潰。
  • 時間:時鐘漂移和位移打破TTL方法。
  • 重新擁有:網絡後的「僵屍」過程可能認為它仍然擁有城堡。
所需屬性:
  • 安全:不超過一個有效所有者(安全)。
  • 活著:城堡在主人失靈(liveness)時被釋放。
  • 正義:沒有饑餓。
  • 時鐘獨立性:正確性獨立於時鐘(或由時鐘調整來補償)。

3)主要型號

3.1 Lease(租賃鎖)

TTL發出了鎖。業主有義務將其延長至到期(心跳/保持活力)。

優點:在裂縫中自負。
風險:如果所有者「卷曲」並繼續工作,但失去了延期,則可能會出現雙重所有權。

3.2 Fencing token(護欄令牌)

每次成功捕獲時,都會發出單調增長的數字。資源用戶(DB,隊列,文件存儲)檢查令牌並拒絕使用舊編號的操作。
這在TTL/租賃和網絡拆分中至關重要-可以防止「舊」所有者。

3.3 Quorum鎖(CP系統)

使用分布式共識(Raft/Paxos;etcd/ZooKeeper/Consul),記錄與共識日誌相關聯→在大多數節點上沒有分裂中斷。

另外:強大的安全保障。
減:對法定人數的敏感性(如果失去法定人數,其生存能力將減弱)。

3.4個AP鎖(內存/緩存+復制)

例如,Redis群集。高可用性和速度,但沒有嚴格的網絡分離安全保證。需要在合成器側面進行彎曲。

4)平臺和模式

4.1 etcd/ZooKeeper/Consul(建議使用強鎖)

短暫節點(ZK)或會話/leases(etcd):關鍵在會話現場時存在。
會期保持性;法定人數的損失→會議到期→鎖被釋放。
等候隊列的序節點(ZK'EPHEMERAL_SEQUENTIAL) →公平性。

Etcd(Go)上的草圖:
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(整潔)

經典是「SET key value NX PX ttl」。

問題:
  • 復制/傳送器可以允許同時擁有者。
  • 多個實例的雷德洛克(Redlock)降低了風險,但無法消除;在網絡不可靠的環境中有爭議。

將Redis應用為快速協調層更安全,但始終在目標資源中補充加密令牌。

示例(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群集(過程/會話)中的鎖。
好吧,當所有關鍵部分都處於一個DB中時。

SQL:

sql
SELECT pg_try_advisory_lock(42); -- take
SELECT pg_advisory_unlock(42); -- let go

4.4個文件/雲「鎖」

S3/GCS+具有「If-Match」(ETag)條件的lock對象元數據→本質上是CAS。
適合備用/遷移。

5)安全鎖設計

5.1所有者身份

存儲「owner_id」(節點#process #pid#start_time)+unlock時用於對賬的隨機令牌。
重復解鎖不應拆除別人的鎖。

5.2 TTL和延期

TTL <T_fail_detect(故障檢測時間)和≥ p99操作關鍵部分×庫存。
擴展-定期(例如,每個「TTL/3」),具有截止日期。

5.3 Fencing token on shinka

改變外部資源的部分必須傳遞「fencing_token」。

Sink(DB/緩存/存儲)存儲「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中,是具有修訂版/發行版的鑰匙;根據「mod_revision」排序。

5.5分裂大腦的行為

CP方法:沒有法定人數,就無法鎖定-停機時間比打破安全性更好。
AP方法:允許在分離的島嶼上取得進展→需要進行調整。

6)領導力(領導力)

在etcd/ZK中-「領導者」是獨家的epemer鍵;其余的訂閱更改。
領導者寫心跳;損失-連任。
所有領導者操作都伴隨著fencing token(時代/修訂號)。

7)錯誤及其處理

客戶拿起城堡,但在→規範之前沒有人會受到傷害。TTL/會議將騰空。

城堡在工作的中間到期:
  • 看門狗是強制性的:如果延期失敗,則中斷關鍵部分並回滾/補償。
  • 沒有「下車」:沒有鎖,關鍵部分就無法繼續。

一個漫長的停頓(GC/stop-the-world)→擴展沒有發生,另一個接管了城堡。工作流必須檢測所有權的損失(保持通道)並中斷。

8)Dedlocks,優先事項和倒置

在分布式世界中,Dedlocks很少見(鎖通常是一個鎖定),但是如果鎖有幾個鎖定,則堅持單個取回順序(鎖定命令)。
優先級倒置:低優先級所有者在高優先級等待時保留資源。解決方案:TTL限制,提前(如果業務允許),共享資源。
禁食:使用等待隊列(ZK子順序節點)實現公平。

9)可觀察性

度量標準:
  • `lock_acquire_total{status=ok|timeout|error}`
  • `lock_hold_seconds{p50,p95,p99}`
  • 「fencing_token_valu」(單調)
  • `lease_renew_fail_total`
  • 'split_brain_prevented_total'(由於缺乏法定人數而拒絕了多少次嘗試)
  • `preemptions_total`, `wait_queue_len`
Logi/Tracing:
  • `lock_name`, `owner_id`, `token`, `ttl`, `attempt`, `wait_time_ms`, `path` (для ZK), `mod_revision` (etcd).
  • 帶有結果的「acquire → critical section → release」。
Alerts:
  • 「lease_renew_fail_total」的增長。
  • `lock_hold_seconds{p99}` > SLO.
  • 「Orfen」鎖(沒有心跳)。
  • 等待隊列膨脹。

10)實例

10.1安全的Redis鎖定與fencing(偽)

1.將令牌計數器存儲在可靠的堆棧中(例如Postgres/etcd)。
2.成功的「SET NX PX」會讀取/嵌入令牌,並且所有資源更改都將通過DB/服務中的令牌驗證來完成。

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 + watchdog (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, Curator)

java
LeaderSelector selector = new LeaderSelector(client, "/leaders/cron", listener);
selector. autoRequeue();
selector. start(); // listener. enterLeadership() с try-finally и heartbeat

10.4 Postgres advisory lock with dedline (SQL+app)

sql
SELECT pg_try_advisory_lock(128765); -- attempt without blocking
-- if false --> return via backoff + jitter

11)花花公子(Game Days)

失去法定人數:禁用1-2個etcd節點→嘗試取鎖不應通過。
GC暫停/停止世界:人為地延遲所有者的流動,→檢查看門狗正在中斷工作。
Split-brain:模仿城堡所有者和看門狗之間的網絡鴻溝→新主人獲得更高的折騰,舊的-被合成器拒絕。
Clock skew/Drift:從所有者那裏移走手表(對於Redis/lease) →確保通過令牌/檢查確保正確性。
發布前崩潰:TTL/會話釋放 →鎖的過程下降。

12)反模式

幹凈的TTL鎖,當訪問外部資源時,不帶扣。
依靠本地時間來正確無誤(沒有HLC/fencing)。
通過一個Redis向導在帶有操縱器的環境中分發鎖,而無需確認副本。
無限關鍵部分(TTL「世紀」)。
在沒有「owner_id」/token對賬的情況下拆除「外星人」鎖。
缺少backoff+Jitter →嘗試的「風暴」。
一站式全球鎖-一袋沖突;在鑰匙上縫合更好。

13)實施支票

  • 定義了資源的類型以及是否可以通過CAS/隊列/冪等來實現。
  • 選擇了一種機制:etcd/ZK/Consul for CP;Redis/緩存-僅帶調試。
  • 實現:「owner_id」,TTL+擴展,看門狗,正確解鎖。
  • 外部資源會檢查fencing token(單調性)。
  • 有領導和失敗的策略。
  • 設置指標、異常值、令牌和修訂版本。
  • 在acquire上提供backoff+jitter和taymout。
  • 舉行比賽日:法定人數,分裂大腦,GC暫停,時鐘跳躍。
  • 關於采取多個鎖的順序的文件(如果需要)。
  • 退化計劃(brownout):在無法進入城堡的情況下該怎麼辦。

14) FAQ

Q: Redis鎖定是否足夠「SET NX PX」?
A:只有當資源檢查了fencing token時。否則,在網絡共享中,兩個所有者是可能的。

Q: 默認情況下選擇什麼?

答:對於嚴格的保證是etcd/ZooKeeper/Consul (CP)。對於一個DB內的輕量級任務-advisory locks Postgres。Redis只是一個騙局。

問:什麼是TTL上市?
答:「TTL ≥ p99的關鍵部分持續時間× 2」,並且足夠短,可以快速清理「僵屍」。擴展-每個「TTL/3」。

問:如何避免饑餓?
A:順序等待隊列(ZK序列)或公平算法;嘗試限制和公平規劃。

Q: 是否需要時間同步?

答:要正確無誤,請使用fencing。對於可操作的可預測性-是的(NTP/PTP),但不要依賴鎖邏輯中的墻時鐘。

15)結果

強大的分布式鎖定是在具有lease+keepalive的法定支架(etcd/ZK/Consul)上構建的,並且必須通過在可變資源級別上的鎖定令牌來補充。任何沒有圍欄的TTL/Redis方法都是破裂的風險。首先考慮低成本和偶然性,在沒有低成本的情況下使用鎖定,測量,測試故障模式-並且「關鍵部分」僅在意義上而不是事件數量上仍然至關重要。

Contact

與我們聯繫

如有任何問題或支援需求,歡迎隨時聯絡我們。我們隨時樂意提供協助!

開始整合

Email 為 必填。Telegram 或 WhatsApp 為 選填

您的姓名 選填
Email 選填
主旨 選填
訊息內容 選填
Telegram 選填
@
若您填寫 Telegram,我們將在 Email 之外,同步於 Telegram 回覆您。
WhatsApp 選填
格式:國碼 + 電話號碼(例如:+886XXXXXXXXX)。

按下此按鈕即表示您同意我們處理您的資料。