繁体   English   中英

PHP/MySQL - 同时进行多个查询

[英]PHP/MySQL - Multiple queries at the same time

我有 24 个数据库,其中有一个标有email_queue的表。

我有另一个数据库,其中列出了所有包含email_queue表的数据库。

我遍历数据库列表并查询我的email_queue表以发送每个数据库的邮件。

这样做的问题是 php 脚本在发送 500 封电子邮件时被阻止在第三个数据库上,然后离开其他数据库等待轮到他们。

我想弄清楚如何同时查询所有 24 个数据库并同时发送电子邮件队列。

有什么建议?

我认为拥有这么多数据库可能是设计不佳的标志。 如果你不能改变它并且现在需要继续前进,我建议两种选择之一:

  1. 使用参数运行相同的脚本以选择要使用的数据库。 您应该能够找到有关如何执行此操作的资源
  2. 使用非阻塞查询 这个答案的其余部分将用于谈论这个。

这是一个使用 mysqli 扩展的比较完整的例子(需要 mysqlnd 驱动程序):

$credentials = array(
    array(
        'host' => 'host1',
        'user' => 'user',
        'password' => 'password',
        'database' => 'database'
    ),
    array(
        'host' => 'host2',
        'user' => 'user',
        'password' => 'password',
        'database' => 'database'
    ),
    // credentials for other sites
);
$dbcs = array();
foreach ($credentials as $config) {
    $dbcs[] = array($db = new mysqli(
        $config['host'],
        $config['user'],
        $config['pass'],
        $config['database']
    ));
    $query = ""; // here is your query to do whatever it is with your table
    $db->query($query, MYSQLI_ASYNC);
}

$results = array();
$errors = array();
$rejected = array();
$secondsToWait = 1;

while (!empty($dbcs)) {
    foreach ($dbcs as $key => $c) {
        $db = $c[0];
        if (mysqli_poll($c, $errors, $rejected, $secondsToWait) == 1) {
            $r = $db->reap_async_query();

            // here you would do your fetches for each query, such as
            $results[] = $r->fetch_assoc();

            // do what you need to do with the result

            // then cleanup
            $r->free();
            $db->close();
            unset($dbcs[$key]);
        }
    }
}

请注意,它确实有缺点,例如失败的查询可能会导致整个程序崩溃。

这样做的方法是使用 curl_multi_open

将你的脚本分成两部分,你可以让一个 php 文件(比如 email_out.php)使用 db 名称(或者一些用于查找 db 名称的变量,开关将在 for 循环中或在 email_out.php 中) ,然后根据该脚本进行群发电子邮件。

第二部分使用 curl_multi_open 多次打开 email_out.php 脚本,有效地创建到不同数据库的多个单独连接,脚本可以在不同时间解析,因为它们都是并行运行的。 本质上,您的循环现在使用不同的参数多次将脚本添加到 curl_multi_open,然后异步执行所有这些。

class Fork
{
    private $_handles = array();
    private $_mh      = array();

    function __construct()
    {
        $this->_mh = curl_multi_init();
    }

    function add($url)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        curl_multi_add_handle($this->_mh, $ch);
        $this->_handles[] = $ch;
        return $this;
    }

    function run()
    {
        $running=null;
        do {
            curl_multi_exec($this->_mh, $running);
            usleep (250000);
        } while ($running > 0);
        for($i=0; $i < count($this->_handles); $i++) {
            $out = curl_multi_getcontent($this->_handles[$i]);
            $data[$i] = json_decode($out);
            curl_multi_remove_handle($this->_mh, $this->_handles[$i]);
        }
        curl_multi_close($this->_mh);
        return $data;
    }
}

(来自http://gonzalo123.com/2010/10/11/speed-up-php-scripts-with-asynchronous-database-queries/

所以你的循环看起来像这样:

$fork = new Fork;
for ($i = 0; $i < 24; $i++) {
    $fork->add("email_out.php?t=" . $i);
}
$fork->run();

在你的脚本中试试这个。

  1. 使用set_time_limit(0); 为了覆盖 PHP 的 max_execution_time
  2. 从命令行运行脚本时,使用getopt()函数获取数据库名称(即php script.php -d database1 )。
  3. 从那里做逻辑。
  4. 在您的 crontab 中,为您希望使用我在 2 中指定的开关 ( -d ) 发送电子邮件的每个数据库创建一个条目。如果您有 20 个数据库,那么您必须有 20 个条目。

使用这种方式,您将看到每个 cron 作业都有一个单独的 PHP 进程,如果遇到错误,您可以隔离数据库。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM