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_querymysqli_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 在执行存储过程时崩溃的主要内容,如果未能解决你的问题,请参考以下文章

从 php 执行时 SQL Server 存储过程错误

Apache DbUtils:处理从存储过程返回的多个结果集

在 PHP 中插入和从存储过程中选择

[转]MYSQL 创建存储过程

mysql数据库存储过程

mysql数据库存储过程