您好,登录后才能下订单哦!
密码登录
登录注册
点击 登录注册 即表示同意《亿速云用户服务条款》
# Java分布式锁的原理以及实现方法
## 目录
1. [分布式锁概述](#分布式锁概述)
2. [分布式锁的核心特性](#分布式锁的核心特性)
3. [常见实现方案对比](#常见实现方案对比)
4. [基于数据库的实现](#基于数据库的实现)
5. [基于Redis的实现](#基于redis的实现)
6. [基于Zookeeper的实现](#基于zookeeper的实现)
7. [RedLock算法详解](#redlock算法详解)
8. [框架集成实践](#框架集成实践)
9. [典型应用场景](#典型应用场景)
10. [性能优化建议](#性能优化建议)
11. [常见问题解决方案](#常见问题解决方案)
---
## 分布式锁概述
在分布式系统中,当多个进程/服务需要互斥地访问共享资源时,分布式锁成为关键组件。与单机锁不同,分布式锁需要解决网络延迟、节点故障等分布式环境特有的问题。
**核心价值**:
- 保证跨JVM的互斥访问
- 避免重复处理(如定时任务)
- 防止超卖等业务场景
## 分布式锁的核心特性
| 特性 | 说明 |
|---------------|----------------------------------------------------------------------|
| 互斥性 | 同一时刻只有一个客户端持有锁 |
| 可重入性 | 同一客户端可多次获取同一把锁 |
| 锁超时 | 防止死锁,自动释放机制 |
| 高可用 | 集群环境下仍能正常工作 |
| 非阻塞 | 获取锁失败立即返回而非阻塞 |
## 常见实现方案对比
```mermaid
pie
title 分布式锁实现方案占比
"Redis" : 45
"Zookeeper" : 35
"数据库" : 15
"其他" : 5
方案对比表:
方案 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
数据库 | 实现简单 | 性能差,无自动过期 | 低并发简单场景 |
Redis | 性能高,功能丰富 | 实现复杂,需处理脑裂问题 | 高并发场景 |
Zookeeper | 可靠性高,原生支持 | 性能中等,依赖ZK集群 | 强一致性要求场景 |
-- 建表语句
CREATE TABLE `distributed_lock` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`lock_key` varchar(64) NOT NULL,
`lock_value` varchar(255) NOT NULL,
`expire_time` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_lock_key` (`lock_key`)
);
// 获取锁
public boolean tryLock(String key, String value, long expireTime) {
Connection conn = null;
try {
conn = dataSource.getConnection();
conn.setAutoCommit(false);
// 先尝试插入
PreparedStatement ps = conn.prepareStatement(
"INSERT INTO distributed_lock(lock_key, lock_value, expire_time) VALUES (?,?,?)");
ps.setString(1, key);
ps.setString(2, value);
ps.setTimestamp(3, new Timestamp(System.currentTimeMillis() + expireTime));
try {
return ps.executeUpdate() == 1;
} catch (SQLException e) {
// 唯一键冲突说明锁已存在
if (e.getErrorCode() == 1062) {
// 检查锁是否过期
PreparedStatement queryPs = conn.prepareStatement(
"SELECT * FROM distributed_lock WHERE lock_key=? FOR UPDATE");
queryPs.setString(1, key);
ResultSet rs = queryPs.executeQuery();
if (rs.next()) {
Timestamp expire = rs.getTimestamp("expire_time");
if (expire.before(new Date())) {
// 锁已过期,可以获取
PreparedStatement updatePs = conn.prepareStatement(
"UPDATE distributed_lock SET lock_value=?, expire_time=? WHERE lock_key=?");
updatePs.setString(1, value);
updatePs.setTimestamp(2, new Timestamp(System.currentTimeMillis() + expireTime));
updatePs.setString(3, key);
return updatePs.executeUpdate() == 1;
}
}
}
return false;
}
} finally {
if (conn != null) conn.close();
}
}
public boolean tryLockWithVersion(String key, String value, long expireTime) {
int version = getCurrentVersion(key);
int updated = jdbcTemplate.update(
"UPDATE distributed_lock SET lock_value=?, expire_time=?, version=version+1 " +
"WHERE lock_key=? AND version=?",
value,
new Timestamp(System.currentTimeMillis() + expireTime),
key,
version
);
return updated == 1;
}
public boolean tryLock(Jedis jedis, String key, String value, int expireTime) {
// SET if Not eXists
String result = jedis.set(key, value, "NX", "PX", expireTime);
return "OK".equals(result);
}
// 解锁Lua脚本
private static final String UNLOCK_SCRIPT =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";
public boolean unlock(Jedis jedis, String key, String value) {
Object result = jedis.eval(UNLOCK_SCRIPT,
Collections.singletonList(key),
Collections.singletonList(value));
return result.equals(1L);
}
// 获取锁
RLock lock = redisson.getLock("orderLock");
try {
// 尝试加锁,最多等待100秒,上锁后30秒自动解锁
boolean res = lock.tryLock(100, 30, TimeUnit.SECONDS);
if (res) {
// 业务逻辑
}
} finally {
lock.unlock();
}
public class ZkDistributedLock implements Watcher {
private ZooKeeper zk;
private String lockPath;
private String currentPath;
private String waitPath;
public boolean tryLock() throws Exception {
// 创建临时顺序节点
currentPath = zk.create(lockPath + "/lock-",
null,
ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
// 检查是否是最小序号节点
List<String> children = zk.getChildren(lockPath, false);
Collections.sort(children);
if (currentPath.equals(lockPath + "/" + children.get(0))) {
return true;
} else {
// 监听前一个节点
int currentIndex = Collections.binarySearch(children,
currentPath.substring(currentPath.lastIndexOf('/') + 1));
waitPath = lockPath + "/" + children.get(currentIndex - 1);
zk.getData(waitPath, this, null);
return false;
}
}
@Override
public void process(WatchedEvent event) {
if (event.getType() == Event.EventType.NodeDeleted) {
synchronized (this) {
this.notifyAll(); // 唤醒等待线程
}
}
}
}
Redis官方推荐的分布式锁算法,解决单点Redis故障问题。
算法步骤: 1. 获取当前毫秒级时间戳 2. 依次尝试从N个独立Redis实例获取锁 3. 计算获取锁总耗时,当且仅当在多数节点上获取成功且总耗时小于锁有效期时才算成功 4. 如果获取失败,需在所有节点上释放锁
public boolean tryRedLock(List<Jedis> jedisList, String key, String value,
int expireTime, int retryCount, long retryDelay) {
int successCount = 0;
long startTime = System.currentTimeMillis();
for (int i = 0; i < retryCount; i++) {
for (Jedis jedis : jedisList) {
if (tryLock(jedis, key, value, expireTime)) {
successCount++;
}
}
// 检查是否获得多数锁
if (successCount >= jedisList.size() / 2 + 1) {
// 检查获取时间是否有效
long elapsed = System.currentTimeMillis() - startTime;
if (elapsed < expireTime) {
return true;
}
break;
}
// 释放已获取的锁
for (Jedis jedis : jedisList) {
unlock(jedis, key, value);
}
// 重试延迟
try {
Thread.sleep(retryDelay);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
return false;
}
@Configuration
public class LockConfig {
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer()
.setAddress("redis://127.0.0.1:6379")
.setDatabase(0);
return Redisson.create(config);
}
@Bean
public DistributedLockTemplate distributedLockTemplate(RedissonClient redisson) {
return new DistributedLockTemplate(redisson);
}
}
@Component
public class OrderService {
@Autowired
private DistributedLockTemplate lockTemplate;
public void createOrder(String orderId) {
lockTemplate.execute("order:" + orderId, 3000, () -> {
// 业务逻辑
return null;
});
}
}
public boolean seckill(Long productId, Long userId) {
String lockKey = "seckill:" + productId;
RLock lock = redisson.getLock(lockKey);
try {
// 尝试获取锁,等待时间100ms,持有锁1秒
if (lock.tryLock(100, 1000, TimeUnit.MILLISECONDS)) {
// 1. 查询库存
int stock = getStockFromDB(productId);
if (stock <= 0) {
return false;
}
// 2. 扣减库存
if (reduceStock(productId) > 0) {
// 3. 创建订单
createOrder(productId, userId);
return true;
}
}
return false;
} finally {
lock.unlock();
}
}
锁粒度控制:
锁等待时间:
// 建议设置合理的等待时间
lock.tryLock(50, 300, TimeUnit.MILLISECONDS);
锁分段技术:
// 将商品库存分为10段
int segment = productId.hashCode() % 10;
RLock lock = redisson.getLock("stock:" + segment);
private ThreadLocal<Long> lockHoldTime = new ThreadLocal<>();
public boolean tryLock(String key, long waitTime, long leaseTime) {
long start = System.currentTimeMillis();
while (System.currentTimeMillis() - start < waitTime) {
if (redis.setnx(key, "1")) {
// 启动守护线程续期
scheduleExpirationRenewal(key, leaseTime);
lockHoldTime.set(System.currentTimeMillis());
return true;
}
Thread.sleep(50);
}
return false;
}
private void scheduleExpirationRenewal(String key, long leaseTime) {
new Thread(() -> {
while (true) {
try {
// 每leaseTime/3时间续期一次
Thread.sleep(leaseTime / 3);
if (lockHoldTime.get() == null) break;
redis.expire(key, leaseTime);
} catch (Exception e) {
break;
}
}
}).start();
}
总结:分布式锁的选择需要根据业务场景在CP和AP之间做出权衡,建议: - 强一致性场景:Zookeeper - 高并发场景:Redis+Redisson - 简单场景:数据库乐观锁 “`
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。