在分佈式系統中,當多個服務實例競爭同一資源(如庫存扣減、訂單創建)時,需要分佈式鎖保證操作的原子性。與單機鎖(如Java的ReentrantLock)不同,分佈式鎖需解決跨節點、跨進程的互斥問題。本文將解析基於Redis、ZooKeeper和etcd的3種實現方案,及其背後的一致性原理。

一、分佈式鎖的核心要求

無論採用哪種方案,分佈式鎖需滿足4個核心特性:

  1. 互斥性:同一時間只能有一個實例持有鎖;
  2. 安全性:避免死鎖(如實例崩潰後鎖能自動釋放);
  3. 可用性:少數節點故障不影響鎖服務;
  4. 一致性:集羣環境下鎖狀態在各節點保持一致。

二、基於Redis的分佈式鎖

Redis憑藉高性能成為分佈式鎖的主流選擇,核心利用SET命令的原子性。

實現方案

// 基於Redisson的分佈式鎖實現(推薦)
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);

// 獲取鎖(自動過期避免死鎖)
RLock lock = redisson.getLock("order:lock:1001");
try {
    // 嘗試獲取鎖,最多等待10秒,15秒後自動釋放
    boolean locked = lock.tryLock(10, 15, TimeUnit.SECONDS);
    if (locked) {
        // 執行業務邏輯(如扣減庫存)
        updateInventory();
    }
} finally {
    // 釋放鎖
    if (lock.isHeldByCurrentThread()) {
        lock.unlock();
    }
}

一致性原理

  • 單節點:依賴Redis單實例的SET key value NX PX命令(NX確保僅當鍵不存在時設置,PX設置過期時間),通過原子操作保證互斥;
  • 集羣環境:需使用Redisson的RedLock算法,向多個獨立Redis節點申請鎖,只有超過半數節點成功才認為獲鎖,避免單點故障導致的鎖失效。

優缺點

  • 優點:性能極高(單機QPS萬級),適合高併發場景;
  • 缺點:集羣模式下一致性較弱(最終一致性),極端情況可能出現鎖丟失。

三、基於ZooKeeper的分佈式鎖

ZooKeeper基於ZAB協議提供強一致性,適合對可靠性要求高的場景。

實現方案

// 基於Curator的分佈式鎖實現
CuratorFramework client = CuratorFrameworkFactory.builder()
    .connectString("zk1:2181,zk2:2181,zk3:2181")
    .retryPolicy(new ExponentialBackoffRetry(1000, 3))
    .build();
client.start();

// 創建分佈式鎖
InterProcessMutex lock = new InterProcessMutex(client, "/locks/order_1001");
try {
    // 嘗試獲取鎖,最多等待10秒
    if (lock.acquire(10, TimeUnit.SECONDS)) {
        // 執行業務邏輯
        updateInventory();
    }
} finally {
    // 釋放鎖
    if (lock.isAcquiredInThisProcess()) {
        lock.release();
    }
}

一致性原理

  • 利用ZooKeeper的臨時順序節點特性:
  1. 所有實例在/locks目錄下創建臨時順序節點(如/locks/lock-00000001);
  2. 只有序號最小的節點持有者獲鎖;
  3. 其他節點監聽前序節點,當前序節點刪除時(持有鎖的實例釋放或崩潰),觸發重新競爭。
  • 臨時節點特性確保實例崩潰後鎖自動釋放(ZooKeeper檢測到會話超時後刪除節點)。

優缺點

  • 優點:強一致性,無死鎖風險,適合數據一致性要求高的場景(如金融交易);
  • 缺點:性能較低(單次操作百毫秒級),集羣部署複雜度高。

四、基於etcd的分佈式鎖

etcd基於Raft算法提供強一致性,兼具Redis的高性能和ZooKeeper的可靠性。

實現方案

// 基於etcd/clientv3的分佈式鎖實現(Go語言示例)
client, err := clientv3.New(clientv3.Config{
    Endpoints:   []string{"etcd1:2379", "etcd2:2379", "etcd3:2379"},
    DialTimeout: 5 * time.Second,
})
if err != nil {
    log.Fatal(err)
}
defer client.Close()

// 創建鎖
lock := etcdclientv3.NewLock(client, "/locks/order_1001")

// 獲取鎖(自動續租避免超時)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
leaseId, err := lock.Lock(ctx)
if err != nil {
    log.Fatal(err)
}
defer cancel()

// 執行業務邏輯
updateInventory()

// 釋放鎖
_, err = lock.Unlock(context.TODO(), leaseId)
if err != nil {
    log.Fatal(err)
}

一致性原理

  • 利用etcd的Put命令和Lease機制:
  1. 通過Put /locks/order_1001 <value> --lease=xxx創建帶租約的鍵,租約過期自動刪除;
  2. 利用Raft算法保證鍵值對在集羣中的強一致性,確保只有一個實例能成功創建鍵;
  3. 獲鎖實例需定期續租(KeepAlive),避免業務未完成時鎖過期。

優缺點

  • 優點:強一致性(Raft協議),性能優於ZooKeeper,支持Watch機制實時感知鎖狀態;
  • 缺點:相對Redis複雜度略高,客户端生態不如Redis成熟。

五、三種方案對比與選型建議

維度

Redis

ZooKeeper

etcd

一致性

最終一致性(集羣)

強一致性

強一致性

性能

極高(萬級QPS)

中(千級QPS)

高(萬級QPS)

可靠性

依賴RedLock算法

天然可靠

天然可靠

適用場景

高併發,允許短暫不一致

金融級,強一致性要求

雲原生,平衡性能與一致性

選型建議

  • 秒殺、庫存等高頻場景首選Redis,搭配Redisson降低實現複雜度;
  • 金融交易等強一致性場景選ZooKeeper或etcd;
  • 雲原生環境(如Kubernetes)優先選etcd,與基礎設施天然集成。

總結

分佈式鎖的核心是解決跨節點互斥,三種方案分別基於不同的一致性模型:Redis依賴客户端算法保證最終一致性,ZooKeeper和etcd通過集羣協議提供強一致性。實際落地時,需結合業務對性能、一致性的要求選擇方案,同時注意鎖超時時間設置、自動續租、異常釋放等細節,避免出現鎖失效或死鎖問題。