算法 sidekiq jobs 里的竞态问题

hz_qiuyuanxin · 2014年07月04日 · 最后由 ruohanc 回复于 2014年07月04日 · 8024 次阅读

问题

对于单个用户来说,对于其某个资源(例如一个用户的资金、积分等)的更新必须是串行且具备事务。

例如:当有几个 job 陆续进来,

change job 1: inc 5 change job 2: dec 5 change job 3: inc 5 ...

每当一个 job 开始运行前,就必须检查是否有同样的 job【正在或者准备】对同个用户的某个资源进行操作, 如果有,就必须等待其完成后才能进行。

关于为什么使用 background job 来处理?

  • 业务逻辑较多,需要经过一系列计算才能更新
  • 用户量较大,要求能支撑起 5W 日活跃用户
  • 在某些时间段里,用户活跃比较集中

unique-jobs ?

虽然 sidekiq 有一些 extensions 如 sidekiq-unique-jobs 能够控制 job 的 unique,但是它会将后来进来的 job 直接给抛弃掉, 而不是这种必须保证所有 job 的串行执行。

数据库锁 ?

如果说使用 db row locking,也同样有问题,因为任务必须是 串行执行


请问有没有谁有这方面的经验?

有三个方案 方案一:由 job1 来启动 job2,job2 来启动 job3 方案二:job1 完成的时候在数据库里留下记录,job2 启动前看看 job1 完成了没,没完成的话就再等等 方案三:成为付费高级用户

#1 楼 @dongqs 感谢你的回答

方案一不行,原因是因为 job1 无法控制调度,还有 job1 是不知道后面还又没相同的 job 的,并且 job1 也有结束的时候,job2 和 job3 是后面某个时间点由用户的行为所触发的,而不是一开始就知道的。

方案二即是自己模拟一个锁,这个地球人都知道,就是做起来很难。

方案三跟本问题无关。

用 limit_fetch 设置某个 queue 同时只能由一个 worker 处理

https://github.com/brainopia/sidekiq-limit_fetch

用 limit_fetch 设置某个 queue 同时只能由一个 worker 处理

https://github.com/brainopia/sidekiq-limit_fetch

#4 楼 @quakewang

如果使用 limit_fetch 来控制,这个 queue 里面每次只能被执行一个,那么就变成整个事情都是串行的。 这里需要的是更细粒度的串行控制。是 uid + jobid 而不是 jobid

#5 楼 @hz_qiuyuanxin 用 uid 作为 queue name Sidekiq::Queue['queue_name_' + uid].limit = 1

你每次运行一个 job 的时候,sidekiq 会返回那个 job 的 jid,在一个地方保存那个 job 的 jid,job 完了,就把 jid 清除,不行吗?可以是在用户的表里面保存,也可以用 redis

帮了大忙...我正好也面临非常类似的情况

#6 楼 @quakewang 觉得你这个方案特别靠谱...回头实践下再来汇报...

需要 登录 后方可回复, 如果你还没有账号请 注册新账号