PHP队列执行任务的时候,如何防止进程之间抢夺资源?
比如视频转码队列,每分钟会起一个进程去处理视频转码任务,每次从数据库获取10条
进程1已经获取了ID为10的视频去处理,一分钟后,如何防止进程2 拿到其他进程已经处理过的?
处理完成后,有异步通知,但是异步通知会有延迟,所以视频状态有可能修改不及时
PHP队列执行任务的时候,如何防止进程之间抢夺资源?
比如视频转码队列,每分钟会起一个进程去处理视频转码任务,每次从数据库获取10条
进程1已经获取了ID为10的视频去处理,一分钟后,如何防止进程2 拿到其他进程已经处理过的?
处理完成后,有异步通知,但是异步通知会有延迟,所以视频状态有可能修改不及时
这个问题我解决过,必须要答一下。
首先说一下我们的应用场景:在队列内有多个订单,有多个进程消费此队列,为了防止重复,在订单处理时加了锁,但观察日志发现,锁冲突的记录太多了,于是考虑优化。
我的解决方案从进程入手,即考虑特定进程处理某一种订单,我选用的是 记录id对进程数取模
,即 第个N进程只处理 id % p_num = N 的订单
。
要解决此问题就要考虑进程的标识问题,PHP进程不能常驻内存,要想使得每一个PHP进程都有特定标识N,有两种方案:
每个PHP脚本定义私有变量N,则需要启动N个不一样的进程,代码比较恶心,常进程处理起来不方便;
定义同一类型的进程,在进程初始化时,去获取一个唯一的序号N,这个序号N的获取我这里搞了四种方案(外三内一),写了两篇博客:可以看:
从并发处理谈PHP进程间通信(一)外部介质
从并发处理谈PHP进程间通信(二)System V IPC
我这里选用的redis方案,效果不错,题主可以选一而试。
这个不应该是消息的消费么,把需要消费的数据丢到队列里面,然后多个进程进行消费。推荐:rabbitmq,redis。
当然如果说是一次性的就直接把数据丢到redis,分组,每次进程的带起消费一组数据,并且删除redis保存中的一组数据。最后全部消费完,加个特殊字符去标示全部处理完毕。
5 回答3.2k 阅读✓ 已解决
3 回答3.6k 阅读✓ 已解决
1 回答4k 阅读✓ 已解决
3 回答1.8k 阅读✓ 已解决
2 回答2.2k 阅读✓ 已解决
2 回答2.8k 阅读✓ 已解决
2 回答3.1k 阅读
实际上有一个非常简单的办法,你可以利用数据库操作的原子性来实现,不需要那么复杂的锁机制,甚至队列。就按你的方法来,假设任务数据表
task
里有两个字段id
,status
,我们定义status
三个状态假设你有一堆 PHP 进程都用如下 SQL 语句去取出数据库里的待处理任务
取出来以后,我们为了防止其他用户不再重复取出要把它的状态标记为
1
但是等等,这样就会产生如你所说的资源抢夺,但如果加上一个简单的技巧就可以避免,你把语句变成这样
熟悉一点数据库的人可能会说这样还是避免不了抢夺,只是避免了重复写入。
我要说的是,能避免重复写入就够了,我们的进程在执行完这条操作后,去获取
affected_rows
,即更新的条数,根据数据库的原子性,只有第一个抢占的进程才会返回1
,它可以进行后面的操作。而剩下返回0
的进程,直接进入下一个等待流程即可。