【问题标题】:PHP + MySQL QueuePHP + MySQL 队列
【发布时间】:2010-12-14 00:40:23
【问题描述】:

我需要一个充当队列的简单表。 我的 MySQL 服务器限制是我不能使用 InnoDB 表,只能使用 MyISAM。

客户/工人将同时工作,他们每次都需要接受不同的工作。

我的想法是执行以下操作(伪代码):

$job <- SELECT * FROM queue ORDER BY last_pop ASC LIMIT 1;
UPDATE queue SET last_pop WHERE id = $job->id
return $job

我曾尝试过表锁定和“GET_LOCK”,但没有任何反应,工人有时会收到相同的工作。

【问题讨论】:

    标签: php mysql queue message-queue


    【解决方案1】:

    您需要扭转订单,这样就没有时间窗口了。

    消费者 POP(每个消费者都有一个唯一的 $consumer_id)

    Update queue 
    set last_pop = '$consumer_id' 
    where last_pop is null 
    order by id limit 1;
    
    $job = 
      Select * from queue 
      where last_pop = '$consumer_id' 
      order by id desc 
      limit 1;
    

    供应商推动

    insert into queue 
      (id, last_pop, ...) 
    values 
      (NULL, NULL, ...);
    

    队列按 id 列按时间排序,并在 POP 时由 consumer_id 分配。

    【讨论】:

    • +1 - 我打算使用状态字段,但您的想法与我的想法相同。
    • 我很确定这个解决方案是错误的。 UPDATE 和 SELECT 之间存在竞争条件。假设您有两个并行请求运行此代码。如果这是它的执行顺序:UPDATE 1, UPDATE 2, SELECT 1, SELECT 2。你最终会选择相同的行。
    • @OlegKikin,UPDATE2 不可能与 UPDATE1 发生冲突,因为在 UPDATE1 之后 last_pop 不能是 NULL
    • UPDATE 之后和SELECT 之前的失败将导致孤立项。 START TRANSACTIONCOMMIT 是避免这种风险所必需的。
    • @ethanpil,last_pop 字段在物品被认领之前将为 NULL,然后在选择进行处理时将包含唯一的消费者 ID。没有计时窗口,因为选择不需要与更新重合。
    【解决方案2】:

    仅供参考,还有另一个选项是使用 Gearman 而不是表来创建队列:Rasmus Lerdorf wrote a very nice article about it

    【讨论】:

      【解决方案3】:

      奥列格,

      解决方案是正确的。 $consumer_id 必须是处理器的唯一标识符。例如,如果您在一台机器上有几个 cron 作业,您可以使用他们的 pid 作为 consumer ID

      UPDATE 是原子的,因此它将队列中的一行标记为您的ID使用

      对于某些应用程序,我还有一个已完成的状态字段,因此如果设置了 last_pop 的 consumer_id,但未设置已完成标志并且作业早于 X,则可以标记为重新启动。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-01-14
        • 1970-01-01
        • 2016-11-13
        • 2015-02-12
        • 1970-01-01
        • 2023-03-18
        • 1970-01-01
        相关资源
        最近更新 更多