Yii实现MySQL多数据库和读写分离实例分析


Posted in PHP onDecember 03, 2014

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

Yii Framework是一个基于组件、用于开发大型 Web 应用的高性能 PHP 框架。Yii提供了今日Web 2.0应用开发所需要的几乎一切功能,也是最强大的框架之一,下文我们来介绍Yii实现MySQL多库和读写分离的方法

前段时间为SNS产品做了架构设计,在程序框架方面做了不少相关的压力测试,最终选定了YiiFramework,至于为什么没选用公司内部的 PHP框架,其实理由很充分,公司的框架虽然是"前辈"们辛苦的积累,但毕竟不够成熟,没有大型项目的历练,犹如一个涉世未深的年轻小伙。Yii作为一个 颇有名气开源产品,必定有很多人在使用,意味着有一批人在维护,而且在这之前,我也使用Yii开发过大型项目,Yii的设计模式和它的易扩展特性足以堪当重任。

SNS同一般的社交产品不同的就是它最终要承受大并发和大数据量的考验,架构设计时就要考虑这些问题, web分布式、负载均衡、分布式文件存储、MySQL分布式或读写分离、NoSQL以及各种缓存,这些都是必不可少的应用方案,本文所讲的就是MySQL 分库和主从读写分离在Yii的配置和使用。

Yii默认是不支持读写分离的,我们可以利用Yii的事件驱动模式来实现MySQL的读写分离。

Yii提供了一个强大的CActiveRecord数据库操作类,通过重写getDbConnection方法来实现数据库的切换,然后通过事件 beforeSave、beforeDelete、beforeFind 来实现读写服务器的切换,还需要两个配置文件dbconfig和modelconfig分别配置数据库主从服务器和model所对应的数据库名称,附代码
DBConfig.php文件如下:

<?php

return array(

'passport' => array(

'write' => array(

'class' => 'CDbConnection',

'connectionString' => 'mysql:host=10.1.39.2;dbname=db1′,

'emulatePrepare' => true,

//'enableParamLogging' => true,

'enableProfiling' => true,

'username' => 'root',

'password' => '',

'charset' => 'utf8′,

'schemaCachingDuration'=>3600,

),

'read' => array(

array(

'class' => 'CDbConnection',

'connectionString' => 'mysql:host=10.1.39.3;dbname=db1,

'emulatePrepare' => true,

//'enableParamLogging' => true,

'enableProfiling' => true,

'username' => 'root',

'password' => '',

'charset' => 'utf8′,

'schemaCachingDuration'=>3600,

),

array(

'class' => 'CDbConnection',

'connectionString' => 'mysql:host=10.1.39.4;dbname=db3′,

'emulatePrepare' => true,

//'enableParamLogging' => true,

'enableProfiling' => true,

'username' => 'root',

'password' => '',

'charset' => 'utf8′,

'schemaCachingDuration'=>3600,

),

),

),

);

ModelConfig.php如下:
<?php

return array(

//key为数据库名称,value为Model

'passport' => array('User','Post'),

'microblog' => array('…'),

);

?>

ActiveRecord.php如下:
/**

* 基于CActiveRecord类的封装,实现多库和主从读写分离

* 所有Model都必须继承些类.

*

*/

class ActiveRecord extends CActiveRecord

