PHP CI框架遇到MySQL server has gone away 问题的解决方法

报错信息

Query error: MySQL server has gone away - Invalid query:xxxx

环境

  • php 5.6 (CI框架)
  • mysql 5.6

场景

一个定时任务(crontab 脚本),结构体方法先加载了model类,即进行了连接。插入数据库之前有一段很耗时的代码在执行,当执行插入语句时,报错。

代码剖析(伪代码)

load->model('Test_model','test_model');
    }


    public function index(){
        //这里要执行很长很长时间的处理,时间可能达到了5分钟还没有处理完
           longTimeFunc();
        // ... ...
        //插入数据
        $res = $this->ocpx_model->insert_ignore($insert_data);     
    }
}

分析原因

查看数据库connect_timeout设置

show variables like '%timeout%';
+----------------------------+----------+
| Variable_name              | Value    |
+----------------------------+----------+
| connect_timeout            | 10       |
| delayed_insert_timeout     | 300      |
| innodb_lock_wait_timeout   | 20       |
| innodb_rollback_on_timeout | OFF      |
| interactive_timeout        | 30       |
| lock_wait_timeout          | 31536000 |
| net_read_timeout           | 30       |
| net_write_timeout          | 60       |
| slave_net_timeout          | 3600     |
| wait_timeout               | 30       |
+----------------------------+----------+

很显然,connect_timeout设置为10s,反观我们的代码,初始化时连接了数据库,连接池存储了当前连接,而代码执行到index方法调用longTimeFunc()方法耗时很长,这时候mysql主动断开了连接。然而我们还是继续使用了连接池里旧连接,因此报错MySQL server has gone away

解决方法

方法一

修改connect_timeout值,可以修改长一点,但是治标不治本。

方法二

关闭当前连接,再进行初始化

$this->master->close();
$this->master->initialize();

以下是CI框架DB_driver.php文件关于initialize方法的封装:

/**
     * Initialize Database Settings
     *
     * @return  bool
     */
    public function initialize()
    {
        /* If an established connection is available, then there's
         * no need to connect and select the database.
         *
         * Depending on the database driver, conn_id can be either
         * boolean TRUE, a resource or an object.
         */
        if ($this->conn_id)
        {
            return TRUE;
        }

        // ----------------------------------------------------------------

        // Connect to the database and set the connection ID
        $this->conn_id = $this->db_connect($this->pconnect);

        // No connection resource? Check if there is a failover else throw an error
        if ( ! $this->conn_id)
        {
            // Check if there is a failover set
            if ( ! empty($this->failover) && is_array($this->failover))
            {
                // Go over all the failovers
                foreach ($this->failover as $failover)
                {
                    // Replace the current settings with those of the failover
                    foreach ($failover as $key => $val)
                    {
                        $this->$key = $val;
                    }

                    // Try to connect
                    $this->conn_id = $this->db_connect($this->pconnect);

                    // If a connection is made break the foreach loop
                    if ($this->conn_id)
                    {
                        break;
                    }
                }
            }

            // We still don't have a connection?
            if ( ! $this->conn_id)
            {
                log_message('error', 'Unable to connect to the database');

                if ($this->db_debug)
                {
                    $this->display_error('db_unable_to_connect');
                }

                return FALSE;
            }
        }

        // Now we set the character set and that's all
        return $this->db_set_charset($this->char_set);
    }

方法三

以下是CI官方手册内容:“当你在处理一些重量级的 PHP 操作时(例如处理图像),若超过了数据库的超时值,你应该考虑在执行后续查询前先调用 reconnect() 方法向数据库发送 ping 命令,这样可以优雅的保持连接有效或重新建立起连接。”
参考https://codeigniter.org.cn/user_guide/database/connecting.html
【亲测,并未生效】
其实在实践过程中,我们会发现,这个reconnect函数并没有生效,调用数据库语句之前,如果超过连接时间,依然会报mysql has gone away。
【有坑】
进一步探索才发现,有坑,参考https://www.cnblogs.com/joeblackzqq/p/5614948.html。
大概意思就是实际上reconnect方法还依赖于MYSQL_OPT_RECONNECT这个配置,而这个配置默认(自mysql5.0.3开始)是关闭的!
【解决办法】
解决办法是调用 mysql_options ,将MYSQL_OPT_RECONNECT设置为1:
char value = 1;
mysql_options(mysql, MYSQL_OPT_RECONNECT, &value);

相关知识

mysql 几个超时参数(timeout)解释

1.connect_timeout

mysql客户端在尝试与mysql服务器建立连接时,mysql服务器返回错误握手协议前等待客户端数据包的最大时限。默认10秒。

2.interactive_timeout / wait_timeout

mysql关闭交互/非交互连接前等待的最大时限。默认28800秒。

3.lock_wait_timeout

sql语句请求元数据锁的最长等待时间,默认为一年。此锁超时对于隐式访问Mysql库中系统表的sql语句无效,但是对于使用select,update语句直接访问mysql库中标的sql语句有效

4.net_read_timeout / net_write_timeout

mysql服务器端等待从客户端读取数据 / 向客户端写入数据的最大时限,默认30秒。

5.slave_net_timeout

mysql从复制连结等待读取数据的最大时限,默认3600秒。

你可能感兴趣的:(PHP CI框架遇到MySQL server has gone away 问题的解决方法)