說說你瞭解的分佈式鎖實現

分佈式鎖是分佈式系統中用來解決多個進程或節點之間對共享資源的安全訪問問題的一種機制。

以下是幾種常見的分佈式鎖實現方式:

1. 基於數據庫的分佈式鎖

實現原理:

基於數據庫的分佈式鎖主要利用數據庫的唯一性索引或主鍵等特性來實現。

當需要獲取鎖時,向數據庫中插入一條記錄,如果插入成功,則表示獲取鎖成功;

如果插入失敗(如因唯一索引衝突),則表示鎖已被其他節點持有。

釋放鎖時,從數據庫中刪除相應的記錄。

優缺點:

2. 基於緩存的分佈式鎖(以 Redis 爲例)

實現原理:

Redis 等緩存系統通常支持原子操作,如SETNX(SET if Not eXists)。

當需要獲取鎖時,使用SETNX命令嘗試設置一個鍵值對,如果鍵不存在,則設置成功並返回 1,表示獲取鎖成功;

如果鍵已存在,則返回 0,表示鎖已被其他節點持有。

同時,可以設置鍵的過期時間,以避免死鎖問題。

釋放鎖時,使用DEL命令刪除鍵。

優缺點:

3. 基於 ZooKeeper 的分佈式鎖

實現原理:

ZooKeeper 是一個分佈式協調服務,它提供了臨時節點和順序節點等特性來實現分佈式鎖。

當需要獲取鎖時,客戶端在 ZooKeeper 中創建一個臨時順序節點,然後獲取所有子節點並進行排序。

如果當前節點是所有子節點中最小的(即序號最小),則表示獲取鎖成功;否則,監聽前一個節點,等待其被刪除後重新嘗試獲取鎖。

釋放鎖時,客戶端刪除自己創建的臨時節點。

優缺點:

應用場景

對於上述提到的三種分佈式鎖實現方式(基於數據庫、基於 Redis、基於 ZooKeeper),以下是相應的 Java 示例代碼。

請注意,這些示例是爲了展示基本的實現原理,並不包含完整的錯誤處理和優化。

1. 基於數據庫的分佈式鎖(示例代碼簡化版)

由於基於數據庫的鎖實現通常依賴於數據庫的唯一約束,因此這裏給出一個簡化的 SQL 語句示例,而不是完整的 Java 代碼。

-- 創建鎖表(假設表名爲distributed_lock,包含鎖名稱和鎖持有者的信息)
CREATE TABLE distributed_lock (
    lock_name VARCHAR(255) PRIMARY KEY,
    lock_owner VARCHAR(255),
    lock_time TIMESTAMP
);

-- 獲取鎖(假設鎖名稱爲'my_lock',鎖持有者爲'node1',鎖時間爲當前時間)
-- 如果插入成功,表示獲取鎖成功;如果插入失敗(如因唯一約束衝突),表示鎖已被其他節點持有
INSERT INTO distributed_lock (lock_name, lock_owner, lock_time)
VALUES ('my_lock''node1', NOW())
ON DUPLICATE KEY UPDATE lock_owner = VALUES(lock_owner)lock_time = VALUES(lock_time);

-- 釋放鎖(假設鎖名稱爲'my_lock',且鎖持有者爲'node1')
-- 在實際應用中,可能需要先檢查鎖是否仍然由當前節點持有,以避免誤刪除其他節點的鎖
DELETE FROM distributed_lock WHERE lock_name = 'my_lock' AND lock_owner = 'node1';

2. 基於 Redis 的分佈式鎖(使用 Jedis 客戶端)

import redis.clients.jedis.Jedis;

public class RedisDistributedLock {
    private static final String LOCK_KEY = "my_lock";
    private static final int EXPIRE_TIME = 10; // 鎖過期時間,單位秒
    private static final String LOCK_VALUE = "locked";

    public static boolean tryLock(Jedis jedis, String lockOwnerId) {
        String result = jedis.set(LOCK_KEY, lockOwnerId, "NX""EX", EXPIRE_TIME);
        return "OK".equals(result);
    }

    public static void releaseLock(Jedis jedis, String lockOwnerId) {
        // 使用Lua腳本確保原子性操作:只有當鎖持有者與傳入的值相同時,才刪除鎖
        String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        jedis.eval(luaScript, 1, LOCK_KEY, lockOwnerId);
    }

    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379); // 連接Redis服務器
        String lockOwnerId = "node1"; // 鎖持有者ID(可以是節點名稱、線程ID等)

        // 嘗試獲取鎖
        if (tryLock(jedis, lockOwnerId)) {
            try {
                // 執行臨界區代碼
                System.out.println("Lock acquired, executing critical section...");
            } finally {
                // 釋放鎖
                releaseLock(jedis, lockOwnerId);
                System.out.println("Lock released.");
            }
        } else {
            System.out.println("Failed to acquire lock.");
        }

        jedis.close(); // 關閉Redis連接
    }
}

3. 基於 ZooKeeper 的分佈式鎖(使用 Curator 框架)

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import java.util.concurrent.TimeUnit;

public class ZooKeeperDistributedLock {
    private static final String ZOOKEEPER_ADDRESS = "localhost:2181";
    private static final String LOCK_PATH = "/locks/my_lock";

    public static void main(String[] args) {
        CuratorFramework client = CuratorFrameworkFactory.newClient(
                ZOOKEEPER_ADDRESS,
                new ExponentialBackoffRetry(1000, 3)
        );
        client.start();

        InterProcessMutex lock = new InterProcessMutex(client, LOCK_PATH);

        try {
            // 嘗試獲取鎖,等待最多5秒
            if (lock.acquire(5, TimeUnit.SECONDS)) {
                try {
                    // 執行臨界區代碼
                    System.out.println("Lock acquired, executing critical section...");
                } finally {
                    // 釋放鎖
                    lock.release();
                    System.out.println("Lock released.");
                }
            } else {
                System.out.println("Failed to acquire lock within the specified timeout.");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            client.close();
        }
    }
}

在以上示例中:

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/ByniE88w2FWCeu_ngDnp2g