一、为什么需要分布式锁
在单机应用中,synchronized
或 ReentrantLock
足以解决并发问题。但在 分布式系统 中,多台服务器之间共享同一个资源时,如果没有锁,很可能出现 超卖、重复扣减、数据不一致 等问题。
因此,分布式锁应运而生,Redis 因其高性能与丰富指令,成为分布式锁的常用实现工具。
二、Redis 实现分布式锁的基本原理
Redis 分布式锁主要基于 SET key value NX EX time
命令实现,核心思路:
加锁:
使用
SETNX
保证 key 不存在时才能成功设置。使用
EX
设置过期时间,避免死锁。value 通常设置为随机值,用于标识锁的持有者。
解锁:
判断当前锁的 value 是否是自己设置的随机值。
只有持有者才能释放锁。
使用 Lua 脚本保证“查询 value + 删除 key”的原子性。
这样可以保证:
互斥性:同一时刻只有一个客户端获取锁。
避免死锁:即使客户端异常宕机,锁也会自动过期释放。
可重入/安全解锁:通过 value 标识持有者,防止误删别人的锁。
三、可能存在的问题
锁过期导致并发
如果业务逻辑执行时间 > 锁过期时间,可能导致锁自动释放,其他客户端拿到锁,出现数据不一致。
主从复制延迟问题
在 Redis 主从架构下,如果主节点写入成功但未同步到从节点,主节点宕机后从节点被提升为主,会导致锁丢失。
不可重入性
同一个线程多次加锁会失败,需要额外机制实现可重入。
四、改进策略
1. 合理设置过期时间
根据业务逻辑耗时设置合理的锁过期时间。
可结合 锁续期机制:在后台定时任务中为锁自动续期(如 Redisson 的 WatchDog)。
2. 使用 Lua 脚本保证原子性
解锁必须使用 Lua 脚本,保证“判断 value + 删除 key”操作的原子性。
3. Redlock 算法(Redis 官方提出)
通过 多个 Redis 实例 获取锁,必须在大多数节点上成功才算加锁成功。
提高了分布式锁的可靠性,避免因单点故障导致锁失效。
4. 引入 Redisson 框架
Redisson 封装了分布式锁,支持可重入锁、公平锁、读写锁、自动续期等功能。
在实际业务中更推荐使用,而不是自己手写。
五、典型应用场景
电商秒杀:防止超卖。
库存扣减:保证同一商品库存不会被多次扣减。
订单防重:防止重复提交订单。