Sidekiq/Ruby On Rails使用Redis Redlock锁偶发无法正确锁定问题
问题原因分析
- 日志埋点时序错误:你的代码先执行
puts("locking! #{task_id}")打印加锁日志,再调用lock_manager.lock申请锁。即使锁申请失败也会先打印加锁日志,仅从你提供的日志无法直接判定两个任务都成功拿到了锁,这是最容易引发误判的点。 - Redlock客户端初始化逻辑错误:你每次执行
perform方法都会重新初始化Redlock::Client实例,Redlock算法依赖客户端与Redis节点的时钟偏移校准值,每次新建实例都会重新计算偏移,在两个请求间隔仅1ms的极端场景下,不同实例的偏移计算误差可能导致锁的有效性校验逻辑判断异常,出现两个请求都判定自己持有锁的问题。正确做法是将Redlock客户端全局初始化一次,作为单例复用即可。 - 单节点场景下Redlock特性不适配:Redlock本身是为多Redis节点的分布式场景设计的,单节点使用时锁安全性和普通
SETNX加锁没有区别,同时Redlock默认开启3次锁申请重试,重试间隔带随机抖动,极端场景下可能出现前一个任务刚释放锁,后一个重试请求刚好拿到锁的情况,让你误以为两个任务并发持有锁。 - 锁TTL配置的潜在风险:你设置的锁TTL是6分钟,如果任务实际执行时间超过6分钟,锁会自动过期释放,后续请求也能拿到锁,出现并发执行的问题,建议根据任务实际最大执行时长合理设置TTL,或者添加锁续期逻辑。
- 潜在的变量引用错误:你的方法入参是
task_id和task_type,但执行任务时调用的是Services::ExecTasks::Run.call task.id,如果此处task是未定义变量,会抛出异常直接触发ensure分支的解锁逻辑,如果你没有捕获异常日志,会误以为任务是正常执行完成的。
内容的提问来源于stack exchange,提问作者Amit
相关产品推荐
相关产品推荐

