当前位置:首页 > PHP教程 > php高级应用 > 列表

Yii多数据库主从读写分离实例介绍

发布:smiling 来源: PHP粉丝网  添加日期:2014-09-10 12:32:22 浏览: 评论:0 

Yii框架数据库多数据库、主从、读写分离 实现,功能描述:

1.实现主从数据库读写分离 主库:写 从库(可多个):读

2.主数据库无法连接时 可设置从数据库是否 可写

3.所有从数据库无法连接时 可设置主数据库是否 可读

4.如果从数据库连接失败 可设置N秒内不再连接

利用yii扩展实现,代码如下:

  1. <?php
  2. /** 
  3.  * 主数据库 写 从数据库(可多个)读 
  4.  * 实现主从数据库 读写分离 主服务器无法连接 从服务器可切换写功能 
  5.  * 从务器无法连接 主服务器可切换读功 
  6.  * by lmt 
  7.  * */ 
  8. class DbConnectionMan extends CDbConnection { 
  9.  
  10.     public $timeout = 10; //连接超时时间 
  11.     public $markDeadSeconds = 600; //如果从数据库连接失败 600秒内不再连接  
  12.     //用 cache 作为缓存全局标记 
  13.     public $cacheID = 'cache'
  14.  
  15.     /** 
  16.      * @var array $slaves.Slave database connection(Read) config array. 
  17.      * 配置符合 CDbConnection. 
  18.      * @example 
  19.      * 'components'=>array( 
  20.      *   'db'=>array( 
  21.      *    'connectionString'=>'mysql://<master>', 
  22.      *    'slaves'=>array( 
  23.      *     array('connectionString'=>'mysql://<slave01>'), 
  24.      *     array('connectionString'=>'mysql://<slave02>'), 
  25.      *    ) 
  26.      *   ) 
  27.      * ) 
  28.      * */ 
  29.     public $slaves = array(); 
  30.  
  31.     /** 
  32.      *  
  33.      * 从数据库状态 false 则只用主数据库 
  34.      * @var bool $enableSlave 
  35.      * */ 
  36.     public $enableSlave = true; 
  37.  
  38.     /** 
  39.      * @var slavesWrite 紧急情况主数据库无法连接 切换从服务器(读写). 
  40.      */ 
  41.     public $slavesWrite = false; 
  42.  
  43.     /** 
  44.      * @var masterRead 紧急情况从主数据库无法连接 切换从住服务器(读写). 
  45.      */ 
  46.     public $masterRead = false; 
  47.  
  48.     /** 
  49.      * @var _slave 
  50.      */ 
  51.     private $_slave
  52.  
  53.     /** 
  54.      * @var _disableWrite 从服务器(只读). 
  55.      */ 
  56.     private $_disableWrite = true; 
  57.  
  58.     /** 
  59.      * 
  60.      * 重写 createCommand 方法,1.开启从库 2.存在从库 3.当前不处于一个事务中 4.从库读数据 
  61.      * @param string $sql 
  62.      * @return CDbCommand 
  63.      * */ 
  64.     public function createCommand($sql = null) { 
  65.         if ($this->enableSlave && !emptyempty($this->slaves) && is_string($sql) && !$this->getCurrentTransaction() && self::isReadOperation($sql) && ($slave = $this->getSlave()) 
  66.         ) { 
  67.             return $slave->createCommand($sql); 
  68.         } else { 
  69.             if (!$this->masterRead) { 
  70.                 if ($this->_disableWrite && !self::isReadOperation($sql)) { 
  71.  
  72.                     throw new CDbException("Master db server is not available now!Disallow write operation on slave server!"); 
  73.                 } 
  74.             } 
  75.             return parent::createCommand($sql); 
  76.         } 
  77.     } 
  78.  
  79.     /** 
  80.      * 获得从服务器连接资源 
  81.      * @return CDbConnection 
  82.      * */ 
  83.     public function getSlave() { 
  84.  
  85.         if (!isset($this->_slave)) { 
  86.  
  87.             shuffle($this->slaves); 
  88.             foreach ($this->slaves as $slaveConfig) { 
  89.  
  90.                 if ($this->_isDeadServer($slaveConfig['connectionString'])) { 
  91.                     continue
  92.                 } 
  93.                 if (!isset($slaveConfig['class'])) 
  94.                     $slaveConfig['class'] = 'CDbConnection'
  95.  
  96.                 $slaveConfig['autoConnect'] = false; 
  97.                 try { 
  98.                     if ($slave = Yii::createComponent($slaveConfig)) { 
  99.                         Yii::app()->setComponent('dbslave'$slave); 
  100.                         $slave->setAttribute(PDO::ATTR_TIMEOUT, $this->timeout); 
  101.                         $slave->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true); 
  102.                         $slave->setActive(true); 
  103.                         $this->_slave = $slave
  104.                         break
  105.                     } 
  106.                 } catch (Exception $e) { 
  107.                     $this->_markDeadServer($slaveConfig['connectionString']); 
  108.                     Yii::log("Slave database connection failed!ntConnection string:{$slaveConfig['connectionString']}"'warning'); 
  109.  
  110.                     continue
  111.                 } 
  112.             } 
  113.  
  114.             if (!isset($this->_slave)) { 
  115.                 $this->_slave = null; 
  116.                 $this->enableSlave = false; 
  117.             } 
  118.         } 
  119.         return $this->_slave; 
  120.     } 
  121.  
  122.     public function setActive($value) { 
  123.         if ($value != $this->getActive()) { 
  124.             if ($value) { 
  125.                 try { 
  126.                     if ($this->_isDeadServer($this->connectionString)) { 
  127.                         throw new CDbException('Master db server is already dead!'); 
  128.                     } 
  129.                     //PDO::ATTR_TIMEOUT must set before pdo instance create 
  130.                     $this->setAttribute(PDO::ATTR_TIMEOUT, $this->timeout); 
  131.                     $this->open(); 
  132.                 } catch (Exception $e) { 
  133.                     $this->_markDeadServer($this->connectionString); 
  134.                     $slave = $this->getSlave(); 
  135.                     Yii::log($e->getMessage(), CLogger::LEVEL_ERROR, 'exception.CDbException'); 
  136.                     if ($slave) { 
  137.                         $this->connectionString = $slave->connectionString; 
  138.                         $this->username = $slave->username; 
  139.                         $this->password = $slave->password; 
  140.                         if ($this->slavesWrite) { 
  141.                             $this->_disableWrite = false; 
  142.                         } 
  143.                         $this->open(); 
  144.                     } else { //Slave also unavailable 
  145.                         if ($this->masterRead) { 
  146.                             $this->connectionString = $this->connectionString; 
  147.                             $this->username = $this->username; 
  148.                             $this->password = $this->password; 
  149.                             $this->open(); 
  150.                         } else { 
  151.                             throw new CDbException(Yii::t('yii''CDbConnection failed to open the DB connection.'), (int) $e->getCode(), $e->errorInfo); 
  152.                         } 
  153.                     } 
  154.                 } 
  155.             } else { 
  156.                 $this->close(); 
  157.             } 
  158.         } 
  159.     } 
  160.  
  161.     /** 
  162.      * 检测读操作 sql 语句 
  163.      *  
  164.      * 关键字: SELECT,DECRIBE,SHOW ... 
  165.      * 写操作:UPDATE,INSERT,DELETE ... 
  166.      * */ 
  167.     public static function isReadOperation($sql) { 
  168.         $sql = substr(ltrim($sql), 0, 10); 
  169.         $sql = str_ireplace(array('SELECT''SHOW''DESCRIBE''PRAGMA'), '^O^'$sql); //^O^,magic smile 
  170.         return strpos($sql'^O^') === 0; 
  171.     } 
  172.  
  173.     /** 
  174.      * 检测从服务器是否被标记 失败. 
  175.      */ 
  176.     private function _isDeadServer($c) { 
  177.         $cache = Yii::app()->{$this->cacheID}; 
  178.         if ($cache && $cache->get('DeadServer::' . $c) == 1) { 
  179.             return true; 
  180.         } 
  181.         return false; 
  182.     } 
  183.  
  184.     /** 
  185.      * 标记失败的slaves. 
  186.      */ 
  187.     private function _markDeadServer($c) { 
  188.     //开源代码phpfensi.com 
  189.         $cache = Yii::app()->{$this->cacheID}; 
  190.         if ($cache) { 
  191.             $cache->set('DeadServer::' . $c, 1, $this->markDeadSeconds); 
  192.         } 
  193.     } 
  194.  

