Yii实现多数据库主从读写分离的方法


Posted in PHP onDecember 29, 2014

本文实例讲述了Yii实现多数据库主从读写分离的方法。分享给大家供大家参考。具体分析如下:

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

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

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

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

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

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

<?php

/** 

 * 主数据库 写 从数据库(可多个)读 

 * 实现主从数据库 读写分离 主服务器无法连接 从服务器可切换写功能 

 * 从务器无法连接 主服务器可切换读功 

 * by lmt 

 * */ 

class DbConnectionMan extends CDbConnection { 

    public $timeout = 10; //连接超时时间 

    public $markDeadSeconds = 600; //如果从数据库连接失败 600秒内不再连接  

    //用 cache 作为缓存全局标记 

    public $cacheID = 'cache'; 

 

    /** 

     * @var array $slaves.Slave database connection(Read) config array. 

     * 配置符合 CDbConnection. 

     * @example 

     * 'components'=>array( 

     *   'db'=>array( 

     *    'connectionString'=>'mysql://<master>', 

     *    'slaves'=>array( 

     *     array('connectionString'=>'mysql://<slave01>'), 

     *     array('connectionString'=>'mysql://<slave02>'), 

     *    ) 

     *   ) 

     * ) 

     * */ 

    public $slaves = array(); 

    /** 

     *  

     * 从数据库状态 false 则只用主数据库 

     * @var bool $enableSlave 

     * */ 

    public $enableSlave = true; 

 

    /** 

     * @var slavesWrite 紧急情况主数据库无法连接 切换从服务器(读写). 

     */ 

    public $slavesWrite = false; 

 

    /** 

     * @var masterRead 紧急情况从主数据库无法连接 切换从住服务器(读写). 

     */ 

    public $masterRead = false; 

 

    /** 

     * @var _slave 

     */ 

    private $_slave; 

 

    /** 

     * @var _disableWrite 从服务器(只读). 

     */ 

    private $_disableWrite = true; 

 

    /** 

     * 

     * 重写 createCommand 方法,1.开启从库 2.存在从库 3.当前不处于一个事务中 4.从库读数据 

     * @param string $sql 

     * @return CDbCommand 

     * */ 

    public function createCommand($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)) { 

 

                    throw new CDbException("Master db server is not available now!Disallow write operation on slave server!"); 

                } 

            } 

            return parent::createCommand($sql); 

        } 

    } 

 

    /** 

     * 获得从服务器连接资源 

     * @return CDbConnection 

     * */ 

    public function getSlave() { 

        if (!isset($this->_slave)) { 

            shuffle($this->slaves); 

            foreach ($this->slaves as $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("Slave database connection failed!ntConnection string:{$slaveConfig['connectionString']}", 'warning'); 

 

                    continue; 

                } 

            } 

 

            if (!isset($this->_slave)) { 

                $this->_slave = null; 

                $this->enableSlave = false; 

            } 

        } 

        return $this->_slave; 

    } 

 

    public function setActive($value) { 

        if ($value != $this->getActive()) { 

            if ($value) { 

                try { 

                    if ($this->_isDeadServer($this->connectionString)) { 

                        throw new CDbException('Master db server is already dead!'); 

                    } 

                    //PDO::ATTR_TIMEOUT must set before pdo instance create 

                    $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 { //Slave also unavailable 

                        if ($this->masterRead) { 

                            $this->connectionString = $this->connectionString; 

                            $this->username = $this->username; 

                            $this->password = $this->password; 

                            $this->open(); 

                        } else { 

                            throw new CDbException(Yii::t('yii', 'CDbConnection failed to open the DB connection.'), (int) $e->getCode(), $e->errorInfo); 

                        } 

                    } 

                } 

            } else { 

                $this->close(); 

            } 

        } 

    } 

 

    /** 

     * 检测读操作 sql 语句 

     *  

     * 关键字: SELECT,DECRIBE,SHOW ... 

     * 写操作:UPDATE,INSERT,DELETE ... 

     * */ 

    public static function isReadOperation($sql) { 

        $sql = substr(ltrim($sql), 0, 10); 

        $sql = str_ireplace(array('SELECT', 'SHOW', 'DESCRIBE', 'PRAGMA'), '^O^', $sql); //^O^,magic smile 

        return strpos($sql, '^O^') === 0; 

    } 

 

    /** 

     * 检测从服务器是否被标记 失败. 

     */ 

    private function _isDeadServer($c) { 

        $cache = Yii::app()->{$this->cacheID}; 

        if ($cache && $cache->get('DeadServer::' . $c) == 1) { 

            return true; 

        } 

        return false; 

    } 

 

    /** 

     * 标记失败的slaves. 

     */ 

    private function _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程序设计有所帮助。

