基于Redis的分布式锁
基于Redis的分布式锁
setNx 加锁
在使用 Redis 实现分布式锁时,首要想到的方法是使用 setNx
命令。
if (jedis.setnx(lockKey, val) == 1) {
jedis.expire(lockKey, timeout); // 设置锁的超时时间
}
// 注释:尽管这段代码看似完成了加锁操作,但实际上,`setNx` 和 `expire` 是两个独立的命令,它们之间并非原子操作。
// 如果在加锁成功后,设置超时时间失败,那么 lockKey 就可能成为永不超时的锁,在高并发场景下,这将带来严重的问题。
7.2 set 命令加锁
为了解决上述问题,我们可以改用 Redis 的 set
命令,并指定多个参数来实现原子性的加锁操作。
String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);
if ("OK".equals(result)) {
return true; // 加锁成功
}
return false; // 加锁失败
// 参数解释:
// lockKey:锁的唯一标识
// requestId:请求标识,用于区分不同的加锁请求
// NX:只有在键不存在时才设置值
// PX:设置键的过期时间,单位为毫秒
// expireTime:锁的超时时间(毫秒)
7.3 释放锁
对于释放锁的操作,为何需要记录 requestId 呢?
if (jedis.get(lockKey).equals(requestId)) {
jedis.del(lockKey);
return true; // 释放锁成功
}
return false; // 释放锁失败
// 解释:releaseId 在释放锁时起到关键作用,确保只能释放当前请求所持有的锁,防止误删其它请求的锁。
如果使用 userId 替换 requestId 是否可行呢?
答案:不可行。若在请求流程结束时,恰好锁因过期而失效,而另一个请求恰巧使用相同的 userId 加锁成功,这时,第一个请求在释放锁时就会错误地删除掉第二个请求加的锁。为避免此类问题,应使用 requestId。
当然,通过 Lua 脚本可以确保释放锁时的原子性:
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end
// 此脚本确保了查询锁是否存在与删除锁这两个操作的原子性。
** 自旋锁**
上述加锁方法在面对大量并发请求时可能存在不足,例如在秒杀场景下,如果有大量请求同时竞争同一把锁,大部分请求都将失败。
try {
Long start = System.currentTimeMillis();
while (true) {
String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);
if ("OK".equals(result)) {
return true; // 加锁成功,跳出循环
}
long time = System.currentTimeMillis() - start;
if (time >= timeout) {
return false; // 超过设定的等待时间,仍未加锁成功,返回失败
}
try {
Thread.sleep(50); // 微妙级别的休眠,降低CPU占用率
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
unlock(lockKey, requestId); // 无论加锁成功与否,都需要在finally块中尝试释放锁
}
return false;
// 解释:引入自旋锁机制,在规定的等待时间内(如500毫秒),持续尝试加锁,直至成功或超时。这种方式可以在短时间内增加获取锁的成功概率,尤其适用于秒杀场景,防止库存瞬间被均匀分配,不符合预期的抢购效果。
> 原文链接 [https://www.hanyuanhun.cn](https://www.hanyuanhun.cn) | [https://node.hanyuanhun.cn](https://node.hanyuanhun.cn)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!