分布式锁的实现

目录

1.基于数据库实现分布式锁

2.基于redis的互斥命令实现分布式锁

3.基于zookeeper节点的有序性实现分布式锁


分布式锁是用于在分布式系统中实现资源的并发访问控制,以确保同一时刻只有一个客户端能够访问共享资源。下面是几种常见的分布式锁的实现方式及详解:

1.基于数据库实现分布式锁

基于数据库实现分布式锁是一种可行的方式,但通常不是最佳选择,因为数据库的性能和可伸缩性可能无法满足高并发的需求。然而,如果你仍然需要使用数据库实现分布式锁,以下是一个简单的基于数据库实现分布式锁的示例(以MySQL为例):

首先,我们可以创建一张数据库表来存储分布式锁信息,如下所示:

CREATE TABLE distributed_lock (
    id INT AUTO_INCREMENT PRIMARY KEY,
    lock_key VARCHAR(128) NOT NULL,
    lock_holder VARCHAR(128) NOT NULL,
    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

接下来,我们可以使用数据库的事务来申请和释放锁,并结合唯一索引来确保同一时刻只有一个客户端能够成功申请锁。以下是一个简单的Java示例代码:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class DatabaseDistributedLock {

    private static final String DB_URL = "jdbc:mysql://localhost:3306/your_database";
    private static final String DB_USER = "your_username";
    private static final String DB_PASSWORD = "your_password";

    private Connection connection;

    public DatabaseDistributedLock() throws SQLException {
        connection = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
    }

    public boolean acquireLock(String lockKey, String lockHolder, long timeout) {
        String sql = "INSERT INTO distributed_lock (lock_key, lock_holder) VALUES (?, ?)";
        
        try {
            connection.setAutoCommit(false);

            PreparedStatement statement = connection.prepareStatement(sql);
            statement.setString(1, lockKey);
            statement.setString(2, lockHolder);

            int rowsAffected = statement.executeUpdate();

            connection.commit();
            connection.setAutoCommit(true);

            return rowsAffected > 0; // 如果插入成功,则表示获取锁成功
        } catch (SQLException e) {
            e.printStackTrace();
            try {
                connection.rollback();
                connection.setAutoCommit(true);
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        }
        return false; // 获取锁失败
    }

    public void releaseLock(String lockKey) {
        String sql = "DELETE FROM distributed_lock WHERE lock_key = ?";
        try {
            PreparedStatement statement = connection.prepareStatement(sql);
            statement.setString(1, lockKey);
            statement.execute(); // 释放锁
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

在上面的示例中,我们使用数据库的唯一索引和事务来尝试插入一条记录,以实现获取分布式锁。在释放锁的时候,我们直接删除对应的记录即可。

2.基于redis的互斥命令实现分布式锁

基于Redis的互斥命令实现分布式锁是一种常见且高效的方式。Redis提供了一些原子性的操作,如SETNX(SET if Not eXists)和EXPIRE命令,这些命令可以很好地支持分布式锁的实现。

下面是一个简单的基于Redis的分布式锁的示例(使用Java的Jedis客户端):

import redis.clients.jedis.Jedis;

public class RedisDistributedLock {

    private static final String LOCK_KEY = "my_lock"; // 锁的键名
    private static final int LOCK_EXPIRE = 30000; // 锁的过期时间(毫秒)

    private Jedis jedis;

    public RedisDistributedLock() {
        jedis = new Jedis("localhost"); // 初始化Redis连接
    }

    public boolean acquireLock() {
        String lockValue = String.valueOf(System.currentTimeMillis() + LOCK_EXPIRE + 1); // 为锁生成一个唯一的值
        String result = jedis.set(LOCK_KEY, lockValue, "NX", "PX", LOCK_EXPIRE);
        return "OK".equals(result); // 如果返回OK,则获取锁成功
    }

    public void releaseLock() {
        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, jedis.get(LOCK_KEY));
    }
}

在上述示例中,acquireLock方法使用了Redis的SET命令,其中"NX"表示只在键不存在时设置成功,"PX"表示设置键的过期时间。如果SET命令返回"OK",则表示获取锁成功。

在releaseLock方法中,使用Lua脚本来实现原子性释放锁的操作,首先通过GET命令获取锁的当前值,然后比较锁的当前值是否与之前获取的锁值相等,如果相等则删除键,即释放锁。

3.基于zookeeper节点的有序性实现分布式锁

基于ZooKeeper的分布式锁 ZooKeeper是一个分布式协调服务,可以用于实现分布式锁。通过创建一个有序临时节点,并判断自身是否是子节点中序号最小的节点,来判断是否获得了锁。当释放锁时,直接删除对应的节点即可。

基于ZooKeeper节点有序性实现分布式锁是非常流行和可靠的方式。ZooKeeper提供了有序节点和watch机制,可以很好地支持分布式锁的实现。

以下是一个简单的基于ZooKeeper的分布式锁的示例:

首先,我们新建一个节点存储锁,并创建临时顺序节点作为竞争锁的客户端:

import org.apache.zookeeper.*;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;

public class ZooKeeperDistributedLock {

    private static final String ZK_LOCK_PATH = "/distributed_lock";
    private ZooKeeper zooKeeper;
    private String currentLockPath;
    private CountDownLatch latch = new CountDownLatch(1);

    public ZooKeeperDistributedLock() throws IOException, InterruptedException, KeeperException {
        this.zooKeeper = new ZooKeeper("localhost:2181", 10000, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                if (event.getState() == Event.KeeperState.SyncConnected) {
                    latch.countDown();
                }
            }
        });
        latch.await();
    }

    public void lock() throws InterruptedException, KeeperException {
        currentLockPath = zooKeeper.create(ZK_LOCK_PATH + "/lock_", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        List<String> children = zooKeeper.getChildren(ZK_LOCK_PATH, false);
        Collections.sort(children);
        if (currentLockPath.equals(ZK_LOCK_PATH + "/" + children.get(0))) {
            return;
        } else {
            String subStr = currentLockPath.substring(ZK_LOCK_PATH.length() + 1);
            int preNodeIndex = Collections.binarySearch(children, subStr) - 1;
            zooKeeper.exists(ZK_LOCK_PATH + "/" + children.get(preNodeIndex), true);
        }
    }

    public void unlock() throws InterruptedException, KeeperException {
        zooKeeper.delete(currentLockPath, -1);
    }

    public void close() throws InterruptedException {
        zooKeeper.close();
    }
}

在上述示例中,我们首先创建一个/lock节点,并为每个客户端创建一个临时顺序节点。当一个客户端创建完节点后,将获取/lock节点下的所有子节点,对子节点进行排序,如果当前节点是最小的,表示客户端持有锁,可以执行业务逻辑。否则,阻塞等待前一个节点的删除事件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值