PHP 相关文章推荐
基于Zend的Config机制的应用分析
May 02 PHP
PHP echo,print,printf,sprintf函数之间的区别与用法详解
Nov 27 PHP
PHP开发框架kohana3 自定义路由设置示例
Jul 14 PHP
实例介绍PHP的Reflection反射机制
Aug 05 PHP
php中mysql连接方式PDO使用详解
Feb 25 PHP
PHP 7的一些引人注目的新特性简单介绍
Nov 08 PHP
php有效防止同一用户多次登录
Nov 19 PHP
Laravel使用Caching缓存数据减轻数据库查询压力的方法
Mar 15 PHP
PHP生成短网址方法汇总
Jul 12 PHP
php网页版聊天软件实现代码
Aug 12 PHP
php实现等比例压缩图片
Jul 26 PHP
PHP与Perl之间知识点区别整理
Mar 19 PHP
php调用mysql存储过程实例分析
Dec 29 #PHP
php生成excel列名超过26列大于Z时的解决方法
Dec 29 #PHP
php+mysqli实现批量替换数据库表前缀的方法
Dec 29 #PHP
PHP跨平台获取服务器IP地址自定义函数分享
Dec 29 #PHP
PHP中使用xmlreader读取xml数据示例
Dec 29 #PHP
php读取远程gzip压缩网页的方法
Dec 29 #PHP
php导入大量数据到mysql性能优化技巧
Dec 29 #PHP
You might like
php中strtotime函数性能分析
2016/11/20 PHP
PHP中number_format()函数的用法讲解
2019/04/08 PHP
javascript继承之为什么要继承
2012/11/10 Javascript
javascript返回顶部效果(自写代码)
2013/01/06 Javascript
JS实现字体选色板实例代码
2013/11/20 Javascript
JavaScript中的数组操作介绍
2014/12/30 Javascript
jquery插件jquery.dragscale.js实现拖拽改变元素大小的方法(附demo源码下载)
2016/02/25 Javascript
深入解析AngularJS框架中$scope的作用与生命周期
2016/03/05 Javascript
Angularjs CURD 详解及实例代码
2016/09/14 Javascript
js生成随机数方法和实例
2017/01/17 Javascript
js中编码函数:escape,encodeURI与encodeURIComponent详解
2017/03/21 Javascript
详解vue-cli + webpack 多页面实例配置优化方法
2017/07/13 Javascript
Angularjs的启动过程分析
2017/07/18 Javascript
实现div内部滚动条滚动到底部和顶部的代码
2017/11/15 Javascript
微信小程序中使用wxss加载图片并实现动画效果
2018/08/13 Javascript
vue.js添加一些触摸事件以及安装fastclick的实例
2018/08/28 Javascript
基于vue中keep-alive缓存问题的解决方法
2018/09/21 Javascript
vue中的面包屑导航组件实例代码
2019/07/01 Javascript
AutoJs实现刷宝短视频的思路详解
2020/05/22 Javascript
python2 与python3的print区别小结
2018/01/16 Python
利用Pandas 创建空的DataFrame方法
2018/04/08 Python
Python设计模式之建造者模式实例详解
2019/01/17 Python
Python基础之文件读取的讲解
2019/02/16 Python
Python中numpy模块常见用法demo实例小结
2019/03/16 Python
python pytest进阶之fixture详解
2019/06/27 Python
Python基于进程池实现多进程过程解析
2020/04/30 Python
运行Python编写的程序方法实例
2020/10/21 Python
CSS3 input框的实现代码类似Google登录的动画效果
2020/08/04 HTML / CSS
餐厅执行经理岗位职责范本
2014/02/26 职场文书
公司应聘求职信
2014/06/21 职场文书
优秀教师先进事迹材料
2014/12/15 职场文书
2015年全国科普日活动总结
2015/03/23 职场文书
2015年财务个人工作总结范文
2015/05/22 职场文书
为Java项目添加Redis缓存的方法
2021/05/18 Redis
Mysql 设置boolean类型的操作
2021/06/04 MySQL
详解MongoDB排序时内存大小限制与创建索引的注意事项
2022/05/06 MongoDB