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 相关文章推荐
PHP+javascript液晶时钟
Oct 09 PHP
php URL编码解码函数代码
Mar 10 PHP
用来解析.htpasswd文件的PHP类
Sep 05 PHP
zf框架的校验器使用使用示例(自定义校验器和校验器链)
Mar 13 PHP
php定义数组和使用示例(php数组的定义方法)
Mar 29 PHP
Destoon旺旺无法正常显示,点击提示“会员名不存在”的解决办法
Jun 21 PHP
PHP截取指定图片大小的方法
Dec 10 PHP
Symfony2学习笔记之系统路由详解
Mar 17 PHP
PHP递归实现层级树状展开
Apr 01 PHP
php flush无效,IIS7下php实时输出的方法
Aug 25 PHP
PHP截取发动短信内容的方法
Jul 04 PHP
asp函数split()对应php函数explode()
Feb 27 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
DC动画电影《黑暗正义联盟》曝预告 5月5日上线数字平台
2020/04/09 欧美动漫
实现分十页分向前十页向后十页的处理
2006/10/09 PHP
PHP中浮点数计算比较及取整不准确的解决方法
2015/01/09 PHP
PHP与jquery实时显示网站在线人数实例详解
2016/12/02 PHP
php下载远程大文件(获取远程文件大小)的实例
2017/06/17 PHP
Firefox下提示illegal character并出现乱码的原因
2010/03/25 Javascript
JavaScript整除运算函数ceil和floor的区别分析
2015/04/14 Javascript
js动态创建及移除div的方法
2015/06/03 Javascript
javascript动画算法实例分析
2015/07/31 Javascript
jquery自动补齐功能插件flexselect用法示例
2016/08/06 Javascript
通过bootstrap全面学习less
2016/11/09 Javascript
基于js实现二级下拉联动
2016/12/17 Javascript
详解用node-images 打造简易图片服务器
2017/05/08 Javascript
微信小程序 本地图片按照屏幕尺寸处理
2017/08/04 Javascript
基于Vue、Vuex、Vue-router实现的购物商城(原生切换动画)效果
2018/01/09 Javascript
详解Vue项目中出现Loading chunk {n} failed问题的解决方法
2018/09/14 Javascript
一些可能会用到的Node.js面试题
2019/06/15 Javascript
[51:29]Alliance vs TNC 2019国际邀请赛小组赛 BO2 第二场 8.16
2019/08/18 DOTA
Python中使用md5sum检查目录中相同文件代码分享
2015/02/02 Python
Python魔术方法详解
2015/02/14 Python
django之常用命令详解
2016/06/30 Python
Python用61行代码实现图片像素化的示例代码
2018/12/10 Python
Python字符串内置函数功能与用法总结
2019/04/16 Python
Python实现微信翻译机器人的方法
2019/08/13 Python
在 Jupyter 中重新导入特定的 Python 文件(场景分析)
2019/10/27 Python
python给指定csv表格中的联系人群发邮件(带附件的邮件)
2019/12/31 Python
python+selenium定时爬取丁香园的新型冠状病毒数据并制作出类似的地图(部署到云服务器)
2020/02/09 Python
Python中使用threading.Event协调线程的运行详解
2020/05/02 Python
python 爬虫请求模块requests详解
2020/12/04 Python
医院办公室主任职责
2013/12/29 职场文书
大学生毕业求职自荐书范文
2014/02/04 职场文书
教师竞聘演讲稿
2014/05/16 职场文书
会计演讲稿范文
2014/05/23 职场文书
2014年中秋寄语
2014/08/11 职场文书
乡镇党的群众路线教育实践活动总结报告
2014/10/30 职场文书
2015教师节师德演讲稿
2015/03/19 职场文书