分佈式鎖設計與實踐
分佈式鎖定義
- 分佈式環境下,鎖定全局唯一資源
- 請求處理串行化
- 實際表現互斥鎖
分佈式鎖目的
- 交易訂單鎖定
- 防止重複下單
- 解決業務層冪等問題
- MQ消息冪等性
- 發送消息重複
- 消息消費端去重
- 比如手機提現
- 在用戶對商品下單後,訂單狀態爲待支付,在某一時刻用戶正在對該訂單做支付操作,商家對該訂單進行改價操作
- 狀態的修改行爲需要做串行化處理,避免出現數據錯亂。
基於redis分佈式鎖
- 基於redis分佈式鎖方案
- 唯一線程串行處理
- 實現方式
- Redis Setnx(set if not exists)命令在指定的key不存在時,爲key設置指定的值
- setnx key value expire time
- 設置成功,返回1,設置失敗返回0
- setnx key value expire time
- 存在問題
- 鎖時間不可控
- 無法續租期
- 單點問題
- 單實例存在進程一旦死掉,會徹底阻塞業務流程(無法保證高可用)
- 主從方式,主從數據異步,會存在鎖失效問題
- 官方建議
- redis本身建議使用Redlock算法來保證,但是問題是需要至少三個Redis主從實例來完成,維護成本相對較高。Redlock等同於自己實現簡單的一致性協議,細節繁瑣,且容易出錯。
- 鎖時間不可控
- Redis Setnx(set if not exists)命令在指定的key不存在時,爲key設置指定的值
問題本質:分佈式鎖是CP模型,redis集羣是AP模型通過CP模型解決
高可用分佈式鎖設計目標
- 設計目標
- 強一致性
- 服務高可用,系統穩健
- 鎖自動續約及其自動釋放
- 代碼高度抽象,業務接入極簡
- 可視化管理後臺,監控及管理
高可用分佈式鎖設計方案對比
存儲層產品對比
- | redis | zookeeper | etcd |
---|---|---|---|
一致性算法 | 無 | paxos | raft |
CAP | AP | CP | CP/AP |
高可用 | 主從 | N+1可用 | N+1可用 |
接口類型 | 客戶端 | 客戶端 | http/grpc |
實現 | setNX | createEphemeral | restful API |
- 由於redis無法保證數據一致性
- zk對鎖實現使用創建臨時節點和watch機制。執行效率、擴展性、社區活躍度等低於etcd
- 選擇基於etcd實現
分佈式鎖存儲選型
-
etcd
- 簡單KV
- 強一致性
- 高可用
- 無單點
- 數據高可靠
- 持久化
-
分佈式Client+etcd
- Client TTL模式
使用場景一:申請鎖
- 業務方申請資源鎖,調用時提供key,ttl
- etcd生成uuid,作爲當前鎖的唯一憑證,將(key,uuid,ttl)寫etcd
- 檢查etcd中此key是否存在,如沒有,嘗試寫入key,寫入失敗,拿鎖失敗,寫入成功拿到鎖
- 拿鎖後,心跳線程啓動,心跳線程維持時間爲ttl/3,cas uuid(比較還是不是自己的鎖,是的話就更新),從而將key值續租
- 相關etcd API
- 申請鎖
- curl http://ip:2379/v2/keys/foo -XPUT -d value=bar -d ttl=5 prevExist=false
- CAS更新鎖租約
- curl http://ip:2379/v2/keys/foo?prevValue=prev_uuid -XPUT -d ttl=5 -d ttl=5 -d refresh=true -d prevExist=true
- CAS刪除鎖
- curl http://ip:2379/v2/keys/foo?prevValue=prev_uuid -XDELETE
- 申請鎖
使用場景二:申請鎖,但鎖已被持有
- 業務方申請資源鎖,調用時提過key,ttl
- 檢查etcd中key的存在,若已存在,拿鎖失敗
使用場景三:鎖的清理
- 如果調用方正常結束,通過cas接口調用delete方法自動清理etcd中的key值
- 如果調用方異常終止,等待原有鎖ttl過期後,鎖資源釋放
業務接入
提供客戶端
作業:使用etcd實現分佈式鎖,提供簡易客戶端
獲取鎖平均耗時監控
etcd兼容性測試
- etcd提供了獨有的集羣管理模式,方便進行極端case下的測試,以三個節點的etcd集羣爲例
- 單節點停機,不影響持續寫入,不影響讀,結果有一致性
- 當只有一個節點時,讀會停機,寫入成功
- 理論上只要不是多節點同時停機,線上服務不會受影響
etcd恢復/版本
- etcd有自有的數據恢方式,如果服務停機後,可以將所有數據轉移重啓
- etcd的增刪節點,節點遷移等部署相關,均有相關操作方式
- etcd版本選擇,選擇使用etcd3.2.9,但是因爲V3 API暫時還不完備,建議用V2方式實現
- V3提供gRPC接口
- 天然提供分佈式鎖功能
- 只需要申請鎖,釋放鎖
- 不用關注鎖的租期問題
分佈式鎖特殊場景
-
特殊場景一:分佈式鎖只是在同一自然時間的互斥鎖,本身不解決冪等性問題
- 接入業務需要完善從獲得鎖到釋放鎖中間的數據冪等邏輯
-
特殊場景二:鎖沒有按照預期續租
- 心跳續租沒成功
- 馬上啓動GC,GC時間夠長
-
特殊場景三:etcd內部協調發生問題
- leader節點掛了,選主中
- Raft日誌數據同步發送錯誤或者不一致問題