Yii实现多数据库主从读写分离的方法
2023-06-13 09:15:38 时间
本文实例讲述了Yii实现多数据库主从读写分离的方法。分享给大家供大家参考。具体分析如下:
Yii框架数据库多数据库、主从、读写分离实现,功能描述:
1.实现主从数据库读写分离主库:写从库(可多个):读
2.主数据库无法连接时可设置从数据库是否可写
3.所有从数据库无法连接时可设置主数据库是否可读
4.如果从数据库连接失败可设置N秒内不再连接
利用yii扩展实现,代码如下:
/**
*主数据库写从数据库(可多个)读
*实现主从数据库读写分离主服务器无法连接从服务器可切换写功能
*从务器无法连接主服务器可切换读功
*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数组中,代码如下:
"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程序设计有所帮助。
相关文章
- tp5(thinkPHP5)框架实现多数据库查询的方法
- MongoDB数据库两阶段提交实现事务的方法详解
- Oracle数据库安装指南(oracle安装方法)
- MySQL数据库完整备份:全库导出的好处及实现方法(Mysql全库导出数据)
- MySQL连接远程数据库的步骤与方法(mysql怎么连接远程数据库)
- C语言操作Oracle数据库的实现方法(c读取oracle数据库)
- 解决 Oracle 数据库存储单引号的方法(oracle存储单引号)
- 使用JNI连接Java和Oracle数据库的方法(jnioracle)
- 深入了解Oracle语句跟踪方法,提高数据库调试效率!(oracle语句跟踪)
- Oracle数据库中的自增长列实现方法(oracle自增长列)
- 实现MySQL数据库恢复的正确方法(mysql数据库恢复)
- 通过ODBC实现导入MSSQL数据库的方法(导入mssql的方法)
- PHP访问MSSQL数据库的实现方法(php 读取 mssql)
- Oracle数据库中实现追加数据的方法(oracle追加数据)
- MSSQL查询数据库表名称的方法(mssql 查数据库表名)
- 应用解决Oracle数据库实际停用的巧妙方法(oracle关闭实际)
- Oracle数据库中先分组再排序的实现方法(oracle先分组再排序)
- MySQL数据库两表主外键关联的完整实现方法(mysql两表主外键关联)
- 轻松实现ASA数据库转MySQL的方法(asa数据库转mysql)
- mysql数据库导出xml的实现方法
- php更新数据库中断的解决方法
- asp.net中将数据库绑定到DataList控件的实现方法与实例代码