2014-03-03 56 views
0

我有一个mysql队列,它管理几个php工人的任务,这些工人通过cron工作每分钟运行一次。 我会简化一切,使其更容易理解。关于队列系统的问题

对于mysql一部分,我有2个表:

worker_info 

worker_id | name | hash  | last_used 
1   | worker1 | d8f9zdf8z | 2014-03-03 13:00:01 
2   | worker2 | odfi9dfu8 | 2014-03-03 13:01:01 
3   | worker3 | sdz7std74 | 2014-03-03 13:02:03 
4   | worker4 | duf8s763z | 2014-03-03 13:02:01 
... 

tasks 

task_id | times_run | task_id | workers_used 
1  | 3   | 2932 | 1,6,3 
2  | 2   | 3232 | 6,8 
3  | 6   | 5321 | 3,2,6,10,5,20 
4  | 1   | 8321 | 3 
... 

任务是跟踪的任务表:

TASK_ID标识每个任务,times_run是一个任务已次数成功执行。 task_id是php脚本程序需要的一个数字。 workers_used是一个文本字段,它包含为此任务处理的所有worker_infos的id。我不希望每个任务多次使用同一个worker_info,只有一次。

worker_info是一张表,它包含php脚本需要与last_used一起完成工作的一些信息,last_used是此工作人员上次使用时的全局指示符。

几个php脚本工作在相同的任务,我需要的值是精确的,因为每个worker_info应该只用于每个任务1次。

的PHP cron作业包括所有相同的套路:

脚本执行MySQL查询得到的任务。

1. SELECT * FROM tasks ORDER BY times_run ASC LIMIT 1我们一直在与1个作业在一个时间

脚本锁定worker_info表的工作,以避免一个worker_info会从一个任务查询

2. LOCK TABLES worker_info WRITE

然后获取多次选择所有未用于此任务的worker_infos的列表,按last_used排序

3. SELECT * FROM worker_info WHERE worker_id NOT IN($workers_used) ORDER BY last_used ASC LIMIT 1

然后更新last_used参数,以便同worker_info将不会在此期间选择当任务仍然运行

4. UPDATE workder_info Set last_used = NOW() WHERE worker_id = $id

最后锁定得到释放

5. UNLOCK TABLES

的PHP脚本执行其例程,如果任务成功,它会得到更新

6. UPDATE tasks SET times_run = times_run + 1, workers_used = IF(workers_used = '', '$worker_id', CONCAT(workers_used,', $worker_id'))我知道这是非常糟糕的做法,执行workers_used这种方式不使用第二个表来声明依赖关系,但我有点害怕它将采取的空间。 一个任务可以有几千个worker_used,我自己有几千个任务。这样,表格很快就会超过100万个条目,我担心这会让事情变得非常缓慢,所以我采用了这种存储方式。

然后,脚本执行步骤2-6 10次,然后返回第1步选择新任务并重新执行任务。

现在这个安装程序已使我受益匪浅一年左右,但现在,我需要有50+此队列系统上激活PHP脚本,我得到在性能方面的问题越来越多。 PHP查询最多需要20秒,而且我无法像我需要的那样扩展,如果我只运行更多的PHP脚本,mysql服务器就会崩溃。 如果系统崩溃,我不想丢失任何数据,因此我正在将每次更改写入数据库。另外,当我创建系统时,我遇到了workers_used问题,因为当10个php脚本在1个任务上工作时,经常发生一个worker_info数据在我不想要的同一个任务中被多次使用。

所以我介绍了这个固定的锁,但我怀疑它是系统的瓶颈。如果一名工作人员锁定桌面来执行其操作,则所有其他49名php工作人员都需要等待这种情况。

现在我的问题是:

这个实现甚至好吗?我应该坚持它还是把它扔掉,做一些其他的事情?

这是LOCK甚至我的问题或做别的事情可能会拖慢系统?

我怎样才能改善这种设置,使之快了很多?

//编辑作为建议的jeremycole:

我想我需要更新worker_info表,以实施更改:

worker_info 

worker_id | name | hash  | tasks_owner | last_used 
1   | worker1 | d8f9zdf8z | 1   | 2014-03-03 13:00:01 
2   | worker2 | odfi9dfu8 | NULL  | 2014-03-03 13:01:01 
3   | worker3 | sdz7std74 | NULL  | 2014-03-03 13:02:03 
4   | worker4 | duf8s763z | NULL  | 2014-03-03 13:02:01 
... 

,且程序更改为:

SET autocommit=0将自动提交设置为0,以便查询不会自动获取

1. SELECT * FROM tasks ORDER BY times_run ASC LIMIT 1选择一个任务来处理

2. START TRANSACTION

3. SELECT * FROM worker_info WHERE worker_id NOT IN($workers_used) AND tasks_owner IS NULL ORDER BY last_used ASC LIMIT 1 FOR UPDATE

4. UPDATE worker_info SET last_used = NOW(), tasks_owner = $task_id WHERE worker_id = $worker_id

5. COMMIT

待办事项PHP程序,如果成功:

6. UPDATE tasks SET times_run = times_run + 1, workers_used = IF(workers_used = '', '$worker_id', CONCAT(workers_used,', $worker_id'))

这应该是它还是我错在某些时候? 是否真的需要tasks_owner还是足以改变last_used日期?

回答

1

阅读我的回答有关如何实现MySQL中的作业队列这里的另一个问题可能是有用的:

MySQL deadlocking issue with InnoDB

总之,使用LOCK TABLES因为这是完全没有必要的,不可能产生良好的结果。您的回复

+0

谢谢,我注意到你从你一些其他的答案,并希望你在这里回答为好。我已经更新了我的初始文章,实施了您在其他文章中建议的例程。你能检查一下它是否正确吗? – maddo7

+0

附加说明:我不明白你的观点3.2:如何检查一个条目是否不再无人认领? – maddo7

+0

附加注释II:我只是用包含一个ID,一个数字和一个日期的表格进行测试,并用各种条目填充它。然后我在2个php文件中同时执行这些查询:'SET autocommit = 0; BEGIN TRANSACTION; SELECT * FROM mysql_tests ORDER BY date date ASC FOR UPDATE; UPDATE mysql_tests SET count = count + 1 WHERE tid = 1; COMMIT;'I added提交前10秒延迟,所以我可以看到会发生什么。结果是一个带有查询的脚本在执行查询之前等待另一个脚本完成,因此它们都在同一行上工作。有没有一种方法, – maddo7