1. 数据库分布式锁
实现原理
-
基于唯一索引:
-
创建一张锁表,通过唯一索引(如锁名称)保证互斥性。
-
加锁:插入一条记录,成功则获取锁,失败则重试。
-
解锁:删除对应记录。
-
-
乐观锁(版本号):
-
使用版本号字段,更新时检查版本号是否匹配。
-
-
悲观锁(SELECT FOR UPDATE):
-
通过数据库行锁锁定记录(如
SELECT ... FOR UPDATE
)。
-
// 基于唯一索引的简单实现(JDBC示例)
public class DatabaseLock {
private Connection connection;
public boolean tryLock(String lockKey, String clientId, int expireSeconds) {
try {
String sql = "INSERT INTO distributed_lock (lock_key, client_id, expire_time) VALUES (?, ?, ?)";
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setString(1, lockKey);
stmt.setString(2, clientId);
stmt.setTimestamp(3, new Timestamp(System.currentTimeMillis() + expireSeconds * 1000));
return stmt.executeUpdate() > 0;
} catch (SQLException e) {
return false;
}
}
public void unlock(String lockKey, String clientId) {
try {
String sql = "DELETE FROM distributed_lock WHERE lock_key = ? AND client_id = ?";
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setString(1, lockKey);
stmt.setString(2, clientId);
stmt.executeUpdate();
} catch (SQLException ignored) {}
}
}
优缺点
-
优点:
-
实现简单,无需额外依赖。
-
-
缺点:
-
性能低(依赖数据库IO)。
-
无自动超时机制(需手动清理过期锁)。
-
单点故障风险。
-
适用场景
-
低频并发场景,或作为临时方案。
2. Redis分布式锁
实现原理
-
单节点锁(SETNX + EXPIRE):
-
加锁:使用
SET key value NX EX seconds
原子命令设置锁并指定超时时间。 -
解锁:通过 Lua 脚本校验客户端唯一标识后删除锁。
-
-
RedLock算法(多节点容错):
-
向多个独立 Redis 节点请求锁,半数以上成功视为加锁成功。
-
Java代码示例(Jedis客户端)
public class RedisLock {
private Jedis jedis;
private String lockKey;
private String clientId;
private int expireTime;
public RedisLock(Jedis jedis, String lockKey, int expireTime) {
this.jedis = jedis;
this.lockKey = lockKey;
this.clientId = UUID.randomUUID().toString();
this.expireTime = expireTime;
}
public boolean tryLock() {
// 加锁:SET lockKey clientId NX EX expireTime
String result = jedis.set(lockKey, clientId, SetParams.setParams().nx().ex(expireTime));
return "OK".equals(result);
}
public void unlock() {
// 解锁:通过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, Collections.singletonList(lockKey), Collections.singletonList(clientId));
}
}
优缺点
-
优点:
-
高性能(基于内存操作)。
-
支持自动超时,避免死锁。
-
-
缺点:
-
时钟漂移可能导致锁提前释放。
-
RedLock 算法实现复杂,依赖多节点。
-
适用场景
-
高并发场景(如秒杀、限流)。
3. ZooKeeper分布式锁
实现原理
-
临时顺序节点:
-
加锁:客户端在 ZooKeeper 的锁目录下创建临时顺序节点。
-
监听机制:判断当前节点是否为最小节点,是则获取锁;否则监听前一个节点的删除事件。
-
-
临时节点:
-
创建临时节点,成功则获取锁,否则监听节点删除。
-
public class ZkLock {
private CuratorFramework client;
private String lockPath;
private InterProcessMutex lock;
public ZkLock(CuratorFramework client, String lockPath) {
this.client = client;
this.lockPath = lockPath;
this.lock = new InterProcessMutex(client, lockPath);
}
public boolean tryLock(long timeout, TimeUnit unit) {
try {
return lock.acquire(timeout, unit);
} catch (Exception e) {
return false;
}
}
public void unlock() {
try {
lock.release();
} catch (Exception ignored) {}
}
}
// 使用示例
CuratorFramework client = CuratorFrameworkFactory.newClient("localhost:2181", new RetryNTimes(3, 1000));
client.start();
ZkLock zkLock = new ZkLock(client, "/locks/resource");
try {
if (zkLock.tryLock(10, TimeUnit.SECONDS)) {
// 执行业务逻辑
}
} finally {
zkLock.unlock();
}
优缺点
-
优点:
-
强一致性(ZooKeeper 的 ZAB 协议)。
-
自动释放锁(临时节点断开即删除)。
-
支持公平锁(顺序节点)。
-
-
缺点:
-
性能低于 Redis(频繁的节点操作和监听)。
-
需维护 ZooKeeper 集群。
-
适用场景
-
需要强一致性和高可靠性的场景(如金融交易)。
对比总结
特性 | 数据库锁 | Redis锁 | ZooKeeper锁 |
---|---|---|---|
实现复杂度 | 简单 | 中等 | 复杂 |
性能 | 低 | 高 | 中 |
可靠性 | 低 | 中(依赖集群) | 高 |
自动超时 | 手动实现 | 支持 | 支持(临时节点) |
公平性 | 不支持 | 不支持 | 支持 |
适用场景 | 低频简单场景 | 高并发场景 | 强一致性场景 |
选择建议
注意事项
-
锁超时时间:根据业务操作合理设置,避免锁过早释放。
-
唯一客户端标识:防止其他客户端误删锁。
-
容错机制:Redis 建议使用 RedLock,ZooKeeper 需集群部署。