并行cron作业获取相同的SQL行

时间:2013-12-27 11:24:14

标签: php mysql sql cron

我基本上有一个cron文件,它同时向{1}}发送一个文件,因此是并行的,在cron文件中。

我的 cron 文件看起来像这样(发送并行请求)

multi_curl

然后在我的<?php require "files/bootstrap.php"; $amount = array( "10","11","12","13","14" ); $urls = array(); foreach($amount as $cron_id) { $urls[] = Config::$site_url."single_cron.php?cron_id=".$cron_id; } $pg = new ParallelGet($urls); ?> 内我得到了以下查询

single_cron.php

即使我在查询中有SELECT * FROM accounts C JOIN proxies P ON C.proxy_id = P.proxy_id WHERE C.last_used < DATE_SUB(NOW(), INTERVAL 1 MINUTE) AND C.status = 1 AND C.running = 0 AND P.proxy_status = 1 AND C.test_account = 0 ORDER BY uuid() LIMIT 1 ,他们似乎仍然会以某种方式拾取同一行,防止这种情况的最佳方法是什么?我听说过交易

我正在使用的当前框架是PHP,所以如果有任何解决方案可行,那么我可以自由地使用解决方案。

1 个答案:

答案 0 :(得分:1)

检查select for update命令。这可以防止其他并行查询通过阻止选择同一行,直到您执行commit。所以你的选择应该包括last_process_time > 60之类的条件,你应该在选择它之后更新该行,将last_processed_time设置为当前时间。也许您有一种不同的机制来检测最近是否选择/处理了一行,您也可以使用它。关于它的重要一点是select for update会对行进行锁定,所以即使你并行运行查询,它们也会被mysql服务器序列化。

这是确保您没有2个查询选择同一行的唯一方法 - 即使您的uuid()订单工作正常,您也可以选择2个并行查询中的相同行

使用交易执行此操作的正确方法是:

START TRANSACTION;

SELECT * 
    FROM accounts C JOIN proxies P 
                ON C.proxy_id = P.proxy_id 
    WHERE C.last_used < DATE_SUB(NOW(), INTERVAL 1 MINUTE)
    AND C.status = 1
    AND C.running = 0
    AND P.proxy_status = 1
    AND C.test_account = 0
    LIMIT 1;

(假设您的帐户表中有一个列'ID',用于唯一标识行)

UPDATE accounts
set last_used=now(), .... whatever else ....
where id=<insert the id you selected here>;

COMMIT;

将首先执行到达服务器的查询,并锁定返回的行。此时将阻止所有其他查询。现在你更新你想要的任何东西。提交后,将执行其他进程的其他查询。他们将找不到您刚刚更改的行,因为last_used < ...条件不再正确。其中一个查询将找到一行,将其锁定,其他查询将再次被阻止,直到第二个进程执行提交。这一直持续到一切都结束。

您也可以在会话中将自动提交设置为0,而不是START TRANSACTION。并且不要忘记这仅在使用InnoDB表时才有效。如果您需要更多详细信息,请查看我给您的链接。