{

//model配置

public $modelConfig = '';

//数据库配置

public $dbConfig = '';

//定义一个多数据库集合

static $dataBase = array();

//当前数据库名称

public $dbName = '';

//定义库类型(读或写)

public $dbType = 'read'; //'read' or 'write'

/**

* 在原有基础上添加了一个dbname参数

* @param string $scenario Model的应用场景

* @param string $dbname 数据库名称

*/

public function __construct($scenario='insert', $dbname = '')

{

if (!empty($dbname))

$this->dbName = $dbname;

parent::__construct($scenario);

}

/**

* 重写父类的getDbConnection方法

* 多库和主从都在这里切换

*/

public function getDbConnection()

{

//如果指定的数据库对象存在则直接返回

if (self::$dataBase[$this->dbName]!==null)

return self::$dataBase[$this->dbName];

if ($this->dbName == 'db'){

self::$dataBase[$this->dbName] = Yii::app()->getDb();

}else{

$this->changeConn($this->dbType);

}

if(self::$dataBase[$this->dbName] instanceof CDbConnection){

self::$dataBase[$this->dbName]->setActive(true);

return self::$dataBase[$this->dbName];

} else

throw new CDbException(Yii::t('yii','Model requires a "db" CDbConnection application component.'));

}

/**

* 获取配置文件

* @param unknown_type $type

* @param unknown_type $key

*/

private function getConfig($type="modelConfig",$key="){

$config = Yii::app()->params[$type];

if($key)

$config = $config[$key];

return $config;

}

/**

* 获取数据库名称

*/

private function getDbName(){

if($this->dbName)

return $this->dbName;

$modelName = get_class($this->model());

$this->modelConfig = $this->getConfig('modelConfig');

//获取model所对应的数据库名

if($this->modelConfig)foreach($this->modelConfig as $key=>$val){

if(in_array($modelName,$val)){

$dbName = $key;

break;

}

}

return $dbName;

}

/**

* 切换数据库连接

* @param unknown_type $dbtype

*/

protected function changeConn($dbtype = 'read'){

if($this->dbType == $dbtype && self::$dataBase[$this->dbName] !== null)

return self::$dataBase[$this->dbName];

$this->dbName = $this->getDbName();

if(Yii::app()->getComponent($this->dbName.'_'.$dbtype) !== null){

self::$dataBase[$this->dbName] = Yii::app()->getComponent($this->dbName.'_'.$dbtype);

return self::$dataBase[$this->dbName];

}

$this->dbConfig = $this->getConfig('dbConfig',$this->dbName);

//跟据类型取对应的配置(从库是随机值)

if($dbtype == 'write'){

$config = $this->dbConfig[$dbtype];

}else{

$slavekey = array_rand($this->dbConfig[$dbtype]);

$config = $this->dbConfig[$dbtype][$slavekey];

}

//将数据库配置加到component中

if($dbComponent = Yii::createComponent($config)){

Yii::app()->setComponent($this->dbName.'_'.$dbtype,$dbComponent);

self::$dataBase[$this->dbName] = Yii::app()->getComponent($this->dbName.'_'.$dbtype);

$this->dbType = $dbtype;

return self::$dataBase[$this->dbName];

} else

throw new CDbException(Yii::t('yii','Model requires a "changeConn" CDbConnection application component.'));

}

/**

* 保存数据前选择 主 数据库

*/

protected function beforeSave(){

parent::beforeSave();

$this->changeConn('write');

return true;

}

/**

* 删除数据前选择 主 数据库

*/

protected function beforeDelete(){

parent::beforeDelete();

$this->changeConn('write');

return true;

}

/**

* 读取数据选择 从 数据库

*/

protected function beforeFind(){

parent::beforeFind();

$this->changeConn('read');

return true;

}

/**

* 获取master库对象

*/

public function dbWrite(){

return $this->changeConn('write');

}

/**

* 获取slave库对象

*/

public function dbRead(){

return $this->changeConn('read');

}

}

这是我写好的类,放在components文件夹里,然后所有的Model都继承ActiveRecord类就可以实现多库和主从读写分离了,至于如何支持原生的SQL也同时使用读写分离,此类都已经实现。

希望本文所述对大家基于Yii框架的PHP程序设计有所帮助。

