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 相关文章推荐
frename PHP 灵活文件命名函数 frename
Sep 09 PHP
php引用地址改变变量值的问题
Mar 23 PHP
屏蔽机器人从你的网站搜取email地址的php代码
Nov 14 PHP
详解 PHP加密解密字符串函数附源码下载
Dec 18 PHP
zen_cart实现支付前生成订单的方法
May 06 PHP
php mysqli查询语句返回值类型实例分析
Jun 29 PHP
Yii2中关联查询简单用法示例
Aug 10 PHP
php使用str_replace替换多维数组的实现方法分析
Jun 15 PHP
PHP使用Curl实现模拟登录及抓取数据功能示例
Apr 27 PHP
PHP绕过open_basedir限制操作文件的方法
Jun 10 PHP
Laravel框架实现超简单的分页效果示例
Feb 08 PHP
PHP抽象类与接口的区别详解
Mar 21 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
同时提取多条新闻中的文本一例
2006/10/09 PHP
用文本文件实现的动态实时发布新闻的程序
2006/10/09 PHP
Smarty的配置与高级缓存技术分享
2012/06/05 PHP
使用php语句将数据库*.sql文件导入数据库
2014/05/05 PHP
php设计模式之单例模式代码
2016/06/11 PHP
使用XHProf查找PHP性能瓶颈的实例
2017/12/13 PHP
jquery插件之信息弹出框showInfoDialog(成功/错误/警告/通知/背景遮罩)
2013/01/09 Javascript
js使用eval解析json实例与注意事项分享
2014/01/18 Javascript
node.js中的fs.truncateSync方法使用说明
2014/12/15 Javascript
JS拖拽组件学习使用
2016/01/19 Javascript
把普通对象转换成json格式的对象的简单实例
2016/07/04 Javascript
JS中的phototype详解
2017/02/04 Javascript
jQuery插件HighCharts绘制的2D堆柱状图效果示例【附demo源码下载】
2017/03/14 Javascript
jQuery插件开发发送短信倒计时功能代码
2017/05/09 jQuery
基于JavaScript实现无缝滚动效果
2017/07/21 Javascript
React学习之事件绑定的几种方法对比
2017/09/24 Javascript
JS交互点击WKWebView中的图片实现预览效果
2018/01/05 Javascript
Vue如何实现响应式系统
2018/07/11 Javascript
nodejs中实现用户注册路由功能
2019/05/20 NodeJs
python正则分组的应用
2013/11/10 Python
python删除列表中重复记录的方法
2015/04/28 Python
通过mod_python配置运行在Apache上的Django框架
2015/07/22 Python
让python在hadoop上跑起来
2016/01/27 Python
python 垃圾收集机制的实例详解
2017/08/20 Python
python实现翻译word表格小程序
2020/02/27 Python
任意一块网页内容实现“活”的背景(目前火狐浏览器专有)
2014/05/07 HTML / CSS
HTML5+JS实现俄罗斯方块原理及具体步骤
2013/11/29 HTML / CSS
Bally澳大利亚官网:瑞士奢侈品牌
2018/11/01 全球购物
英国绿色商店:Natural Collection
2019/05/03 全球购物
高中生家长寄语大全
2014/04/03 职场文书
企业安全生产责任书
2014/04/14 职场文书
2015年银行大堂经理工作总结
2015/04/24 职场文书
2015年幼儿园卫生保健工作总结
2015/05/12 职场文书
幼儿园六一儿童节主持词
2015/06/30 职场文书
sentinel支持的redis高可用集群配置详解
2022/04/01 Redis
Java对文件的读写操作方法
2022/04/29 Java/Android