为什么使用事务时 multi_query 不起作用?

Pra*_*nki 8 php mysqli multi-query mysqli-multi-query

这是一个例子。

$mysqli = new mysqli("localhost", "root", "123", "temp");

$mysqli->begin_transaction();

$sql1 = "insert into test (Name) values ('pratik5');";
$sql1 .= "insert into test (Name) values ('pratik6');";

$test = $mysqli->multi_query($sql1);

$mysqli->commit();
Run Code Online (Sandbox Code Playgroud)

两个查询中都没有任何错误,但是在调用时commit()这些值并未存储在数据库中。如果拆分为单独的查询并通过query().

Pat*_*ert 5

$mysqli->multi_query($sql1);
$mysqli->commit(); // This will result in a "Commands out of sync; you can't run this command now" error.
Run Code Online (Sandbox Code Playgroud)

上面的内容等同于:

$mysqli->multi_query($sql1);
$mysqli->query("commit"); // This will result in a "Commands out of sync; you can't run this command now" error.
Run Code Online (Sandbox Code Playgroud)

无论你输入什么$mysqli->query("...");,它都会导致"Commands out of sync"错误,即使是简单的SELECT 1;

此错误的原因是因为->commit()操作运行单个查询 ( commit;)。但之前查询的结果还没有被读取。

当使用单个query()操作时,MySQL服务器将根据查询语句以响应帧进行应答。

使用时multi_query(),在 MySQL 通信协议级别会发生以下情况:

  1. 发送“Request Set Option”(如 Wireshark 中显示的)帧,并将“multi statements”标志设置为ON
  2. multi_query()包含作为请求传输的整个字符串的帧。
  3. MySQL 服务器以可能包含不同结果集的响应进行应答。调用存储过程时也是如此。

解决方案1

如果您想使用multi_query(),则必须将start transaction/commit操作作为其中的一部分:

$mysqli = new mysqli("localhost", "root", "123", "temp");

$sql1 = "start transaction;"; // $mysqli->begin_transaction() is a convenience function for simply doing this.
$sql1 .= "insert into test (Name) values ('pratik5');";
$sql1 .= "insert into test (Name) values ('pratik6');";
$sql1 .= "commit;"; // $mysqli->commit() is a convenience function for simply doing this.

$mysqli->multi_query($sql1);

/* As in "Solution 2", if you plan to perform other queries on DB resource
   $mysqli after this, you must consume all the resultsets:
// This loop ensures that all resultsets are processed and consumed:
do {
    $mysqli->use_result();
}
while ($mysqli->next_result());
*/
Run Code Online (Sandbox Code Playgroud)

解决方案2

$mysqli = new mysqli("localhost", "root", "123", "temp");

$mysqli->begin_transaction();

$sql1 = "insert into test (Name) values ('pratik5');";
$sql1 .= "insert into test (Name) values ('pratik6');";

$mysqli->multi_query($sql1);

// This loop ensures that all resultsets are processed and consumed:
do {
    $mysqli->use_result();
}
while ($mysqli->next_result());

// Now that all resultsets are processed, a single query `commit;` can happen:
$mysqli->commit();
Run Code Online (Sandbox Code Playgroud)

MySQL 参考:“命令不同步”

  • MySQL允许多个查询的通信协议不知道内容。对于“mysqli->multi_query()”也是如此。无论“SELECT”与“UPDATE”/“DELETE”/“INSERT”类型如何,每个单独的查询都会由服务器生成一个结果集。这意味着如果它们是 `$sql1` 内的 2 条语句,则 while 循环将进入两次,在其中放入一些 echo 语句,您将看到它的运行情况。 (2认同)
  • INSERT 不会产生结果,除非将它们放入对“multi_query()”的调用中。当使用“multi_query()”时,PHP 不会将提供的字符串中的各种查询分开并单独发送每个查询。它是底层 MySQL 客户端库,它发送一个设置了“多语句标志”的“请求设置选项”,以便下一个 MySQL 请求包含传递给“multi_query()”的整个字符串。PHP 不会分析传递给 `multi_query()` 的内容,它会**按原样**发送整个内容。 (2认同)

You*_*nse 3

您不应该使用多重查询。重写你的代码如下

$mysqli->begin_transaction();

$mysqli->query("insert into test (Name) values ('pratik5')");
$mysqli->query("insert into test (Name) values ('pratik6')");

$mysqli->commit();
Run Code Online (Sandbox Code Playgroud)

或者,对于现实生活中的插入,

$mysqli->begin_transaction();

$stmt = $mysqli->prepare("insert into test (Name) values (?)");
$stmt->bind_param("s", $name);
$name = 'pratik5';
$stmt->execute();
$name = 'pratik6';
$stmt->execute();

$mysqli->commit();
Run Code Online (Sandbox Code Playgroud)

  • @YourCommonSense:`multi_query()` 不是异步的,这不是失败的原因,而是因为所有结果集都应该被消耗/释放。 (3认同)