PHP Apache 在执行存储过程时崩溃
Posted
技术标签:
【中文标题】PHP Apache 在执行存储过程时崩溃【英文标题】:PHP Apache crashes while executing a STORED PROCEDURE 【发布时间】:2012-03-21 20:17:44 【问题描述】:问题
当我执行以下代码时(我正在调用一个带有 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:父进程:子进程 9628 以状态 255 退出 -- 正在重新启动。
我尝试了什么
-
此代码运行良好,并且可以正确返回结果:
$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 工作台尝试过,两个调用都运行良好。
我在 WIN7 Enterprise 64b 上使用 WAMP 64b,我尝试使用 WAMP 32b 但遇到了同样的问题。
我检查了windows事件,发现httpd.exe崩溃是由php5ts.dll引起的
如果你在谷歌上搜索“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”。此过程正在执行选择/插入,我检查并正确插入。 但是在“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;
一切正常!!所以我想我现在找到了问题:我该如何解决?
【问题讨论】:
ERR_CONNECTION_RESET 表示这是浏览器问题,但不是编码问题 $stmt->get_result()->fetch_assoc();假定查询返回至少一行。是吗? 如果第一个查询有效,怎么可能是浏览器问题? 是 $stmt->get_result()->fetch_assoc();返回至少一行,实际上第一个查询是返回第一行的数据 ERR_CONNECTION_RESET 是 chrome 问题,与您的代码无关,您可以看到有关该代码的各种答案。我会在那个 stmt->execute 上设置一个真假标志,并确保你正在返回一些东西。 【参考方案1】:另一个人在这里咬着灰尘。刚刚加入以提供解决方法。
只有在使用 prepare()
时,带有光标的 SP 才会崩溃。
如果你把它改成multi_query()
call 就可以了。一个简单的可重现 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 是首选,但如果您的存储过程使用游标,则别无选择。
【讨论】:
【参考方案2】:尝试更改您的服务器堆栈。升级 WAMP 或在不同的环境中运行您的代码。这个问题表明 WAMP 中有一个错误会导致 Apache 抛出 255 错误
php -> mysql_connect -> child process exited with status 255 -- Restarting
【讨论】:
【参考方案3】:显然,PHP 人员和 MySQLi 人员之间存在一些推卸责任 - 即 scaricabarile。
似乎正在发生的事情是 MySQLi 以“不正确”的方式做出反应,并向 PHP 返回了一个意外的值(我敢打赌它是 NULL),这会适时进行核心转储。这种行为is documented 和 PHPland 的结论是:“不是 [PHP] 错误”。在他们这边,MySQLi 人坚持认为是 PHP 没有正确检查返回的结果。无论如何,“不正确”的结果取决于您的查询。
所以我冒昧地假设您的问题与“通信困难”相同,因此问题变成:“您的查询强制 MySQLi 断开/重新连接”。为什么呢?显然(一些)存储过程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"
【讨论】:
谢谢,我可以向您确认(正如您可以从我的问题“我尝试了什么,第 1 点”中看到的那样),如果我不准备查询,一切都会正常工作。太糟糕了,他们没有就解决方案达成一致......【参考方案4】:我知道如何重现它,我相信这是一个 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
php:https://bugs.php.net/bug.php?id=70073
【讨论】:
$stmt->get_result() 让我“调用未定义的方法 mysqli_stmt::get_result()” - ***.com/questions/8321096/…【参考方案5】:如果每次运行短脚本时 php5ts.dll 或 apache 的任何其他部分都以可重现的方式崩溃,那么您很可能遇到了 php 中的错误。尤其是如果您使用从 php.net 下载的最新 php 版本运行最新的 apache,您很有可能获得 PHP 团队的支持。所以通读https://bugs.php.net/how-to-report.php,看看你是否可以在没有apache(在cli上使用php.exe的单独脚本中)和generate a backtrace的情况下运行脚本时重现崩溃,然后你可以将其作为错误提交。
有时可以解决此类错误,但很少从脚本的上下文中修复。 PHP 永远不应该让网络服务器崩溃,如果它可以重现,则应该提交一个错误以便修复它
【讨论】:
也许吧,但是如果你在谷歌上搜索“httpd.exe php5ts.dll”,你会发现很多人遇到这个问题。不知道我是否应该打开一个错误... 如果您有一个可重现的测试用例,那么您有很好的机会,您是否能够生成正确的回溯?【参考方案6】:我认为您混淆了$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";
【讨论】:
我的代码没有问题。它是从 PHP 文档中复制粘贴的。此外,它适用于传递给存储过程的不同值... @StefanoGiacone 部分并没有混淆。这是 mysqli 部分的混淆。没有游标的 SP 同时适用于准备和多查询,但使用游标的 SP 可用于 multi_query,但会在准备时崩溃。仍然。 所以你可以将你的答案定位为一种解决方法以上是关于PHP Apache 在执行存储过程时崩溃的主要内容,如果未能解决你的问题,请参考以下文章