PHP Apache 在执行存储过程时崩溃
PHP Apache crashes while executing a STORED PROCEDURE
问题
当我执行以下代码时(我正在调用一个带有 5 个 IN 参数和 1 个 OUT 参数的存储过程)
$conn->query("SET @res = ''");
$mysqli=$conn;
if (!($stmt = $mysqli->prepare("CALL retrieve_matches(5,3, 16, 2, false, @res)"))) {
echo "Prepare failed: (" . $mysqli->errno . ") " . $mysqli->error;
}
if (!$stmt->execute()) {
echo "Execute failed: (" . $stmt->errno . ") " . $stmt->error;
}
do {
if ($res = $stmt->get_result()) { //Apache crash on this call
printf("---\n");
var_dump(mysqli_fetch_all($res));
mysqli_free_result($res);
} else {
if ($stmt->errno) {
echo "Store failed: (" . $stmt->errno . ") " . $stmt->error;
}
}
} while ($stmt->more_results() && $stmt->next_result());
apache 因错误而崩溃:
AH00428: Parent: child process 9628 exited with status 255 --
Restarting.
我试过的
- 此代码运行良好,并且正确返回结果:
$conn->query("SET @res = ''");
$res=$conn->query("CALL retrieve_matches(5,3, 16, 2, false, @res)");
var_dump($res->fetch_assoc());
注意:如果我只是更改存储过程输入中的数字,则上面导致 Apache 崩溃的相同代码可以正常工作,例如:
if (!($stmt = $mysqli->prepare("CALL retrieve_matches(5,6, 16, 2, false, @res)"))) {
- 从 MySQl workbench 开始尝试,两个调用都正常。
- 我在 WIN7 Enterprise 64b 上使用 WAMP 64b,我尝试使用 WAMP 32b 但遇到了同样的问题。
- 我检查了 windows 事件,发现 httpd.exe 崩溃是由 php5ts.dll
引起的
- 如果您在 google 上搜索 "httpd.exe php5ts.dll",您会发现很多人都在解决这个问题。但是我没有找到 WAMP 的解决方案...
- 尝试使用 AMPPS,问题完全相同
PHP 错误日志
[10-Jul-2015 15:30:03 UTC] PHP Warning: PHP Startup: Unable to load dynamic library 'C:/Program Files/wamp/bin/php/php5.5.12/ext/php_ldap.dll' - Impossibile trovare il modulo specificato.
in Unknown on line 0
[10-Jul-2015 15:30:04 UTC] PHP Warning: PHP Startup: Unable to load dynamic library 'C:/Program Files/wamp/bin/php/php5.5.12/ext/php_intl.dll' - Impossibile trovare il modulo specificato.
APACHE 错误日志:
[Tue Jul 14 15:02:13.038276 2015] [mpm_winnt:notice] [pid 7044:tid 404] AH00428: Parent: child process 9448 exited with status 255 -- Restarting.
[Tue Jul 14 15:02:13.324305 2015] [mpm_winnt:notice] [pid 7044:tid 404] AH00455: Apache/2.4.9 (Win32) PHP/5.5.12 configured -- resuming normal operations
[Tue Jul 14 15:02:13.329306 2015] [mpm_winnt:notice] [pid 7044:tid 404] AH00456: Apache Lounge VC11 Server built: Mar 16 2014 12:13:13
[Tue Jul 14 15:02:13.329306 2015] [core:notice] [pid 7044:tid 404] AH00094: Command line: 'C:\Program Files\wamp\bin\apache\apache2.4.9\bin\httpd.exe -d C:/Program Files/wamp/bin/apache/apache2.4.9'
[Tue Jul 14 15:02:13.352308 2015] [mpm_winnt:notice] [pid 7044:tid 404] AH00418: Parent: Created child process 3140
[Tue Jul 14 15:02:14.528388 2015] [mpm_winnt:notice] [pid 3140:tid 332] AH00354: Child: Starting 64 worker threads.
我真的迷路了,我应该在哪里寻找问题?
非常感谢您的帮助
编辑
我意识到存储过程 "retrieve_matches" 正在根据更改的值调用不同的存储过程。我调试了导致 Apache 崩溃的存储过程 "retrieve_standalone"。这个程序正在做 select/insert,我检查了插入是否正确。
但是在 "retrieve_standalone" 我以一种奇怪的方式使用光标:
declare bNoMoreRows bool default false;
declare tmp_cursor cursor for
select comp_id from to_match; -- to_match is a temporary table
declare continue handler for not found set bNoMoreRows := true;
如果我不打开游标
open tmp_cursor;
一切正常!!所以我想我现在发现了问题:我该如何解决它?
我认为您混淆了 $conn
和 $stmt
。这是我所拥有的。 (它将结果收集到 $out 中。)
if ($conn->multi_query($call)) {
$raa = array();
do {
/* store first result set */
if ($result = $conn->use_result()) {
while ($row = $result->fetch_row()) {
$raa[] = $row;
printf("+\n");
}
$result->close();
}
$out[] = $raa;
/* print divider */
if ($conn->more_results()) {
printf("-----------------\n");
}
} while ($conn->next_result());
}
else
echo "err";
如果 php5ts.dll 或 Apache 的任何其他部分在您每次 运行 一个简短的脚本时以可重现的方式崩溃,您很可能遇到了 php 中的错误。特别是如果你 运行 一个最新的 apache 和从 php.net 下载的最新 php 版本,你很有可能得到 PHP 团队的支持。因此,通读 https://bugs.php.net/how-to-report.php, see if you can reproduce the crash when running the script without apache (in a separate script on the cli using php.exe) and generate a backtrace 然后您可以将其作为错误提交。
像这样的错误有时可以解决,但很少能从脚本的上下文中修复。 PHP 永远不应使网络服务器崩溃,如果它重复出现这种情况,则应提交错误以便修复
我找到了如何重现它,我相信这是一个 php/apache 错误
PHP代码:
if (!($stmt = $mysqli->prepare("CALL test()"))) {
echo "Prepare failed: (" . $mysqli->errno . ") " . $mysqli->error;
}
if (!$stmt->execute()) {
echo "Execute failed: (" . $stmt->errno . ") " . $stmt->error;
}
do {
if ($res = $stmt->get_result()) {
printf("---\n");
//var_dump(mysqli_fetch_all($res));
var_dump($res->fetch_assoc());
mysqli_free_result($res);
} else {
if ($stmt->errno) {
echo "Store failed: (" . $stmt->errno . ") " . $stmt->error;
}
}
} while ($stmt->more_results() && $stmt->next_result());
存储过程代码:
CREATE DEFINER=`root`@`localhost` PROCEDURE `test`()
BEGIN
declare test_var varchar(100) default "ciao";
declare bNoMoreRows bool default false;
declare test_cursor cursor for
select id from tmp_folder;
declare continue handler for not found set bNoMoreRows := true;
create temporary table tmp_folder select "test" as id;
open test_cursor;
fetch test_cursor into test_var;
close test_cursor;
select test_var;
drop temporary table if exists tmp_folder;
END
打开的错误:
阿帕奇:https://bz.apache.org/bugzilla/show_bug.cgi?id=58136
显然有一点推卸责任 - 即 scaricabarile - 在 PHP 人和 MySQLi 人之间。
似乎正在发生的事情是 MySQLi 以 "improper" 方式做出反应,return 一个意外值(我敢打赌它是 NULL)到 PHP, 适当的核心转储。这种行为 is documented 和来自 PHPland 的判决是:"Not a [PHP] bug"。在他们这边,MySQLi 人员坚持认为 PHP 没有正确检查 returned 结果。 'improper' 结果取决于 你的 查询。
所以我要冒险,假设你的问题与 "communication difficulties" 相同,那么问题就变成了:"Your query forces MySQLi to drop/reconnect"。为什么?显然(某些)存储过程 require mysqli_multi_query
in order to behave properly.
并且mysqli_multi_query
与mysqli_prepare
不兼容。
所以我建议在不准备查询的情况下尝试,并 运行 使用 mysqli_multi_query
。
$conn->query("SET @res = ''");
$conn->multi_query("CALL retrieve_matches(5,3, 16, 2, false, @res)");
do {
if ($result = $conn->store_result()) {
while ($row = $result->fetch_row()) {
var_dump($row);
}
$result->free();
}
} while ($conn->more_results() && $conn->next_result());
使用此代码,您的测试用例如预期的那样得到了我,
array(1) {
[0] =>
string(4) "test"
}
尝试更改您的服务器堆栈。在不同的环境中升级 WAMP 或 运行 您的代码。这个问题表明 WAMP 中存在一个错误,会导致 Apache 抛出 255 错误
php -> mysql_connect -> child process exited with status 255 -- Restarting
另一个在这里咬灰尘。刚刚加入以提供解决方法。
带光标的 SP 仅在使用 prepare()
时崩溃。
如果您将其更改为 multi_query()
调用,它将起作用。一个简单的可重现 POC
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = mysqli_connect('127.0.0.1','user','pass','db');
$mysqli->query("DROP PROCEDURE IF EXISTS mysqli_crash");
$mysqli->query("CREATE PROCEDURE mysqli_crash ()
BEGIN
DECLARE col TEXT;
DECLARE res CURSOR FOR SELECT 1 from dual;
OPEN res;
FETCH res INTO col;
CLOSE res;
SELECT 1 from dual;
END;");
echo "--- multi_query ---\n";
$mysqli->multi_query("call mysqli_crash()");
$result = $mysqli->store_result();
echo json_encode($result->fetch_all(MYSQLI_ASSOC)),"\n";
$mysqli->next_result();
echo "--- prepare ---\n";
$stmt = $mysqli->prepare("call mysqli_crash()");
$stmt->execute();
$result = $stmt->get_result();
echo json_encode($result->fetch_all(MYSQLI_ASSOC)),"\n";
$stmt->next_result();
是的,prepare 是首选,但如果您的存储过程使用游标,则别无选择。
问题
当我执行以下代码时(我正在调用一个带有 5 个 IN 参数和 1 个 OUT 参数的存储过程)
$conn->query("SET @res = ''");
$mysqli=$conn;
if (!($stmt = $mysqli->prepare("CALL retrieve_matches(5,3, 16, 2, false, @res)"))) {
echo "Prepare failed: (" . $mysqli->errno . ") " . $mysqli->error;
}
if (!$stmt->execute()) {
echo "Execute failed: (" . $stmt->errno . ") " . $stmt->error;
}
do {
if ($res = $stmt->get_result()) { //Apache crash on this call
printf("---\n");
var_dump(mysqli_fetch_all($res));
mysqli_free_result($res);
} else {
if ($stmt->errno) {
echo "Store failed: (" . $stmt->errno . ") " . $stmt->error;
}
}
} while ($stmt->more_results() && $stmt->next_result());
apache 因错误而崩溃:
AH00428: Parent: child process 9628 exited with status 255 -- Restarting.
我试过的
- 此代码运行良好,并且正确返回结果:
$conn->query("SET @res = ''");
$res=$conn->query("CALL retrieve_matches(5,3, 16, 2, false, @res)");
var_dump($res->fetch_assoc());
注意:如果我只是更改存储过程输入中的数字,则上面导致 Apache 崩溃的相同代码可以正常工作,例如:
if (!($stmt = $mysqli->prepare("CALL retrieve_matches(5,6, 16, 2, false, @res)"))) {
- 从 MySQl workbench 开始尝试,两个调用都正常。
- 我在 WIN7 Enterprise 64b 上使用 WAMP 64b,我尝试使用 WAMP 32b 但遇到了同样的问题。
- 我检查了 windows 事件,发现 httpd.exe 崩溃是由 php5ts.dll 引起的
- 如果您在 google 上搜索 "httpd.exe php5ts.dll",您会发现很多人都在解决这个问题。但是我没有找到 WAMP 的解决方案...
- 尝试使用 AMPPS,问题完全相同
PHP 错误日志
[10-Jul-2015 15:30:03 UTC] PHP Warning: PHP Startup: Unable to load dynamic library 'C:/Program Files/wamp/bin/php/php5.5.12/ext/php_ldap.dll' - Impossibile trovare il modulo specificato.
in Unknown on line 0
[10-Jul-2015 15:30:04 UTC] PHP Warning: PHP Startup: Unable to load dynamic library 'C:/Program Files/wamp/bin/php/php5.5.12/ext/php_intl.dll' - Impossibile trovare il modulo specificato.
APACHE 错误日志:
[Tue Jul 14 15:02:13.038276 2015] [mpm_winnt:notice] [pid 7044:tid 404] AH00428: Parent: child process 9448 exited with status 255 -- Restarting.
[Tue Jul 14 15:02:13.324305 2015] [mpm_winnt:notice] [pid 7044:tid 404] AH00455: Apache/2.4.9 (Win32) PHP/5.5.12 configured -- resuming normal operations
[Tue Jul 14 15:02:13.329306 2015] [mpm_winnt:notice] [pid 7044:tid 404] AH00456: Apache Lounge VC11 Server built: Mar 16 2014 12:13:13
[Tue Jul 14 15:02:13.329306 2015] [core:notice] [pid 7044:tid 404] AH00094: Command line: 'C:\Program Files\wamp\bin\apache\apache2.4.9\bin\httpd.exe -d C:/Program Files/wamp/bin/apache/apache2.4.9'
[Tue Jul 14 15:02:13.352308 2015] [mpm_winnt:notice] [pid 7044:tid 404] AH00418: Parent: Created child process 3140
[Tue Jul 14 15:02:14.528388 2015] [mpm_winnt:notice] [pid 3140:tid 332] AH00354: Child: Starting 64 worker threads.
我真的迷路了,我应该在哪里寻找问题? 非常感谢您的帮助
编辑
我意识到存储过程 "retrieve_matches" 正在根据更改的值调用不同的存储过程。我调试了导致 Apache 崩溃的存储过程 "retrieve_standalone"。这个程序正在做 select/insert,我检查了插入是否正确。 但是在 "retrieve_standalone" 我以一种奇怪的方式使用光标:
declare bNoMoreRows bool default false;
declare tmp_cursor cursor for
select comp_id from to_match; -- to_match is a temporary table
declare continue handler for not found set bNoMoreRows := true;
如果我不打开游标
open tmp_cursor;
一切正常!!所以我想我现在发现了问题:我该如何解决它?
我认为您混淆了 $conn
和 $stmt
。这是我所拥有的。 (它将结果收集到 $out 中。)
if ($conn->multi_query($call)) {
$raa = array();
do {
/* store first result set */
if ($result = $conn->use_result()) {
while ($row = $result->fetch_row()) {
$raa[] = $row;
printf("+\n");
}
$result->close();
}
$out[] = $raa;
/* print divider */
if ($conn->more_results()) {
printf("-----------------\n");
}
} while ($conn->next_result());
}
else
echo "err";
如果 php5ts.dll 或 Apache 的任何其他部分在您每次 运行 一个简短的脚本时以可重现的方式崩溃,您很可能遇到了 php 中的错误。特别是如果你 运行 一个最新的 apache 和从 php.net 下载的最新 php 版本,你很有可能得到 PHP 团队的支持。因此,通读 https://bugs.php.net/how-to-report.php, see if you can reproduce the crash when running the script without apache (in a separate script on the cli using php.exe) and generate a backtrace 然后您可以将其作为错误提交。
像这样的错误有时可以解决,但很少能从脚本的上下文中修复。 PHP 永远不应使网络服务器崩溃,如果它重复出现这种情况,则应提交错误以便修复
我找到了如何重现它,我相信这是一个 php/apache 错误
PHP代码:
if (!($stmt = $mysqli->prepare("CALL test()"))) {
echo "Prepare failed: (" . $mysqli->errno . ") " . $mysqli->error;
}
if (!$stmt->execute()) {
echo "Execute failed: (" . $stmt->errno . ") " . $stmt->error;
}
do {
if ($res = $stmt->get_result()) {
printf("---\n");
//var_dump(mysqli_fetch_all($res));
var_dump($res->fetch_assoc());
mysqli_free_result($res);
} else {
if ($stmt->errno) {
echo "Store failed: (" . $stmt->errno . ") " . $stmt->error;
}
}
} while ($stmt->more_results() && $stmt->next_result());
存储过程代码:
CREATE DEFINER=`root`@`localhost` PROCEDURE `test`()
BEGIN
declare test_var varchar(100) default "ciao";
declare bNoMoreRows bool default false;
declare test_cursor cursor for
select id from tmp_folder;
declare continue handler for not found set bNoMoreRows := true;
create temporary table tmp_folder select "test" as id;
open test_cursor;
fetch test_cursor into test_var;
close test_cursor;
select test_var;
drop temporary table if exists tmp_folder;
END
打开的错误: 阿帕奇:https://bz.apache.org/bugzilla/show_bug.cgi?id=58136
显然有一点推卸责任 - 即 scaricabarile - 在 PHP 人和 MySQLi 人之间。
似乎正在发生的事情是 MySQLi 以 "improper" 方式做出反应,return 一个意外值(我敢打赌它是 NULL)到 PHP, 适当的核心转储。这种行为 is documented 和来自 PHPland 的判决是:"Not a [PHP] bug"。在他们这边,MySQLi 人员坚持认为 PHP 没有正确检查 returned 结果。 'improper' 结果取决于 你的 查询。
所以我要冒险,假设你的问题与 "communication difficulties" 相同,那么问题就变成了:"Your query forces MySQLi to drop/reconnect"。为什么?显然(某些)存储过程 require mysqli_multi_query
in order to behave properly.
并且mysqli_multi_query
与mysqli_prepare
不兼容。
所以我建议在不准备查询的情况下尝试,并 运行 使用 mysqli_multi_query
。
$conn->query("SET @res = ''");
$conn->multi_query("CALL retrieve_matches(5,3, 16, 2, false, @res)");
do {
if ($result = $conn->store_result()) {
while ($row = $result->fetch_row()) {
var_dump($row);
}
$result->free();
}
} while ($conn->more_results() && $conn->next_result());
使用此代码,您的测试用例如预期的那样得到了我,
array(1) {
[0] =>
string(4) "test"
}
尝试更改您的服务器堆栈。在不同的环境中升级 WAMP 或 运行 您的代码。这个问题表明 WAMP 中存在一个错误,会导致 Apache 抛出 255 错误
php -> mysql_connect -> child process exited with status 255 -- Restarting
另一个在这里咬灰尘。刚刚加入以提供解决方法。
带光标的 SP 仅在使用 prepare()
时崩溃。
如果您将其更改为 multi_query()
调用,它将起作用。一个简单的可重现 POC
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = mysqli_connect('127.0.0.1','user','pass','db');
$mysqli->query("DROP PROCEDURE IF EXISTS mysqli_crash");
$mysqli->query("CREATE PROCEDURE mysqli_crash ()
BEGIN
DECLARE col TEXT;
DECLARE res CURSOR FOR SELECT 1 from dual;
OPEN res;
FETCH res INTO col;
CLOSE res;
SELECT 1 from dual;
END;");
echo "--- multi_query ---\n";
$mysqli->multi_query("call mysqli_crash()");
$result = $mysqli->store_result();
echo json_encode($result->fetch_all(MYSQLI_ASSOC)),"\n";
$mysqli->next_result();
echo "--- prepare ---\n";
$stmt = $mysqli->prepare("call mysqli_crash()");
$stmt->execute();
$result = $stmt->get_result();
echo json_encode($result->fetch_all(MYSQLI_ASSOC)),"\n";
$stmt->next_result();
是的,prepare 是首选,但如果您的存储过程使用游标,则别无选择。