问题描述在最近的项目线上环境中,队列服务器上频繁出现数据库死锁问题。这个问题可以追溯到几年前,19年就出现了,那时候经常开发业务功能,所以没有处理过这个问题。这次只是来探究死锁的原因和问题。首先,目前项目中使用的队列驱动是数据库。因为简单、高效、不需要扩展其他第三方应用,所以一直使用mysql数据库作为队列驱动。线上队列环境运行:Ubuntu16.04+Mysql5。7+Laravel5.6,这样的配置目前使用supervisor在上面托管16个队列进程。上图是17,因为有匹配符,所以需要-1,也就是16。查看死锁日志,异常监控这个死锁问题。将近440,000个事件被触发,几乎每分钟都有机会触发死锁。看了队列的源码,发现是X锁导致的,然后尝试模拟更多进程队列消耗是否会导致死锁。多进程消费队列通过artisan命令生成一个测试作业,然后我们暂停每个队列500毫秒,以模拟处理过程。onQueue('test');}配置supervisor托管文件我们使用supervisor来这里托管我们的8个处理进程,配置如下:[program:laravel-worker-queue-test]process_name=%(program_name)s_%(process_num)02dcommand=php/data/sites/test/artisanqueue:work--queue=testautostart=trueautorestart=truenumprocs=8user=rootredirect_stderr=truestdout_logfile=/data/sites/test/storage/logs/worker.log然后启动8个进程,进行测试,发现在消费1400+任务时,发生了456次死锁。下面我们来分析一下死锁的过程,并尝试解决一些解决方案。求职机制(GetJob)我们运行了8个进程,相当于8个工作人员。他们都将执行“求职行动”以获得下一份工作,Laravel源码中的实现是这样的:publicfunctionpop($queue=null){$queue=$this->getQueue($queue);返回$this->database->transaction(function()use($queue){if($job=$this->getNextAvailableJob($queue)){return$this->marshalJob($queue,$job);}returnnull;});}转换成SQL语句如下:BEGINTRANSACTION;SELECT*FROM`jobs`WHERE`queue`=?AND((`reserved_at`ISNULLand`available_at`<=NOW())OR(`reserved_at`<=?))ORDERBY`id`ASClimit1FORUPDATE;UPDATE`jobs`SET`reserved_at`=NOW(),`attempts`=`attempts`+1WHERE`id`=?;COMMIT;第一次select查询主要是获取下一个可用的job,如果available_at
