【问题标题】:Executing mysqli_stmt->execute() causes "MySQL server has gone away" error执行 mysqli_stmt->execute() 导致“MySQL 服务器已消失”错误
【发布时间】:2018-08-27 14:06:39
【问题描述】:

好的。这是一个奇怪的问题。首先,这是错误:

致命错误:未捕获的 mysqli_sql_exception:MySQL 服务器已在 database.php:84 中消失 堆栈跟踪: #0 database.php(84): mysqli_stmt->execute()

根据其他 StackOverflow 文章,例如 thisthis,那个错误,MySQL server has gone away 意味着:

  • mysqli 对象在没有被关闭的情况下被重用(不,绝对不是)
  • MySQL 服务器端的连接超时变量太小
  • 最大数据包大小变量太小

但是,我已将超时和最大数据包大小变量设置为它们的最大值,并且查询只是从一个空表中进行选择。没有理由为什么这两个应该是一个问题。我还通过 Python 进行了验证——可以连接到服务器并且应该能够执行查询。它甚至可以在 phpMyAdmin 中正常工作。

mysqli_stmt::prepare 的 PHP 文档中,关于错误的说明如下:

  • 我在Linux上使用mysqlnd,当语句长于max_allowed_packet时不应该给出这个错误
  • 我已经提到我如何将max_allowed_packet 设置为变量的最大值。

如果您希望我提供更多信息,例如我的 SQL 服务器或 PHP 配置,请告诉我您需要什么。

我读过的一篇文章说使用mysqli->ping() 来检查连接的情况,在我调用mysqli_stmt->execute() 之前它似乎没问题。

我很确定这是我的实现问题——我尝试重新安装 Web 服务器和 MySQL 服务器,切换 PHP 版本,甚至尝试切换主机。但尽管我尝试解决此问题,但我仍然收到错误。

代码如下:

<?php
    ini_set('mysql.connect_timeout', 3000);
    ini_set('default_socket_timeout', 3000);

    /* define the database class */
    class Database {
        public $host = 'localhost';
        public $name = '';
        public $user = '';
        public $pass = '';

        private $mysqli;

        /* constructor function, inits the database connection */
        function __construct($chost, $cname, $cuser, $cpass) {
            $this->host = $chost;
            $this->name = $cname;
            $this->user = $cuser;
            $this->pass = $cpass;

            mysqli_report(MYSQLI_REPORT_ALL);
            $this->mysqli = new mysqli($this->getHost(), $this->getUsername(), $this->getPassword(), $this->getName());
        }

        /* closes the connection to the database */
        function close() {
            return $this->getMySQLi()->close();
        }

        /* returns a query object for the given parameters */
        function query($query, $type='', ...$params) {
            $statement = $this->getMySQLi()->prepare($query);


            if(strlen($type) != 0) {
                // bind parameters to query
                $statement->bind_param($type, ...$params);
            }

            /*
             * stackoverflow readers: this the debug code
             * I mentioned to check the connection
             *
             */
            if ($this->getMySQLi()->ping()) {
                printf ("Our connection is ok!\n");
            } else {
                printf ("Error: %s\n", $this->getMySQLi()->error);
            }

            return new Query($statement);
        }

        /* getter functions */

        function getMySQLi() {
            return $this->mysqli;
        }

        function getHost() {
            return $this->host;
        }

        function getName() {
            return $this->name;
        }

        function getUsername() {
            return $this->user;
        }

        function getPassword() {
            return $this->pass;
        }
    }

    /* define the query class */
    class Query {
        private $statement;
        private $result;

        /* constructor, sets variables and stuff */
        function __construct($statement) {
            $this->statement = $statement;
        }

        /* executes the statement */
        function execute() {
            $status = $this->getStatement()->execute();
            $this->result = $this->getStatement()->get_result();

            return $status;
        }

        /* closes the statement */
        function close() {
            return $this->getStatement()->close();
        }

        /* returns the number of results */
        function countRows() {
            return $this->getResult()->num_rows;
        }

        /* getter functions */

        /* returns the statement object */
        function getStatement() {
            return $this->statement;
        }

        /* returns the result object */
        function getResult() {
            return $this->result;
        }

        function getRow() {
            return $this->getResult()->fetch_assoc();
        }

        /* returns the result in an array */
        function getRows() {
            $rows = array();
            while($row = $this->getRow()) {
                $rows[] = $row;
            }

            return $rows;
        }
    }
