1、简述
分布式锁是分布式系统中用来控制资源访问的一种机制,主要解决多实例、多线程同时访问共享资源时的并发问题。本文将介绍常见的分布式锁实现方案,包括基于数据库、Redis、ZooKeeper 的实现方式,并提供实践样例。
2、分布式锁的基本要求
互斥性:同一时刻只能有一个客户端获取锁。
防死锁:即使客户端发生故障,锁也能自动释放。
可重入性:一个线程在持有锁时可以重复获取。
高性能:锁的获取和释放需要高效。
3、常见分布式锁实现方案
3.1 基于数据库实现分布式锁
利用数据库的唯一约束或 SELECT FOR UPDATE 特性实现分布式锁。
package com.example.springbootclient.lock;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class DatabaseDistributedLock {
private final DataSource dataSource;
public DatabaseDistributedLock(DataSource dataSource) {
this.dataSource = dataSource;
}
public boolean tryLock(String lockKey, long expireTime) {
String sql = "INSERT INTO distributed_locks (lock_key, expire_time) VALUES (?, ?) ON DUPLICATE KEY UPDATE expire_time=?";
try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, lockKey);
stmt.setLong(2, System.currentTimeMillis() + expireTime);
stmt.setLong(3, System.currentTimeMillis() + expireTime);
return stmt.executeUpdate() > 0;
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
public void releaseLock(String lockKey) {
String sql = "DELETE FROM distributed_locks WHERE lock_key = ?";
try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, lockKey);
stmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
优点
简单易用,适合小规模分布式应用。
缺点
性能较差,数据库压力大。
需要手动清理过期锁。
3.2 基于 Redis 实现分布式锁
首先,在 Spring Boot 项目中引入 Jedis 依赖。你可以在 pom.xml 文件中添加以下内容:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.4.3</version> <!-- 请根据实际需求选择最新版本 -->
</dependency>
利用 Redis 的 SET NX 命令实现互斥锁,并结合 EXPIRE 设置超时时间。
package com.example.springbootclient.lock;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;
public class RedisDistributedLock {
private final Jedis jedis;
public RedisDistributedLock(Jedis jedis) {
this.jedis = jedis;
}
public boolean tryLock(String lockKey, String requestId, long expireTime) {
SetParams setParams = new SetParams();
setParams.nx();
setParams.px(expireTime);
String result = jedis.set(lockKey, requestId, setParams);
return "OK".equals(result);
}
public boolean releaseLock(String lockKey, String requestId) {
String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) else return 0 end";
Object result = jedis.eval(luaScript, 1, lockKey, requestId);
return "1".equals(result.toString());
}
}
优点
高性能,适合高并发场景。
易于扩展和部署。
缺点
实现稍复杂,需要保证锁释放的原子性
3.3 基于 ZooKeeper 实现分布式锁
首先,在 Spring Boot 项目中引入 ZooKeeper 依赖。你可以在 pom.xml 文件中添加以下内容:
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.8.1</version> <!-- 请根据实际需求选择最新版本 -->
</dependency>
通过 ZooKeeper 的节点特性(临时有序节点)实现分布式锁。
package com.example.springbootclient.lock;
import org.apache.zookeeper.*;
import java.util.Collections;
import java.util.List;
public class ZookeeperDistributedLock {
private final ZooKeeper zk;
private final String lockBasePath;
private String currentLockPath;
public ZookeeperDistributedLock(ZooKeeper zk, String lockBasePath) {
this.zk = zk;
this.lockBasePath = lockBasePath;
}
public void tryLock() throws Exception {
currentLockPath = zk.create(lockBasePath + "/lock_", new byte[0],
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
while (true) {
List<String> children = zk.getChildren(lockBasePath, false);
Collections.sort(children);
if (currentLockPath.endsWith(children.get(0))) {
return; // 获取锁成功
}
// 等待前一个节点释放
int index = children.indexOf(currentLockPath.substring(lockBasePath.length() + 1));
String previousNode = children.get(index - 1);
zk.exists(lockBasePath + "/" + previousNode, watchedEvent -> {
if (watchedEvent.getType() == Watcher.Event.EventType.NodeDeleted) {
synchronized (this) {
this.notifyAll();
}
}
});
synchronized (this) {
this.wait();
}
}
}
public void releaseLock() throws Exception {
zk.delete(currentLockPath, -1);
}
}
优点
可靠性高,天然支持故障恢复。
无需手动清理过期锁。
缺点
部署复杂,需要维护 ZooKeeper 集群。
性能不如 Redis。
4、总结
分布式锁在分布式系统中是重要的组件,根据具体需求和场景选择合适的实现方案尤为关键:
如果系统简单且性能要求不高,可以选择基于数据库的分布式锁。
如果性能和扩展性是首要考虑,可以选择基于 Redis 的分布式锁。
如果系统需要强一致性和高可靠性,可以选择基于 ZooKeeper 的分布式锁。
通过实践这些分布式锁方案,可以有效解决分布式系统中的资源竞争问题,从而提升系统的可靠性和稳定性。
原创文章,作者:速盾高防cdn,如若转载,请注明出处:https://www.sudun.com/ask/205901.html