在现代分布式系统中,多进程或多线程环境下的资源竞争问题尤为突出。为了确保数据的一致性和安全性,分布式锁应运而生。下面介绍三种常用的实现分布式锁的方法,分别是基于数据库的锁、基于Redis的锁以及基于Zookeeper的锁。

一、基于数据库的锁

在传统的关系型数据库中,我们可以使用表来实现分布式锁。具体做法是,创建一张锁表,然后通过事务机制来实现加锁和解锁操作。

-- 创建锁表
CREATE TABLE distributed_lock (
    lock_name VARCHAR(255) PRIMARY KEY,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Java代码示例:

public class DatabaseLock {
    private DataSource dataSource;

    public DatabaseLock(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public boolean acquireLock(String lockName) {
        try (Connection conn = dataSource.getConnection()) {
            conn.setAutoCommit(false);
            PreparedStatement ps = conn.prepareStatement("INSERT INTO distributed_lock (lock_name) VALUES (?)");
            ps.setString(1, lockName);
            int rowsAffected = ps.executeUpdate();
            if (rowsAffected > 0) {
                conn.commit();
                return true; // 成功获取锁
            }
            conn.rollback();
            return false; // 锁已被占用
        } catch (SQLException e) {
            e.printStackTrace();
            return false;
        }
    }

    public void releaseLock(String lockName) {
        try (Connection conn = dataSource.getConnection()) {
            PreparedStatement ps = conn.prepareStatement("DELETE FROM distributed_lock WHERE lock_name = ?");
            ps.setString(1, lockName);
            ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

二、基于Redis的分布式锁

Redis提供了SETNX命令,非常适合用来实现分布式锁。使用SETNX可以在键不存在时设置键值并返回成功,保证了原子性。

import redis.clients.jedis.Jedis;

public class RedisLock {
    private Jedis jedis;
    private static final String LOCK_SUCCESS = "OK";
    private static final Long RELEASE_SUCCESS = 1L;

    public RedisLock(Jedis jedis) {
        this.jedis = jedis;
    }

    public boolean acquireLock(String lockKey, String lockValue, int expireTime) {
        String result = jedis.set(lockKey, lockValue, "NX", "EX", expireTime);
        return LOCK_SUCCESS.equals(result);
    }

    public boolean releaseLock(String lockKey, String lockValue) {
        // Lua脚本原子删除
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        return jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(lockValue)).equals(1L);
    }
}

三、基于Zookeeper的锁

Zookeeper也非常适合用来实现分布式锁,利用它的顺序节点和临时节点特性,能够构建一个可靠的分布式锁机制。

Java代码示例:

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.ACL;
import java.util.Collections;
import java.util.List;

public class ZkLock implements Watcher {
    private ZooKeeper zooKeeper;
    private String lockRoot = "/locks";
    private String lockName;
    private String myZnode;

    public ZkLock(ZooKeeper zooKeeper, String lockName) {
        this.zooKeeper = zooKeeper;
        this.lockName = lockName;
    }

    public boolean acquireLock() throws Exception {
        if (zooKeeper.exists(lockRoot, false) == null) {
            zooKeeper.create(lockRoot, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        }
        myZnode = zooKeeper.create(lockRoot + "/" + lockName, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        return checkLock();
    }

    private boolean checkLock() throws Exception {
        List<String> children = zooKeeper.getChildren(lockRoot, false);
        Collections.sort(children);
        if (myZnode.equals(lockRoot + "/" + children.get(0))) {
            return true; // 成功获得锁
        }
        return false; // 锁被其他节点持有
    }

    public void releaseLock() throws Exception {
        zooKeeper.delete(myZnode, -1);
    }

    @Override
    public void process(WatchedEvent event) {
        // 处理 Zookeeper 事件
    }
}

总结

以上三种方式各有优缺点。基于数据库的锁简单易用,但可能存在性能瓶颈;Redis锁高效且能很好地支持高并发,但需要外部的Redis服务;Zookeeper能够提供高可靠的分布式锁支持,但其复杂性和维护成本相对较高。选择使用哪种方式,需要根据具体的应用场景和需求进行权衡。

点赞(0) 打赏

微信小程序

微信扫一扫体验

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部