mysql - PHP队列执行任务的时候,如何防止进程之间抢夺资源?
PHP中文网
PHP中文网 2017-04-11 10:23:00
[PHP讨论组]

PHP队列执行任务的时候,如何防止进程之间抢夺资源?
比如视频转码队列,每分钟会起一个进程去处理视频转码任务,每次从数据库获取10条
进程1已经获取了ID为10的视频去处理,一分钟后,如何防止进程2 拿到其他进程已经处理过的?
处理完成后,有异步通知,但是异步通知会有延迟,所以视频状态有可能修改不及时

PHP中文网
PHP中文网

认证高级PHP讲师

全部回复(5)
怪我咯

实际上有一个非常简单的办法,你可以利用数据库操作的原子性来实现,不需要那么复杂的锁机制,甚至队列。就按你的方法来,假设任务数据表 task 里有两个字段 id, status,我们定义status三个状态

0: 待处理
1: 正在处理
2: 处理完成

假设你有一堆 PHP 进程都用如下 SQL 语句去取出数据库里的待处理任务

SELECT * FROM task WHERE status = 0

取出来以后,我们为了防止其他用户不再重复取出要把它的状态标记为 1

UPDATE task SET status = 1 WHERE id = xxx

但是等等,这样就会产生如你所说的资源抢夺,但如果加上一个简单的技巧就可以避免,你把语句变成这样

UPDATE task SET status = 1 WHERE id = xxx AND status = 0

熟悉一点数据库的人可能会说这样还是避免不了抢夺,只是避免了重复写入。
我要说的是,能避免重复写入就够了,我们的进程在执行完这条操作后,去获取 affected_rows ,即更新的条数,根据数据库的原子性,只有第一个抢占的进程才会返回 1,它可以进行后面的操作。而剩下返回 0 的进程,直接进入下一个等待流程即可。

ringa_lee

这个问题我解决过,必须要答一下。
首先说一下我们的应用场景:在队列内有多个订单,有多个进程消费此队列,为了防止重复,在订单处理时加了锁,但观察日志发现,锁冲突的记录太多了,于是考虑优化。
我的解决方案从进程入手,即考虑特定进程处理某一种订单,我选用的是 记录id对进程数取模,即 第个N进程只处理 id % p_num = N 的订单
要解决此问题就要考虑进程的标识问题,PHP进程不能常驻内存,要想使得每一个PHP进程都有特定标识N,有两种方案:

  • 每个PHP脚本定义私有变量N,则需要启动N个不一样的进程,代码比较恶心,常进程处理起来不方便;

  • 定义同一类型的进程,在进程初始化时,去获取一个唯一的序号N,这个序号N的获取我这里搞了四种方案(外三内一),写了两篇博客:可以看:

从并发处理谈PHP进程间通信(一)外部介质
从并发处理谈PHP进程间通信(二)System V IPC
我这里选用的redis方案,效果不错,题主可以选一而试。

PHP中文网

这个不应该是消息的消费么,把需要消费的数据丢到队列里面,然后多个进程进行消费。推荐:rabbitmq,redis。

当然如果说是一次性的就直接把数据丢到redis,分组,每次进程的带起消费一组数据,并且删除redis保存中的一组数据。最后全部消费完,加个特殊字符去标示全部处理完毕。

怪我咯

可以考虑一下把你取ID和处理的逻辑和进程分离。由单一进程负责取id,然后开子进程的方式去处理,这样你就不会取到重复的id了。

大家讲道理

每个队列执行之前。比对一下 数据的id 有重复的就释放掉

热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号