PHP 相关文章推荐
PHP运行时强制显示出错信息的代码
Apr 20 PHP
关于php连接mssql:pdo odbc sql server
Jul 20 PHP
PHP通过正则表达式下载图片到本地的实现代码
Sep 19 PHP
discuz加密解密函数使用方法和中文注释
Jan 21 PHP
ThinkPHP模板中判断volist循环的最后一条记录的验证方法
Jul 01 PHP
php保存二进制原始数据为图片的程序代码
Oct 14 PHP
以文件形式缓存php变量的方法
Jun 26 PHP
深入php内核之php in array
Nov 10 PHP
PHP简单获取及判断提交来源的方法
Apr 22 PHP
PHP检查网站是否宕机的方法示例
Jul 24 PHP
阿里云的WindowsServer2016上部署php+apache
Jul 17 PHP
关于laravel 子查询 &amp; join的使用
Oct 16 PHP
Yii框架登录流程分析
Dec 03 #PHP
Yii框架获取当前controlle和action对应id的方法
Dec 03 #PHP
PHP多线程类及用法实例
Dec 03 #PHP
php提取字符串中网站url地址的方法
Dec 03 #PHP
thinkphp常见路径用法分析
Dec 02 #PHP
ThinkPHP中关联查询实例
Dec 02 #PHP
ThinkPHP实现支付宝接口功能实例
Dec 02 #PHP
You might like
php代码架构的八点注意事项
2016/01/25 PHP
PHP观察者模式示例【Laravel框架中有用到】
2018/06/15 PHP
根据分辨率不同,调用不同的css文件
2006/07/07 Javascript
基于jquery中children()与find()的区别介绍
2013/04/26 Javascript
JS实现点击图片在当前页面放大并可关闭的漂亮效果
2013/10/18 Javascript
在JavaScript中处理时间之setMinutes()方法的使用
2015/06/11 Javascript
JavaScript实战之带收放动画效果的导航菜单
2016/08/16 Javascript
解决vue项目nginx部署到非根目录下刷新空白的问题
2018/09/27 Javascript
elementUI vue this.$confirm 和el-dialog 弹出框 移动 示例demo
2019/07/03 Javascript
layui 上传文件_批量导入数据UI的方法
2019/09/23 Javascript
简单实现节流函数和防抖函数过程解析
2019/10/08 Javascript
jQuery操作事件完整实例分析
2020/01/10 jQuery
详解javascript中var与ES6规范中let、const区别与用法
2020/01/11 Javascript
《Python之禅》中对于Python编程过程中的一些建议
2015/04/03 Python
分享Python文本生成二维码实例
2016/01/06 Python
Tornado高并发处理方法实例代码
2018/01/15 Python
Python建立Map写Excel表实例解析
2018/01/17 Python
Python3 安装PyQt5及exe打包图文教程
2019/01/08 Python
python实现可逆简单的加密算法
2019/03/22 Python
Python向excel中写入数据的方法
2019/05/05 Python
Python中xml和dict格式转换的示例代码
2019/11/07 Python
python3实现单目标粒子群算法
2019/11/14 Python
Python2与Python3的区别详解
2020/02/09 Python
python如何设置静态变量
2020/09/07 Python
python实现感知机模型的示例
2020/09/30 Python
python中最小二乘法详细讲解
2021/02/19 Python
HTML5表格_动力节点Java学院整理
2017/07/11 HTML / CSS
HTML5 video 视频标签使用介绍
2014/02/03 HTML / CSS
canvas实现按住鼠标移动绘制出轨迹的示例代码
2018/02/05 HTML / CSS
浅谈amaze-ui中datepicker和datetimepicker注意的几点
2020/08/21 HTML / CSS
REISS英国官网:伦敦High Street最受欢迎品牌
2016/12/21 全球购物
TripAdvisor越南:全球领先的旅游网站
2017/09/21 全球购物
英国珠宝和手表专家:Pleasance & Harper
2020/10/21 全球购物
文明礼仪事迹材料
2014/01/09 职场文书
2015年大学宣传部工作总结
2015/05/26 职场文书
2015大一新生军训感言
2015/08/01 职场文书