【问题标题】:Questions about Queue System关于排队系统的问题
【发布时间】:2014-03-03 20:07:41
【问题描述】:

我有一个 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
...

Tasks 是一个用于跟踪任务的表格:

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

worker_info 是一个表格,其中包含一些 php 脚本需要完成其工作的信息以及 last_used ,后者是该工作人员上次使用时间的全局指示符。

几个 php 脚本处理相同的任务,我需要精确的值,因为每个 worker_info 应该只为每个任务使用 1 次。

PHP cron 作业包含所有相同的例程:

脚本执行 mysql 查询以获取任务。

1. SELECT * FROM tasks ORDER BY times_run ASC LIMIT 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 而不使用第二个表来声明依赖项是非常糟糕的做法,但我有点害怕它会占用的空间。 一个任务可以有几千个workers_used,而我自己有几千个任务。这样表格很快就会超过 100 万个条目,我担心这会大大降低速度,所以我采用了这种存储方式。

然后脚本为每个任务执行步骤 2-6 10 次,然后返回步骤 1 选择新任务并再次执行所有操作。

现在这个设置已经为我服务了大约一年,但是现在我需要在这个队列系统上激活 50 多个 php 脚本,我在性能方面遇到了越来越多的问题。 PHP 查询最多需要 20 秒,而且我无法像我需要的那样扩展,如果我只运行更多 PHP 脚本,mysql 服务器就会崩溃。 如果系统崩溃,我不希望数据丢失,因此我将每次更改都写入数据库。此外,当我创建系统时,worker_used 出现问题,因为当 10 个 php 脚本在 1 个任务上工作时,经常会在同一个任务中多次使用一个 worker_info 数据,这是我不想要的。

因此我引入了 LOCK 来解决这个问题,但我怀疑它是系统的瓶颈。如果一个工作人员锁定了表来执行它的操作,那么所有其他 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 日期就足够了?

【问题讨论】:

    标签: php mysql queue innodb


    【解决方案1】:

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

    MySQL deadlocking issue with InnoDB

    简而言之,为此使用LOCK TABLES 是完全没有必要的,而且不太可能产生好的结果。

    【讨论】:

    • 感谢您的回复,我从您的其他一些回答中注意到了您,希望您也能在这里回答。我已经更新了我的初始帖子,实施了您在其他帖子中建议的例程。请您检查一下是否正确?
    • 附加评论:我不太明白你的意思 3.2:如何检查条目是否不再无人认领?
    • 附加评论二:我刚刚对一个包含 id、数字和日期的表格进行了测试,并在其中填充了各种条目。然后我同时在 2 个 php 文件中执行了这些相同的查询:SET autocommit=0;BEGIN TRANSACTION;SELECT * FROM mysql_tests ORDER BY date ASC FOR UPDATE;UPDATE mysql_tests SET count = count + 1 WHERE tid = 1;COMMIT; 我在提交前添加了 10 秒的延迟,这样我就可以看到会发生什么。结果是一个带有查询的脚本在执行其查询之前等待另一个脚本完成,因此它们都在同一行上工作。有没有办法
    • @Matthias 在 3.2 中,您将获取要锁定的行。如果lease_owner 现在不为NULL,则该行在您到达之前已被声明,您应该中止(回滚您的事务以释放锁)并从步骤1 重试。
    • 如果一行被锁定,它将被跳过并使用下一个?例如,如果有 2 行符合 ORDER BY 查询的条件,并且一个查询锁定了第一行。是否有可能第二个文件查询只会得到第二行而不是等待第一个查询完成?
    猜你喜欢
    • 1970-01-01
    • 2011-05-20
    • 2011-04-04
    • 1970-01-01
    • 2011-06-20
    • 2010-10-17
    • 2011-11-19
    • 1970-01-01
    • 2020-08-17
    相关资源
    最近更新 更多