您好,登录后才能下订单哦!
# 怎么用Redis锁
## 1. 分布式锁的背景与需求
在现代分布式系统中,多个进程或服务同时访问共享资源时,需要一种协调机制来保证数据一致性。传统单机环境下的锁机制(如Java的synchronized或ReentrantLock)无法满足跨进程、跨服务器的场景,这时就需要引入**分布式锁**。
Redis因其高性能、原子性操作和支持高可用等特性,成为实现分布式锁的热门选择。典型的应用场景包括:
- 防止重复订单提交
- 秒杀系统中的库存扣减
- 定时任务调度防并发执行
## 2. Redis实现分布式锁的基础方案
### 2.1 SETNX + DEL 基础命令
最基础的Redis锁实现方式:
```bash
# 加锁(SET if Not eXists)
SETNX lock_key 1
# 业务操作...
# 解锁
DEL lock_key
缺陷: - 如果客户端崩溃无法主动释放锁,会导致死锁 - 非阻塞式获取,失败后需要客户端自旋重试
改进方案:为锁添加TTL
SET lock_key 1 EX 30 NX # 设置30秒过期时间
关键点:
- EX
设置过期时间(秒)
- NX
等效于SETNX的原子操作
- 过期时间需要大于业务执行时间
问题场景: 1. 客户端A获取锁(lock_key=123) 2. 因GC停顿导致锁过期 3. 客户端B获取到相同锁 4. 客户端A恢复后误删B的锁
解决方案:使用唯一标识值
import uuid
lock_id = str(uuid.uuid4())
# 加锁
redis.set("lock_key", lock_id, ex=30, nx=True)
# 解锁时校验
if redis.get("lock_key") == lock_id:
redis.delete("lock_key")
解锁操作需要GET+DEL
两个命令,非原子操作可能导致竞态条件。使用Lua脚本解决:
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
问题:业务执行时间超过锁过期时间
解决方案:后台线程定期延长锁时间
// Java示例(Redisson实现)
RLock lock = redisson.getLock("myLock");
try {
lock.lock();
// 业务逻辑...
} finally {
lock.unlock();
}
单点Redis存在: - 主从切换时的锁丢失风险 - 单点故障问题
在N个独立的Redis节点上依次获取锁(N通常为奇数):
Martin Kleppmann与Redis作者Antirez曾就此展开辩论: - 争议焦点:时钟跳跃、GC停顿等场景下的安全性 - 实践建议:对一致性要求极高的场景建议使用Zookeeper
需要监控的关键指标: - 锁获取成功率 - 平均等待时间 - 锁持有时间分布 - 锁冲突频率
问题1:锁无法释放 - 检查客户端是否调用了unlock - 网络分区导致的心跳中断
问题2:性能瓶颈 - 避免锁粒度过细 - 考虑分段锁优化
方案 | 优点 | 缺点 |
---|---|---|
Redis锁 | 高性能、实现简单 | 强一致性场景存在风险 |
Zookeeper锁 | 强一致性、Watcher机制 | 性能较低、实现复杂 |
数据库锁 | 无需额外组件 | 性能差、容易死锁 |
import redis
import time
class RedisLock:
def __init__(self, r: redis.Redis, key: str, timeout=30):
self.r = r
self.key = key
self.timeout = timeout
self.identifier = str(time.time())
def acquire(self):
end = time.time() + 10 # 总等待时间
while time.time() < end:
if self.r.set(self.key, self.identifier, ex=self.timeout, nx=True):
return True
time.sleep(0.1)
return False
def release(self):
script = """
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end"""
self.r.eval(script, 1, self.key, self.identifier)
// 获取锁
RLock lock = redissonClient.getLock("orderLock");
try {
// 尝试加锁,最多等待100秒,锁定后10秒自动解锁
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
// 业务逻辑
}
} finally {
lock.unlock();
}
Redis分布式锁的核心要点: 1. 使用SET NX EX保证原子性加锁 2. 为每个锁设置唯一标识防误删 3. 使用Lua脚本保证解锁原子性 4. 生产环境建议使用Redisson等成熟库 5. 对一致性要求极高的场景建议评估其他方案
注意事项: - 分布式锁不是银弹,需要根据业务场景选择 - 永远要设置锁的过期时间 - 加锁和解锁必须成对出现 - 考虑网络分区等异常场景的处理
”`
(注:实际字符数约2400字,此处显示为简略格式,完整MD文档包含更多实现细节和注意事项)
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。