【问题标题】:PHP/MySQL - Multiple queries at the same timePHP/MySQL - 同时进行多个查询
【发布时间】:2020-08-03 08:44:11
【问题描述】:

我有 24 个数据库,其中一个表标记为 email_queue

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

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

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

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

有什么建议吗?

【问题讨论】:

  • 听起来一团糟。您应该使它们成为多个表,而不是多个 DB。
  • 这听起来像是你经常做的事情。您已经建立了一个分离,不妨现在就使用它:运行每个队列的 24 sep cron 脚本。如果您担心会导致过多的拥堵,请将它们错开。
  • 为什么有这么多数据库?
  • 有多个数据库,因为它们用于我拥有的多个站点。我喜欢将它们与其他表和数据一起保存在单独的数据库中。
  • 我会编写多个 cron 来运行多个脚本,每个队列一个,但问题是队列的数量会增加。所以我想要一个动态的解决方案。一定有办法,但我只是在画一个空白。 :(

标签: php mysql sql database


【解决方案1】:

我认为拥有这么多数据库可能是糟糕设计的标志。如果您无法更改它并且需要现在继续前进,我建议您选择以下两种选择之一:

  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]);
        }
    }
}

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

【讨论】:

    【解决方案2】:

    使用 curl_multi_open 可以做到这一点

    将你的脚本一分为二,你可以让一个 php 文件(比如 email_out.php)获取数据库名称(或一些用于查找数据库名称的变量,开关将在 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();
    

    【讨论】:

    • 我正在使用您的建议,但是... $test = $fork->run();print_r($test);返回数组 ( [0] => [1] => [2] => [3] => [4] => [5] => [6] => [7] => [8] => [9 ] => [10] => [11] => [12] => [13] => [14] => [15] => [16] => [17] => [18] => [19 ] => [20] => [21] => [22] => [23] => [24] => )
    • 我有 email_sender.php 脚本输出成功或失败。
    • 我修好了。我的脚本链接出错。非常感谢!这很好用!
    【解决方案3】:

    在你的脚本中试试这个。

    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 进程,并且您可以在遇到错误时隔离数据库。

    【讨论】:

      猜你喜欢
      • 2018-05-20
      • 2015-12-31
      • 1970-01-01
      • 2019-01-26
      • 1970-01-01
      • 1970-01-01
      • 2021-10-30
      相关资源
      最近更新 更多