?>

所以。我的问题是,我的实施有问题吗?如何缓解问题?是 SQL server 的问题还是 PHP 的问题?

编辑:这是我使用 Database 类的方法(getConnection() 只是返回一个新的 Database 实例)

function getUsers() {
    $query = getConnection()->query('SELECT * FROM `users`');
    $query->execute();
    return $query->getRows();
}

【问题讨论】:

  • 您在哪里使用了导致此错误的这些类?我复制并粘贴了您的代码并尝试了它,它对我来说很好。
  • @Wreigh 我编辑了问题并在底部添加了使用此类的代码。如果这也适用于您,请问您使用的是什么 PHP 版本、网络服务器和模块?谢谢!
  • php 7.1.7,我只是使用 php 构建的服务器。这就是我使用它的方式。 $a = new Database('localhost', 'test_suites', 'root', ''); $b = $a-&gt;query("SELECT * FROM test_suites"); $b-&gt;execute();
  • 哦,对不起。我已经评论了 mysqli_report() 这为什么它有效!哈哈等等。
  • @Wreigh 没办法。我想你刚刚找到了这个错误的解决方案,我花了很多时间试图修复它。我不敢相信,但它现在正在工作。我所做的只是通过查询的构造函数传递数据库对象并将其保存在一个变量中(然后在执行之前检查是否为空),然后繁荣,它工作。万分感谢!如果您发布答案,我会将其标记为正确(一旦允许)

标签: php mysql mysqli


【解决方案1】:

我想我现在知道是什么导致了问题!我希望我是对的。

function getUsers() {
    // here you are creating a Database instance,
    // but you've left it in the air, because you just retrieved `query` from it.
    $query = getConnection()->query('SELECT * FROM `users`');
    // at this point, there are already NO Database instance,
    // because you have no reference of it.
    // thus what I've read about resources being garbage collected
    // will now apply here.
    $query->execute();
    // this will fail, indeed, Mysql has gone away!
    return $query->getRows();
}

您至少应该将连接保存到另一个变量。

$conn = getConnection();
$query = $conn->query('SELECT * FROM `user`');
$query->execute();
return $query->getRows();

但作为一种适当的解决方案,您必须在整个脚本执行过程中保持单个连接处于活动状态,并将其用于所有查询。

【讨论】:

  • 非常感谢,你不知道我有多高兴这终于奏效了!
  • 不客气。也很高兴,我能够解决一个我不太熟悉的问题 xD
  • 好的。为了回答您的问题,根据他提供的内容,他正在使用$query = getConnection()-&gt;query('SELECT * FROM user') 创建一个查询。由于getConnection() 正如他所说的那样创建了Database 的新实例,在对query 的链式调用之后,对该新实例的引用现在已经消失(因为没有任何东西捕获它)。好吧,如果我没记错的话,如果没有对 php 中的资源的引用(持久性数据库链接除外),它将被垃圾收集。当没有人抓到数据库实例时,底层的mysqli就消失了。
  • 顺便说一句,他的方法与我发布的方法不同。这就是他在我发布这个答案之前所做的事情。 我所做的只是通过查询的构造函数传递数据库对象并将其保存在一个变量中(然后在执行之前检查是否为空)
猜你喜欢
  • 2013-11-28
  • 1970-01-01
  • 2013-09-07
  • 1970-01-01
  • 2012-06-28
  • 2011-12-18
  • 2011-10-12
相关资源
最近更新 更多