如何避免Sidekiq多线程下同一ID的process_order任务并发执行?
解决Sidekiq同一ID任务并发执行的最佳方案
首选方案:使用Sidekiq唯一作业锁(推荐)
借助专门的Sidekiq扩展实现Redis分布式锁,确保同一order_id的任务不会并发执行。最常用的是开源的sidekiq-unique-jobs gem,以下是具体实现步骤:
步骤1:安装并配置gem
在Gemfile中添加依赖:
gem 'sidekiq-unique-jobs'
执行bundle install后,创建初始化文件(config/initializers/sidekiq_unique_jobs.rb)开启功能:
SidekiqUniqueJobs.configure do |config| config.enabled = true end
步骤2:改造Worker添加锁规则
将你的process_order方法封装为Sidekiq Worker,并针对order_id设置唯一锁:
class ProcessOrderWorker include Sidekiq::Worker # 配置锁逻辑:直到任务执行完成才释放锁,以第一个参数(order_id)作为唯一标识 sidekiq_options lock: :until_executed, unique_args: ->(args) { args[0] } def perform(order_id) # 原process_order的业务逻辑 # ... do something end end
参数说明:
lock: :until_executed:锁会保持到任务执行完毕(无论成功或失败),同一order_id的后续任务会进入队列等待,不会并发执行。unique_args:指定用任务的第一个参数(即order_id)作为锁的唯一键,确保只有相同ID的任务会被锁定。
备选方案:手动实现Redis分布式锁
如果不想引入额外gem,可以直接基于Redis实现简单锁,但需注意异常处理和锁过期逻辑:
class ProcessOrderWorker include Sidekiq::Worker def perform(order_id) lock_key = "process_order_lock:#{order_id}" # 尝试获取锁,设置1小时过期(根据任务实际执行时长调整),防止死锁 acquired_lock = Redis.current.set(lock_key, "locked", nx: true, ex: 3600) # 未获取到锁则直接返回,任务会留在队列等待下次重试 return unless acquired_lock begin # 原process_order的业务逻辑 # ... do something ensure # 无论任务成功失败,都释放锁 Redis.current.del(lock_key) end end end
注意事项:
- 禁止使用进程内的线程锁(如
Mutex),Sidekiq是多进程架构,线程锁无法跨进程生效。 - 手动锁的过期时间必须大于任务的最长可能执行时间,否则会出现锁提前释放导致并发的问题。
内容的提问来源于stack exchange,提问作者netwire
相关产品推荐
相关产品推荐

