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读取目录下所有文件的代码
Jan 07 PHP
PHP详细彻底学习Smarty
Mar 27 PHP
php+mysql分页代码详解
Mar 27 PHP
PHP数组操作汇总 php数组的使用技巧
Jul 17 PHP
mac下安装nginx和php
Nov 04 PHP
解决PhpMyAdmin中导入2M以上大文件限制的方法分享
Jun 06 PHP
PHP提示Cannot modify header information - headers already sent by解决方法
Sep 22 PHP
php中curl使用指南
Feb 05 PHP
php插入含有特殊符号数据的处理方法
Nov 24 PHP
thinkPHP框架实现多表查询的方法
Jun 14 PHP
php实现对文件压缩简单的方法
Sep 29 PHP
laravel框架路由分组,中间件,命名空间,子域名,路由前缀实例分析
Feb 18 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延迟静态绑定示例分享
2014/06/22 PHP
php中10个不同等级压缩优化图片操作示例
2016/11/14 PHP
php+mysql实现简单登录注册修改密码网页
2016/11/30 PHP
php设计模式之策略模式实例分析【星际争霸游戏案例】
2020/03/26 PHP
javascript innerHTML使用分析
2010/12/03 Javascript
学习javascript,实现插入排序实现代码
2011/07/31 Javascript
jquery滚动条插件jScrollPane的使用介绍
2013/11/08 Javascript
Jquery 实现图片轮换
2015/01/28 Javascript
基于jQuery实现仿淘宝套餐选择插件
2015/03/04 Javascript
Vue.js每天必学之过滤器与自定义过滤器
2016/09/07 Javascript
NodeJS整合银联网关支付(DEMO)
2016/11/09 NodeJs
JavaScript数组排序reverse()和sort()方法详解
2017/12/24 Javascript
深入理解ES6之数据解构的用法
2018/01/13 Javascript
详解组件库的webpack构建速度优化
2018/06/18 Javascript
详解React之父子组件传递和其它一些要点
2018/06/25 Javascript
浅谈webpack4 图片处理汇总
2018/09/12 Javascript
jQuery 点击获取验证码按钮及倒计时功能
2018/09/20 jQuery
微信小程序网络层封装的实现(promise, 登录锁)
2019/05/08 Javascript
解决ant design vue 表格a-table二次封装,slots渲染的问题
2020/10/28 Javascript
[03:59]5分钟带你了解什么是DOTA2(第二期)
2017/02/07 DOTA
Python入门篇之数字
2014/10/20 Python
NumPy 如何生成多维数组的方法
2018/02/05 Python
使用实现XlsxWriter创建Excel文件并编辑
2018/05/04 Python
python实现排序算法解析
2018/09/08 Python
Python装饰器用法实例分析
2019/01/14 Python
如何爬取通过ajax加载数据的网站
2019/08/15 Python
Python PO设计模式的具体使用
2019/08/16 Python
Pretty Little Thing爱尔兰:时尚女性服饰
2017/03/27 全球购物
Gretna Green中文官网:苏格兰格林小镇
2019/10/16 全球购物
应届生船舶驾驶求职信
2013/10/19 职场文书
兼职业务员岗位职责
2014/01/01 职场文书
优秀技术工人先进材料
2014/02/17 职场文书
小学生安全演讲稿
2014/04/25 职场文书
运动会广播稿150字(9篇)
2014/09/20 职场文书
优秀员工推荐材料
2014/12/20 职场文书
高二化学教学反思
2016/02/22 职场文书