php从多个运行脚本访问数据库,无需重复选择

时间:2013-12-09 19:15:19

标签: php database

我有一个将图片发布到Facebook个人资料的脚本。它需要很长时间,通常在完成之前崩溃。我需要做的是从cron作业运行多个版本的脚本,同时进行多次处理,以便更快地发布。

我的问题我希望同时运行多个脚本以加快发布速度。 选择一个用户,并更新为已发送。但是,我需要确保脚本不会同时处理两个用户,或者从数据库中选择同一个用户两次,而另一个用户可能正在处理运行脚本,从而导致在用户墙上发布两张照片。

我的问题涉及调用数据库的效率,并创建一种在此数据库上同时运行多个脚本以提高后期速度的方法。

我有一个名为tweetSent的字段,它设置为notSent。发布照片后,它会更新为“已发送”。但是每个帖子需要将近7秒钟,如果另一个脚本在处理一个用户时访问了数据库,它会在该用户墙上发布双重帖子,因为表中的字段仍然是“notSent”

我的代码说选择tweetSent为'notSent'的用户。我有一个想法可以解决我的问题。我正在考虑立即更新它以“发送”,因此同时运行的另一个多个cron作业将选择此用户。然后,当它完成发布时,它将更新为“已发送”。

我还看到了我的代码的另一个问题。它似乎同时SELECT所有用户,然后运行WHILE LOOP并从此循环发布。在我看来,也许我应该选择“tweetSent”为“notSent”的一个用户,然后立即将其更新为“发送”,然后在发送时将其更新为已发送。也许可以将这个单一调用放入函数中的数据库并在while循环中调用该函数?不确定。

我对Stack上所有伟大的编码员的问题是,我想知道这是否是最好的方法来做到这一点,或者有人知道一种更好,更有效的方法来做我想要实现的目标。

下面是我目前脚本的数据库部分,它运行得非常慢,并且不允许我在没有双重帖子的情况下运行此脚本的多个版本。

$query = "SELECT * FROM `user` WHERE tokenExpireDate > \"$date\" AND tweetSent='notSent' ";


$retval = mysql_query($query) or die("MySQL Error: ".mysql_error());

while($row = mysql_fetch_array($retval))
{

    PostPhoto($fbId, $access_token);
    PostText($fbId, $access_token);


    echo "<hr>";

    $query = "UPDATE user SET tweetSent='sent' WHERE fbID='$fbId'";
    mysql_query($query) or die("MySQL Error: ".mysql_error());

}

更新:

我想出了一个'潜在的'解决方案。这会起作用还是这种愚蠢? 当我在3个不同的窗口中同时运行脚本时,我似乎工作了 它只按顺序运行它们,不能同时连接到数据库。好像。

$query = "SELECT * FROM `user` WHERE tokenExpireDate > \"$date\" AND tweetSent='notSent' LIMIT 5";
$retval = mysql_query($query) or die("MySQL Error: ".mysql_error());
while($row = mysql_fetch_array($retval))
    {
        $ID[] = $row['fbID'];
    }

echo count($ID);

for($i=0; count($ID)>$i;$i++)
{   
sendTweet($ID[$i]);
}

function sendTweet($ID)
{
    // QUES THE USER ID
    $query = "SELECT * FROM `user` WHERE tokenExpireDate > \"$date\" AND tweetSent='notSent' AND fbID = '$ID' LIMIT 7";
    $retval = mysql_query($query) or die("MySQL Error: ".mysql_error());

    if (mysql_num_rows($retval) >= 1 ) 
    { 
            $query = "UPDATE user SET tweetSent='sending' WHERE fbID='$ID'";
            mysql_query($query) or die("MySQL Error: ".mysql_error());  
            while($row = mysql_fetch_array($retval))
            {
                $fbId = $row['fbID'];
                $type = $row['type'];
                $access_token = $row['longToken'];


                PostPhoto($fbId, $access_token);
                PostText($fbId, $access_token);


                /// UPDATES TO SENT
                $query = "UPDATE user SET tweetSent='sent' WHERE fbID='$fbId'";
            mysql_query($query) or die("MySQL Error: ".mysql_error());
        }
    }
}

3 个答案:

答案 0 :(得分:1)

一些建议:

  • 您需要了解流程中的瓶颈所在。只是猜测,我会想象这将是Facebook的实际POST。您应该集中精力使这部分更有效率,否则您将永远无法获得所需的吞吐量。
  • 在用尽其他方法之前,不要使用多个重叠的cron脚本。
  • 假设Facebook的POST是性能块,您可以考虑使用curl_mulit_exec()或类似的方法同时对Facebook进行多次调用。我在PHP中设置了一个简单的REST客户端类,我已经设置了这样做。您可以随意使用它,或者只是查看curl_multi_exec()的实现,以了解如何执行此操作。这是链接 - https://github.com/mikecbrant/php-rest-client
  • 使用数据库时,应考虑使用LIMIT语句。通过这种方式,您可以一次处理表的较小子集,更新此子记录组的成功POST,然后再继续下一步。例如,假设您使用curl_multi_exec()一次向Facebook发送10个帖子,您可以从数据库中一次选择10行,为这些行制作10个并行的Facebook请求,将发布结果更新为这些行,然后转到下一组记录
  • 您也可以考虑使用某种排队系统,因为这实质上是您在这里要做的就是建立一个队列。如果这对您来说似乎是一项艰巨的任务,那么可能从上面的步骤开始,如果您发现这仍然无法满足您的需求,请移至队列。当然,如果您希望对大量记录执行此类操作,您可能需要三思而后行直接排队,因为使用关系数据库作为队列并不适合大流量。

答案 1 :(得分:0)

最佳解决方案是将照片发布系统更改为message queue,因为它听起来非常像一个。

IronMQ是一个很好的起点,是最容易使用的,从那里你可以探索其他队列系统。你也获得了很好的免费等级。其他的是RabbitMQAmazon SQS

队列通过从队列中删除或“弹出”消息然后处理来解决您的问题。如果发生错误或无法处理消息,则将其重新放回队列。

通过将其从队列中删除,您可以确保没有其他进程可以访问它。

您可能还想查看Gearman


如果您不想将系统更改为队列系统,那么您需要查看事务并记录锁定。将记录设置为processing可以允许两个进程同时提取记录,然后一个接一个地将其更新为processing,从而导致两个进程都发布了照片。

答案 2 :(得分:-1)

private static $MysqlAdapter;


private $con;

private function __construct() {
$this->con = new mysqli(DB_SERVER, DB_USER, DB_PW, DB_DB);
if ($this->con->connect_errno) {
    echo "Failed to connect to MySQL: (" . $this->con->connect_errno . ") " . $this->con->connect_error;
} else {
    $this->con->set_charset("utf8");
}
}

public static function getInstance() {
if (self::$MysqlAdapter == NULL) {
    self::$MysqlAdapter = new MysqlAdapter();
}
return self::$MysqlAdapter;
}