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采集相关教程之一 CURL函数库
Feb 15 PHP
php 文件上传后缀名与文件类型对照表(几乎涵盖所有文件)
May 16 PHP
比较简单实用的PHP无限分类源码分享(思路不错)
Oct 13 PHP
phpcms模块开发之swfupload的使用介绍
Apr 28 PHP
PHP使用CURL_MULTI实现多线程采集的例子
Jul 29 PHP
php合并数组中相同元素的方法
Nov 13 PHP
PHP中is_file()函数使用指南
May 08 PHP
Yii2框架引用bootstrap中日期插件yii2-date-picker的方法
Jan 09 PHP
Yii2框架RESTful API 格式化响应,授权认证和速率限制三部分详解
Nov 10 PHP
PHP 实现页面静态化的几种方法
Jul 23 PHP
Kindeditor编辑器添加图片上传水印功能(php代码)
Aug 03 PHP
php curl发送请求实例方法
Aug 01 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
怎么样可以把 phpinfo()屏蔽掉?
2006/11/24 PHP
PHP小教程之实现链表
2014/06/09 PHP
php 删除指定文件夹的实例讲解
2017/07/25 PHP
laravel 中某一字段自增、自减的例子
2019/10/11 PHP
关于js数组去重的问题小结
2014/01/24 Javascript
jQuery响应enter键的实现思路
2014/04/18 Javascript
JavaScript获得表单target属性的方法
2015/04/02 Javascript
学习使用bootstrap基本控件(table、form、button)
2016/04/12 Javascript
javascript执行环境及作用域详解
2016/05/05 Javascript
AngularJS实现根据变量改变动态加载模板的方法
2016/11/04 Javascript
js for循环倒序输出数组元素的实例
2017/03/01 Javascript
详解NodeJS框架express的路径映射(路由)功能及控制
2017/03/24 NodeJs
js实现分页功能
2017/05/24 Javascript
JS移动端/H5同时选择多张图片上传并使用canvas压缩图片
2017/06/20 Javascript
微信小程序+云开发实现欢迎登录注册
2019/05/24 Javascript
使用Vue.js 和Chart.js制作绚丽多彩的图表
2019/06/15 Javascript
详解vue 2.6 中 slot 的新用法
2019/07/09 Javascript
JavaScript 俄罗斯方块游戏实现方法与代码解释
2020/04/08 Javascript
微信小程序仿抖音短视频切换效果的实例代码
2020/06/24 Javascript
[05:56]第十六期——新进3大C之小兔基
2014/06/24 DOTA
[08:53]DOTA2-DPC中国联赛 正赛 PSG.LGD vs LBZS 选手采访
2021/03/11 DOTA
Python中的异常处理学习笔记
2015/01/28 Python
编写Python脚本来获取Google搜索结果的示例
2015/05/04 Python
PyQt5每天必学之工具提示功能
2018/04/19 Python
Python实现的多叉树寻找最短路径算法示例
2018/07/30 Python
详解python如何在django中为用户模型添加自定义权限
2018/10/15 Python
如何用Python做一个微信机器人自动拉群
2019/07/03 Python
解决Django 在ForeignKey中出现 non-nullable field错误的问题
2019/08/06 Python
python time()的实例用法
2020/11/03 Python
python的数学算法函数及公式用法
2020/11/18 Python
深入浅析css3 中display box使用方法
2015/11/25 HTML / CSS
会计大学生职业生涯规划书范文
2014/01/13 职场文书
史学专业毕业生求职信
2014/05/09 职场文书
教师敬业奉献模范事迹材料
2014/05/18 职场文书
网站推广策划方案
2014/06/04 职场文书
Go微服务项目配置文件的定义和读取示例详解
2022/06/21 Golang