You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 12:12:15