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的计数器程序
Oct 09 PHP
收藏的PHP常用函数 推荐收藏保存
Feb 21 PHP
php中全局变量global的使用演示代码
May 18 PHP
PHP随机字符串生成代码(包括大小写字母)
Jun 24 PHP
解决PHP mysql_query执行超时(Fatal error: Maximum execution time …)
Jul 03 PHP
php提示Warning:mysql_fetch_array() expects的解决方法
Dec 16 PHP
Win7下手动安装apache2.2、php5.4笔记
Apr 03 PHP
PHP中addslashes与mysql_escape_string的区别分析
Apr 25 PHP
PHP页面跳转操作实例分析(header方法)
Sep 28 PHP
php中类和对象:静态属性、静态方法
Apr 09 PHP
ThinkPHP实现图片上传操作的方法详解
May 08 PHP
PHP实现网站访问量计数器
Oct 27 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原创论坛
2006/10/09 PHP
3.从实例开始
2006/10/09 PHP
PHP学习之数组值的操作
2011/04/17 PHP
php中通过正则表达式下载内容中的远程图片的函数代码
2012/01/10 PHP
PHP实现统计在线人数功能示例
2016/10/15 PHP
PHP使用第三方即时获取物流动态实例详解
2017/04/27 PHP
PHP中abstract(抽象)、final(最终)和static(静态)原理与用法详解
2020/06/05 PHP
如何实现iframe(嵌入式帧)的自适应高度
2006/07/26 Javascript
JavaScript中iframe实现局部刷新的几种方法汇总
2016/01/06 Javascript
批量下载对路网图片并生成html的实现方法
2016/06/07 Javascript
前端js实现文件的断点续传 后端PHP文件接收
2016/10/14 Javascript
js控制li的隐藏和显示实例代码
2016/10/15 Javascript
Angular通过指令动态添加组件问题
2018/07/09 Javascript
jquery操作checkbox的常用方法总结【附测试源码下载】
2019/06/10 jQuery
Vue实现点击显示不同图片的效果
2019/08/10 Javascript
微信小程序实现写入读取缓存详解
2019/08/30 Javascript
javascript 函数的暂停和恢复实例详解
2020/04/25 Javascript
Python修改Excel数据的实例代码
2013/11/01 Python
python将xml xsl文件生成html文件存储示例讲解
2013/12/03 Python
朴素贝叶斯算法的python实现方法
2014/11/18 Python
python 给DataFrame增加index行名和columns列名的实现方法
2018/06/08 Python
Django migrations 默认目录修改的方法教程
2018/09/28 Python
Python实现使用request模块下载图片demo示例
2019/05/24 Python
PyTorch 随机数生成占用 CPU 过高的解决方法
2020/01/13 Python
django执行原始查询sql,并返回Dict字典例子
2020/04/01 Python
Python可视化工具如何实现动态图表
2020/10/23 Python
英国现代家具和装饰网站:PN Home
2018/08/16 全球购物
英国排名第一的冲浪店:Ann’s Cottage
2020/06/21 全球购物
Linux文件系统类型
2012/02/15 面试题
成功的餐厅经营创业计划书
2014/01/15 职场文书
厕所文明标语
2014/06/11 职场文书
宾馆安全管理制度
2015/08/06 职场文书
趣味运动会标语口号
2015/12/26 职场文书
《倍数和因数》教学反思
2016/02/23 职场文书
话题作文之学会尊重
2019/12/16 职场文书
OpenCV3.3+Python3.6实现图片高斯模糊
2021/05/18 Python