基于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)
posted @   汉源魂  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示