在大型 Web 應用中,資料庫往往成為效能瓶頸。傳統做法將排他性鎖邏輯放在資料庫層(行鎖、表鎖、交易),但在高併發場景下,大量鎖操作會嚴重拖垮資料庫效能。本文介紹一種替代方案——分散式鎖服務,將協調邏輯前移,減輕資料庫負擔。
假設需求是保證使用者帳戶資金操作的原子性,且同一時間僅允許一個客戶端對同一物件操作。單機環境下 lock 或單例模式即可解決,但負載平衡架構下,同一邏輯物件可能存在於多個伺服器副本上,這時就需要跨進程、跨機器的協調機制。
利用 Memcached 的 KV 寫入/刪除配合過期時間,可快速構建基礎分散式鎖。但若需求進階——權限控管、父子節點關聯(如鎖定帳戶同步鎖定購物車、積分)、監視器回呼、單點故障容錯——則需更完善的協調服務。Apache ZooKeeper 正是為此設計:開源、分散式、高可用的應用協調服務,支援同步、配置維護、命名服務等。
ZooKeeper 將資料組織為層級命名空間(類似檔案系統),每個節點稱為 ZNode,資料儲存在記憶體中,提供高吞吐、低延遲。節點建立模式分四種:
// 建立連線並註冊預設 Watcher
var zk = new ZooKeeper("192.168.206.129:2181", TimeSpan.FromSeconds(4), new MyWatch());
// 建立持久節點
zk.Create("/locks/account1", data, Ids.OPEN_ACL_UNSAFE, CreateMode.Persistent);
// 臨時順序節點(常用於公平鎖)
zk.Create("/locks/account1/lock-", data, Ids.OPEN_ACL_UNSAFE, CreateMode.EphemeralSequential);
// 刪除節點
zk.Delete("/locks/account1/lock-0000000001", -1);
// 判斷節點是否存在(可掛 Watcher)
zk.Exists("/locks/account1", new MyWatch2());
// 取得資料(可掛 Watcher)
zk.GetData("/locks/account1", new MyWatch2(), stat);
// 設定資料
zk.SetData("/locks/account1", new byte[1], 1);
// 取得子節點
zk.GetChildren("/locks", true);
Watcher 分為 Data Watch(getData、exists、create 觸發)與 Child Watch(getChildren 觸發)。delete 同時觸發兩類。Watcher 為一次性觸發,需重複註冊。
測試環境:Ubuntu 虛擬機(Server)+ Windows 2003(Client)。單機 ZooKeeper 可達每秒數萬次操作,延遲極低。生產環境建議部署 3/5 節點集群(基於 ZAB 協議,類似 Paxos),保證強一致性與高可用。
EPHEMERAL_SEQUENTIAL 實現公平鎖:所有客戶端建立臨時順序節點,序號最小者獲得鎖,其餘監聽前一節點刪除事件ACL 做權限控管分散式鎖服務將協調邏輯從資料庫剝離,配合 ZooKeeper 的強一致性與 Watch 機制,能有效解決高併發下的協調難題,是微服務架構的基礎設施利器。