Redis如何实现分布式锁?

本文深入探讨了如何利用Redis实现分布式锁,通过set命令的NX和EX选项确保锁的独占性和时效性,同时介绍了使用EVAL命令进行解锁的正确方式。

@Redis如何实现分布式锁?TOC

使用redis命令 set key value NX EX max-lock-time 实现加锁(set五个参数:key,value,NX,EX ,最大超时时间.NX:只有key不存在的时候才会进行操作.EX :设置key的过期时间)
使用redis命令 EVAL 实现解锁

在分布式系统中,多个服务实例可能同时访问共享资源(如数据库、缓存、文件等),为了避免并发冲突,需要使用**分布式锁**来协调这些实例。Redis 是一个常用的实现分布式锁的中间件,因为它具备高性能、原子操作和过期机制。 下面是一个使用 Redis 实现分布式锁的经典方式 —— **基于 SETNX + Lua 脚本 的实现**: --- ### ✅ 使用 Redis 实现分布式锁的 Java 示例 ```java import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.stereotype.Service; import java.util.Collections; import java.util.UUID; import java.util.concurrent.TimeUnit; @Service public class RedisDistributedLock { private final StringRedisTemplate redisTemplate; // Lua脚本,用于原子性地判断并设置锁 private static final String LUA_SCRIPT = """ if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then return redis.call('pexpire', KEYS[1], ARGV[2]) else return 0 end """; private final DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(LUA_SCRIPT, Long.class); public RedisDistributedLock(StringRedisTemplate redisTemplate) { this.redisTemplate = redisTemplate; } /** * 尝试获取分布式锁 * * @param lockKey 锁的 key 名称 * @param expireTime 锁的过期时间(毫秒) * @return 成功获取到锁返回 true,否则 false */ public boolean tryLock(String lockKey, long expireTime) { String identifier = UUID.randomUUID().toString(); // 唯一标识符,防止误删锁 Long result = redisTemplate.execute( redisScript, Collections.singletonList(lockKey), identifier, String.valueOf(expireTime) ); return result != null && result == 1; } /** * 释放分布式锁 * * @param lockKey 锁的 key 名称 * @param identifier 锁的唯一标识符 */ public void releaseLock(String lockKey, String identifier) { String script = """ if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end """; DefaultRedisScript<Long> releaseScript = new DefaultRedisScript<>(script, Long.class); Long result = redisTemplate.execute(releaseScript, Collections.singletonList(lockKey), identifier); } } ``` --- ### 🔍 代码解释 #### 🧩 `tryLock` 方法: - 使用 Lua 脚本执行 `SETNX`(Set if Not eXists)操作; - 如果 key 不存在,则设置值为随机 UUID,并设置过期时间(防止死锁); - 返回值为 1 表示加锁成功,0 表示失败。 #### 🧩 `releaseLock` 方法: - 先检查当前锁是否是自己持有的(通过 UUID 比较); - 防止出现“锁被别人释放”的问题; - 只有匹配的 UUID 才能删除对应的 key。 --- ### ⏱️ 设置过期时间的重要性 - 防止因为某个节点崩溃导致锁永远不被释放; - 推荐使用 `PX`(毫秒)精度的过期时间; - 设置过期时间必须与 `SETNX` 原子执行(所以用 Lua 脚本); --- ### 🛡️ 注意事项 1. **锁续期**:如果业务逻辑执行时间可能超过锁的过期时间,需要考虑使用看门狗(Watchdog)机制自动续期。 2. **可重入性**:上述实现不是可重入的,若需支持同一个线程多次获取锁,需额外计数器。 3. **Redlock 算法**:适用于更复杂的多 Redis 实例环境,但一般单实例 + Lua 已满足大多数场景。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值