Mysql has gone awayphp
造一个表java
-- ----------------------------
-- 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;
而后造一个程序mysql
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也要设置,不然不生效sql
执行的时候就会抛异常:数据库
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:\phpWorks\framework\db\CDbCommand.php:362
sleep有效果了。并发
大部分状况就是超时致使的,尤为是脚本执行时间太长app
若是运维不一样意 改变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
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;
}
最近发现,公司所用的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_project\framework\db\CDbCommand.php at line 336
#0 D:\work_project\framework\db\CDbCommand.php(336): PDOStatement->execute()
#1 D:\work_project\task\protected\commands\DbTestCommand.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_project\framework\db\CDbCommand.php:363
Stack trace:
#0 D:\work_project\task\protected\commands\DbTestCommand.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);
}