main.php配置:components 数组中,代码如下:

  1. 'db'=>array
  2.         'class'=>'application.extensions.DbConnectionMan',//扩展路径 
  3.         'connectionString' => 'mysql:host=192.168.1.128;dbname=db_xcpt',//主数据库 写 
  4.         'emulatePrepare' => true, 
  5.         'username' => 'root'
  6.         'password' => 'root'
  7.         'charset' => 'utf8'
  8.         'tablePrefix' => 'xcpt_'//表前缀 
  9.         'enableSlave'=>true,//从数据库启用 
  10.    'urgencyWrite'=>true,//紧急情况 主数据库无法连接 启用从数据库 写功能 
  11.     'masterRead'=>true,//紧急情况 从数据库无法连接 启用主数据库 读功能 
  12.         'slaves'=>array(//从数据库 
  13.             array(   //slave1 
  14.                 'connectionString'=>'mysql:host=localhost;dbname=db_xcpt'
  15.                 'emulatePrepare' => true, 
  16.                 'username'=>'root'
  17.                 'password'=>'root'
  18.                 'charset' => 'utf8'
  19.                 'tablePrefix' => 'xcpt_'//表前缀 
  20.             ), 
  21.    array(   //slave2 
  22.                 'connectionString'=>'mysql:host=localhost;dbname=db_xcpt'
  23.                 'emulatePrepare' => true, 
  24.                 'username'=>'root'
  25.                 'password'=>'root'
  26.                 'charset' => 'utf8'
  27.                 'tablePrefix' => 'xcpt_'//表前缀 
  28.             ), 
  29.  
  30.         ), 
  31.     ),

Tags: Yii多数据库 Yii主从读写

分享到: