调用和“忘记”异步 mysqlnd 插入查询的最佳方式


Best way to call and "forget about" an async mysqlnd INSERT query

Setup

在这种情况下,我正在使用php和mysql。

假设我有这样的 mysqli 连接:

$link = new mysqli('localhost', 'root', 'password', 'A_Database');

我已经安装了mysqlnd来使用"MYSQL_ASYNC"参数执行异步mysql查询:

$link->query("INSERT INTO `A_Table` VALUES('stuff!')", MYSQLI_ASYNC);

目标

只想插入一条记录,直到遥远的将来才需要检索它,所以我不关心异步查询完成需要多长时间,并且当我知道查询完成时,我不需要执行一些最终操作。一旦我通过了发生插入查询的代码部分,我确实需要执行其他不相关的 mysql 查询。

问题

执行此类查询将阻止脚本中稍后出现不同步错误的其他查询。为了解决这个问题,我不得不在每个异步查询后添加类似于以下代码的内容:

$links = $errors = $reject = array($link);
if ($link->poll($links, $errors, $reject, 1)) {
    foreach ($links as $resultLink) {
        if ($result = $resultLink->reap_async_query()) {
            if (is_object($result)) {
                $result->free();
            }
        }
    }
}

这有效地阻止了不同步的错误,我的代码工作正常。
不过,有两件事仍然困扰着我:

    我不想为此
  1. 烦恼,因为我只执行插入查询,当我知道插入完成时,我不关心在我的代码中有一些响应。

  2. 轮询代码在我的服务器上运行得非常慢;比执行常规查询并同步返回结果要慢得多。

回顾

我想运行具有两个要求的插入查询;查询是非阻塞(异步)的,我以后仍然可以执行其他 mysql 查询。我只想插入查询并"忘记它",然后继续我的代码。

关于最好的方法是什么的任何建议?

如果您的目标是插入记录并继续执行代码而不用担心结果,则可以使用 INSERT DELAY 语法而无需 ASYNC 标志:

$link->query("INSERT DELAYED INTO `A_Table` VALUES('stuff!')" );

它会立即返回,您可以继续在同一连接上执行新查询。我们在日志系统上使用它。

如果您希望能够触发并忘记您的查询,您当前的方法基本上是正确的,尽管我建议您遇到的缓慢可能是因为您在$link->poll($links, $errors, $reject, 1)中将"1"设置为"secs"参数。这意味着轮询将等待最多 1 秒以返回查询。如果将其设置为"0",则它不会等待,它会更快地返回查询是否完成的答案。

其次,如果你不需要这个查询的答案,你可以在执行这个查询时做其他事情,如果你有其他事情要做,你不必坐在那里等待它在异步轮询循环中返回。这就是异步查询的设计目的。我建议您在查询执行方法中添加检查以检查是否已执行未完成的 ASYNC 查询,因此每当您需要触发新查询时,它才会轮询服务器并等待任何以前的查询完成并变得可用。

或者,如果要运行多个相互没有任何

影响或依赖关系的查询,没有什么可以阻止您设置多个数据库连接并并行对所有数据库连接运行 ASYNC 查询,或者在需要时设置新连接并执行查询。

如果你想把你的SQL完全外包给另一个进程(没有cron),一种方法是设置一个持久的监视脚本,例如使用Supervisor/Gearman,这样你就可以向它发送查询,并让它处理与本地进程不同的进程(甚至服务器)中的查询。这样做的好处是远程脚本作为 CLI 应用程序执行,因此具有更多使用分支和多线程处理的能力,并且可以在不太暴露的环境中运行。

另一种方法(可选地使用 cron)是使用写入优化(可能是内存中)本地数据库表,快速执行插入到该表中,然后让另一个进程间歇性地读取此表(或设置触发器以监视新条目),并让该进程从该表中获取新插入的行并将它们移动到速度较慢的主表中。