Yii实现多数据库主从读写分离的方法
本文实例讲述了Yii实现多数据库主从读写分离的方法。分享给大家供大家参考。具体分析如下:
Yii框架数据库多数据库、主从、读写分离实现,功能描述:
1.实现主从数据库读写分离主库:写从库(可多个):读
2.主数据库无法连接时可设置从数据库是否可写
3.所有从数据库无法连接时可设置主数据库是否可读
4.如果从数据库连接失败可设置N秒内不再连接
利用yii扩展实现,代码如下:
<?php /** *主数据库写从数据库(可多个)读 *实现主从数据库读写分离主服务器无法连接从服务器可切换写功能 *从务器无法连接主服务器可切换读功 *bylmt **/ classDbConnectionManextendsCDbConnection{ public$timeout=10;//连接超时时间 public$markDeadSeconds=600;//如果从数据库连接失败600秒内不再连接 //用cache作为缓存全局标记 public$cacheID='cache'; /** *@vararray$slaves.Slavedatabaseconnection(Read)configarray. *配置符合CDbConnection. *@example *'components'=>array( * 'db'=>array( * 'connectionString'=>'mysql://<master>', * 'slaves'=>array( * array('connectionString'=>'mysql://<slave01>'), * array('connectionString'=>'mysql://<slave02>'), * ) * ) *) **/ public$slaves=array(); /** * *从数据库状态false则只用主数据库 *@varbool$enableSlave **/ public$enableSlave=true; /** *@varslavesWrite紧急情况主数据库无法连接切换从服务器(读写). */ public$slavesWrite=false; /** *@varmasterRead紧急情况从主数据库无法连接切换从住服务器(读写). */ public$masterRead=false; /** *@var_slave */ private$_slave; /** *@var_disableWrite从服务器(只读). */ private$_disableWrite=true; /** * *重写createCommand方法,1.开启从库2.存在从库3.当前不处于一个事务中4.从库读数据 *@paramstring$sql *@returnCDbCommand **/ publicfunctioncreateCommand($sql=null){ if($this->enableSlave&&!emptyempty($this->slaves)&&is_string($sql)&&!$this->getCurrentTransaction()&&self::isReadOperation($sql)&&($slave=$this->getSlave()) ){ return$slave->createCommand($sql); }else{ if(!$this->masterRead){ if($this->_disableWrite&&!self::isReadOperation($sql)){ thrownewCDbException("Masterdbserverisnotavailablenow!Disallowwriteoperationonslaveserver!"); } } returnparent::createCommand($sql); } } /** *获得从服务器连接资源 *@returnCDbConnection **/ publicfunctiongetSlave(){ if(!isset($this->_slave)){ shuffle($this->slaves); foreach($this->slavesas$slaveConfig){ if($this->_isDeadServer($slaveConfig['connectionString'])){ continue; } if(!isset($slaveConfig['class'])) $slaveConfig['class']='CDbConnection'; $slaveConfig['autoConnect']=false; try{ if($slave=Yii::createComponent($slaveConfig)){ Yii::app()->setComponent('dbslave',$slave); $slave->setAttribute(PDO::ATTR_TIMEOUT,$this->timeout); $slave->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY,true); $slave->setActive(true); $this->_slave=$slave; break; } }catch(Exception$e){ $this->_markDeadServer($slaveConfig['connectionString']); Yii::log("Slavedatabaseconnectionfailed!ntConnectionstring:{$slaveConfig['connectionString']}",'warning'); continue; } } if(!isset($this->_slave)){ $this->_slave=null; $this->enableSlave=false; } } return$this->_slave; } publicfunctionsetActive($value){ if($value!=$this->getActive()){ if($value){ try{ if($this->_isDeadServer($this->connectionString)){ thrownewCDbException('Masterdbserverisalreadydead!'); } //PDO::ATTR_TIMEOUTmustsetbeforepdoinstancecreate $this->setAttribute(PDO::ATTR_TIMEOUT,$this->timeout); $this->open(); }catch(Exception$e){ $this->_markDeadServer($this->connectionString); $slave=$this->getSlave(); Yii::log($e->getMessage(),CLogger::LEVEL_ERROR,'exception.CDbException'); if($slave){ $this->connectionString=$slave->connectionString; $this->username=$slave->username; $this->password=$slave->password; if($this->slavesWrite){ $this->_disableWrite=false; } $this->open(); }else{//Slavealsounavailable if($this->masterRead){ $this->connectionString=$this->connectionString; $this->username=$this->username; $this->password=$this->password; $this->open(); }else{ thrownewCDbException(Yii::t('yii','CDbConnectionfailedtoopentheDBconnection.'),(int)$e->getCode(),$e->errorInfo); } } } }else{ $this->close(); } } } /** *检测读操作sql语句 * *关键字:SELECT,DECRIBE,SHOW... *写操作:UPDATE,INSERT,DELETE... **/ publicstaticfunctionisReadOperation($sql){ $sql=substr(ltrim($sql),0,10); $sql=str_ireplace(array('SELECT','SHOW','DESCRIBE','PRAGMA'),'^O^',$sql);//^O^,magicsmile returnstrpos($sql,'^O^')===0; } /** *检测从服务器是否被标记失败. */ privatefunction_isDeadServer($c){ $cache=Yii::app()->{$this->cacheID}; if($cache&&$cache->get('DeadServer::'.$c)==1){ returntrue; } returnfalse; } /** *标记失败的slaves. */ privatefunction_markDeadServer($c){ $cache=Yii::app()->{$this->cacheID}; if($cache){ $cache->set('DeadServer::'.$c,1,$this->markDeadSeconds); } } }
main.php配置:components数组中,代码如下:
'db'=>array( 'class'=>'application.extensions.DbConnectionMan',//扩展路径 'connectionString'=>'mysql:host=192.168.1.128;dbname=db_xcpt',//主数据库写 'emulatePrepare'=>true, 'username'=>'root', 'password'=>'root', 'charset'=>'utf8', 'tablePrefix'=>'xcpt_',//表前缀 'enableSlave'=>true,//从数据库启用 'urgencyWrite'=>true,//紧急情况主数据库无法连接启用从数据库写功能 'masterRead'=>true,//紧急情况从数据库无法连接启用主数据库读功能 'slaves'=>array(//从数据库 array( //slave1 'connectionString'=>'mysql:host=localhost;dbname=db_xcpt', 'emulatePrepare'=>true, 'username'=>'root', 'password'=>'root', 'charset'=>'utf8', 'tablePrefix'=>'xcpt_',//表前缀 ), array( //slave2 'connectionString'=>'mysql:host=localhost;dbname=db_xcpt', 'emulatePrepare'=>true, 'username'=>'root', 'password'=>'root', 'charset'=>'utf8', 'tablePrefix'=>'xcpt_',//表前缀 ), ), ),
希望本文所述对大家基于Yii框架的php程序设计有所帮助。