zoukankan      html  css  js  c++  java
  • Yii 数据库重连告别General error: 2006 MySQL server has gone away

    General error: 2006 MySQL server has gone away

    • 错误原因
    • 制造错误
    • 解决办法
    • 最新办法

    错误原因

    Mysql has gone away

    1. MySQL 服务宕了
    2. mysql连接超时 show global variables like ‘%timeout’;
    3. mysql请求链接进程被主动kill show global status like ‘com_kill’;
    4. Your SQL statement was too large. show global variables like ‘max_allowed_packet’;

    制造错误

    造一个表

    -- ----------------------------
    -- Table structure for t_biubiu
    -- ----------------------------
    DROP TABLE IF EXISTS `t_biubiu`;
    CREATE TABLE `t_biubiu` (
      `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
      `value` varchar(255) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    然后造一个程序

    public function actionInsert(){
            Biubiu::model()->findAll();
            $result = Yii::app()->db->createCommand("show global variables like '%timeout'")->queryAll();
            self::show_vars($result);
            for($index = 1 ;$index< 10;$index++){
                $model_ = new Biubiu();
                $model_->value = "insert".$index;
                if($index == 8){
                    sleep(31);
                }
                $model_->save();
            }
            $model = new Biubiu();
            $model->value = "insert4";
            $model->save();
         }

    设置你的MYSQL的wait_timeout = 30,interactive_timeout也要设置,否则不生效

    执行的时候就会抛异常:

    exception 'CDbException' with message 'CDbCommand failed to execute the SQL statement: SQLSTATE[HY000]: General error: 2006 MySQL server has gone away. The SQL statement executed was: INSERT INTO `t_biubiu` (`value`) VALUES (:yp0). Bound with :yp0='insert8'' in E:phpWorksframeworkdbCDbCommand.php:362

    sleep有效果了。

    大部分情况就是超时导致的,尤其是脚本执行时间太长

    解决办法

    如果运维不同意 改变wait_timeout的值:

    方法做法
    1 wait_timeout改大一些,并不能一劳永逸
    2 代码中遇到超时重连
    3 检查是不是Mysql连接过多,并发太高,忘记释放连接

    解决方法推荐

    例如,读写分离的 JDbConnection。在调用save delete方法的时候执行ensureActive() 。注意 @,否则异常发生的时候PHP ERROR直接挂掉。

    <?php
    
    /**
     * JDbConnection(Database Connection Manager) class is a manager of database connections.
     *
     * for the purpose of database read/write splitting.
     * It override the createCommand method,
     * detect the sql statement to decide which connection will be used.
     * Default it use the master connection.
     *
     */
    class JDbConnection extends CDbConnection
    {
        /**
         * @var array $slaves.Slave database connection(Read) config array.
         * The array value's format is the same as CDbConnection.
         * <code>
         * 'components'=>array(
         * 'db'=>array(
         * 'class' => 'JDbConnection',
         * 'connectionString'=>'MySQL://<master>',
         * 'slaves'=>array(
         * array('connectionString'=>'mysql://<slave01>'),
         * array('connectionString'=>'mysql://<slave02>'),
         * )
         * )
         * )
         * </code>
         */
        public $slaves = array();
    
        /**
         * Whether enable the slave database connection.
         * Defaut is true.Set this property to false for the purpose of only use the master database.
         *
         * @var bool $enableSlave
         */
        public $enableSlave = true;
    
        /**
         * @var CDbConnection
         */
        private $_slave;
    
    
        /**
         * Creates a CDbCommand object for excuting sql statement.
         * It will detect the sql statement's behavior.
         * While the sql is a simple read operation.
         * It will use a slave database connection to contruct a CDbCommand object.
         * Default it use current connection(master database).
         *
         * @override
         * @param string $sql
         * @return CDbCommand
         */
        public function createCommand($query = null)
        {
            if ($this->enableSlave && !$this->getCurrentTransaction() && self::isReadOperation($query)) {
                return $this->getSlave()->createCommand($query);
            } else {
                return parent::createCommand($query);
            }
        }
    
    
        /**
         * Construct a slave connection CDbConnection for read operation.
         *
         * @return CDbConnection
         */
        public function getSlave()
        {
            if (!isset($this->_slave)) {
                foreach ($this->slaves as $slaveConfig) {
                    if (!isset($slaveConfig['class']))
                        $slaveConfig['class'] = 'CDbConnection';
                    try {
                        if ($slave = Yii::createComponent($slaveConfig)) {
                            Yii::app()->setComponent('dbslave', $slave);
                            $this->_slave = $slave;
                            break;
                        }
                    } catch (Exception $e) {
                        Yii::log('Create slave database connection failed!', 'warn');
                        continue;
                    }
                }
                if (!$this->_slave) {
                    $this->_slave = clone $this;
                    $this->_slave->enableSlave = false;
                }
            }
            return $this->_slave;
        }
    
    
        /**
         * Detect whether the sql statement is just a simple read operation.
         * Read Operation means this sql will not change any thing ang aspect of the database.
         * Such as SELECT,DECRIBE,SHOW etc.
         * On the other hand:UPDATE,INSERT,DELETE is write operation.
         *
         * @return bool
         */
        public function isReadOperation($sql)
        {
            return !!preg_match('/^s*(SELECT|SHOW|DESCRIBE|PRAGMA)/i', $sql);
        }
    
    
        /**
         * 确保数据库连接有效
         *
         * @params bool $isSlaveping主库还是从库
         * @return void
         */
        public function ensureActive($isSlave = true)
        {
                if ($this->getActive()) {
                    try {
                        @$this->getPdoInstance()->query('SELECT 1');
                    } catch (PDOException $e) {
                        $this->setActive(false);
                        $this->setActive(true);
                    }
                }
        }
    }
    

    调用示例:

     Yii::app()->db->ensureActive();

    另外一种方法

    执行SQL之前执行:

    Yii::$app->db->createCommand('SET SESSION wait_timeout = 28800;')->execute();

    最新办法

    避免每次多请求了一次数据库。只有在遇到问题时进入异常处理,重试的时候Yii会自动进行重连[不确定]。

    public static function retry($params, $call){
        for($retry = 0; $retry < 3;$retry++){
            try{
                if($params instanceof CModel){
                    return $params->$call();
                }
            }catch(CDbException $e){
                if (!(strpos($e, "error: 2006") && !strpos($e, "error: 2013"))) {
                    throw new Exception($e->getMessage(), 520);
                }
            }
        }
        return false;
    }

    2017年4月9日 更新

    最近发现,公司所用的yii framework1.1的框架,和官方下载的不太一样。

    下载最新框架,测试代码如下,我将mysql设置为wait_timeout=30, interactive_timeout=30

    class DbTestCommand extends CConsoleCommand {
    
        public function actionInsert(){
    
            $sql = "insert into adv_pro ( `name` , `int_value`) values ('insert', 100)";
            Yii::app()->localdb->createCommand($sql)->execute();
            sleep(31);
            $sql = "insert into adv_pro ( `name` , `int_value`) values ('insert', 200)";
            Yii::app()->localdb->createCommand($sql)->execute();
    
        }
    
    }

    结果就出现了:

    PHP Error[2]: PDOStatement::execute(): MySQL server has gone away
        in file D:work_projectframeworkdbCDbCommand.php at line 336
    #0 D:work_projectframeworkdbCDbCommand.php(336): PDOStatement->execute()
    #1 D:work_project	askprotectedcommandsDbTestCommand.php(19): CDbCommand->execute()
    #2 unknown(0): DbTestCommand->actionInsert()

    再加上@符号抑制错误

    @Yii::app()->localdb->createCommand($sql)->execute();

    结果异常就抛出来了:

    exception 'CDbException' with message 'CDbCommand failed to execute the SQL statement: SQLSTATE[HY000]: General error: 2006 MySQL server has gone away. The SQL statement executed was: insert into adv_pro ( `name` , `int_value`) values ('insert', 200)' in D:work_projectframeworkdbCDbCommand.php:363
    Stack trace:
    #0 D:work_project	askprotectedcommandsDbTestCommand.php(19): CDbCommand->execute()

    那么可以把框架的db/CDbCommand.php内的execute方法改一下,改成:

    if($params===array())
        @$this->_statement->execute();
    else
        @$this->_statement->execute($params);

    这样就能捕获到这个异常了。

    那么据此,可以为CDbCommand.php增加一个方法:

    public function sqlDbExecute($params=array()){
        $e = new Exception("sqlDbExecute failed three times", 10001);
        for ($count=0;$count<3;$count++){
            try {
                $result = $this->execute($params);
                return $result;
            } catch (Exception $e) {
                   Yii::log('Error in sqlDbExecute SQL: '.$this->getText(),CLogger::LEVEL_ERROR,'system.db.CDbCommand');
                $this->_connection->setActive(true);
                //If not found 2006 then throw up
                if (!strpos($e, "error: 2006")) {
                       throw new Exception($e,10001);
                }
            }
        }
        throw new Exception($e,10001);
    
    }
  • 相关阅读:
    ccf-csp201909题解
    TYUT程序设计入门第四讲练习题题解--数论入门
    Pairs Forming LCM (LightOJ
    Help Hanzo (LightOJ
    hnsdfz -- 6.19 -- day4
    hsdfz -- 6.18 -- day3
    hsdfz -- 6.17 -- day2
    hsdfz -- 6.16 -- day1
    【BZOJ2555】SubString
    洋葱第4场C和D题解……
  • 原文地址:https://www.cnblogs.com/slankka/p/9158512.html
Copyright © 2011-2022 走看看