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 相关文章推荐
PHP4之COOKIE支持详解
Oct 09 PHP
php获取当前网址url并替换参数或网址的方法
Jun 06 PHP
codeigniter教程之上传视频并使用ffmpeg转flv示例
Feb 13 PHP
CI框架中通过hook的方式实现简单的权限控制
Jan 07 PHP
PHP函数extension_loaded()用法实例
Jan 19 PHP
PHP加密解密函数详解
Oct 28 PHP
laravel 5.1下php artisan migrate的使用注意事项总结
Jun 07 PHP
使用 laravel sms 构建短信验证码发送校验功能
Nov 06 PHP
layui框架实现文件上传及TP3.2.3(thinkPHP)对上传文件进行后台处理操作示例
May 12 PHP
PHP封装XML和JSON格式数据接口操作示例
Mar 06 PHP
Laravel框架控制器,视图及模型操作图文详解
Dec 04 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远程连接MYSQL数据库非常慢的解决方法
2008/07/05 PHP
destoon调用discuz论坛中带图片帖子的实现方法
2014/08/21 PHP
php根据生日计算年龄的方法
2015/07/13 PHP
php mysql获取表字段名称和字段信息的三种方法
2016/11/13 PHP
JQuery 学习笔记 选择器之三
2009/07/23 Javascript
ASP.NET中基于JQUERY的高性能的TreeView补充
2011/02/23 Javascript
对象无length属性时IE6/IE7中无法将其转换成伪数组(ArrayLike)
2011/07/31 Javascript
JavaScript中常用的六种互动方法示例
2015/03/13 Javascript
jQuery实现区域打印功能代码详解
2016/06/17 Javascript
微信小程序scroll-view实现横向滚动和上拉加载示例
2017/03/06 Javascript
vue2.0 实现页面导航提示引导的方法
2018/03/13 Javascript
vue 通过绑定事件获取当前行的id操作
2020/07/27 Javascript
JavaScript this关键字指向常用情况解析
2020/09/02 Javascript
vue element和nuxt的使用技巧分享
2021/01/14 Vue.js
[05:53]完美世界携手游戏风云打造 卡尔工作室观战系统篇
2013/04/22 DOTA
[01:02:25]2014 DOTA2华西杯精英邀请赛5 24 NewBee VS VG
2014/05/25 DOTA
[03:23:49]2016.12.17日完美“圣”典全回顾
2016/12/19 DOTA
qpython3 读取安卓lastpass Cookies
2016/06/19 Python
在python3.5中使用OpenCV的实例讲解
2018/04/02 Python
让代码变得更易维护的7个Python库
2018/10/09 Python
Python-接口开发入门解析
2019/08/01 Python
记一次django内存异常排查及解决方法
2020/08/07 Python
python常量折叠基础知识点讲解
2021/02/28 Python
Jupyter安装拓展nbextensions及解决官网下载慢的问题
2021/03/03 Python
html5 分层屏幕适配的方法
2018/03/16 HTML / CSS
HTML5进阶段内联标签汇总(小篇)
2016/07/13 HTML / CSS
Luxplus瑞典:香水和美容护理折扣
2018/01/28 全球购物
泰国办公用品购物网站:OfficeMate
2018/02/04 全球购物
为什么group by 和order by会使查询变慢
2014/05/16 面试题
驾驶员培训方案
2014/05/01 职场文书
搞笑结婚保证书
2015/05/08 职场文书
2016年校长新年寄语
2015/08/17 职场文书
生日宴会家属答谢词
2015/09/29 职场文书
IDEA使用SpringAssistant插件创建SpringCloud项目
2021/06/23 Java/Android
详解CSS3.0(Cascading Style Sheet) 层叠级联样式表
2021/07/16 HTML / CSS
python计算列表元素与乘积详情
2022/08/05 Python