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正则
Jul 07 PHP
php&amp;java(二)
Oct 09 PHP
强制PHP命令行脚本单进程运行的方法
Apr 15 PHP
php输入流php://input使用浅析
Sep 02 PHP
Laravel框架路由配置总结、设置技巧大全
Sep 03 PHP
php表单提交与$_POST实例分析
Jan 26 PHP
php对象在内存中的存在形式分析
Feb 03 PHP
PHP实现支持SSL连接的SMTP邮件发送类
Mar 05 PHP
thinkphp3.x连接mysql数据库的方法(具体操作步骤)
May 19 PHP
Zend Framework使用Zend_Loader组件动态加载文件和类用法详解
Dec 09 PHP
Centos 6.5系统下编译安装PHP 7.0.13的方法
Dec 19 PHP
php 实现简单的登录功能示例【基于thinkPHP框架】
Dec 02 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 无极分类(递归)实现代码
2010/01/05 PHP
php tp验证表单与自动填充函数代码
2012/02/22 PHP
php中mysql连接和基本操作代码(快速测试使用,简单方便)
2014/04/25 PHP
php实现微信公众平台账号自定义菜单类
2014/12/02 PHP
php判断手机浏览还是web浏览,并执行相应的动作简单实例
2016/07/28 PHP
设定php简写功能的方法
2019/11/28 PHP
js动态加载以及确定加载完成的代码
2011/07/31 Javascript
jQuery 插件开发指南
2014/11/14 Javascript
TinyMCE提交AjaxForm获取不到数据的解决方法
2015/03/05 Javascript
JS+DIV+CSS实现的经典标签切换效果代码
2015/09/14 Javascript
原生js实现水平方向无缝滚动
2017/01/10 Javascript
BootstrapTable请求数据时设置超时(timeout)的方法
2017/01/22 Javascript
JS异步加载的三种实现方式
2017/03/16 Javascript
基于vue实现滚动条滚动到指定位置对应位置数字进行tween特效
2019/04/18 Javascript
JS实现拖拽元素时与另一元素碰撞检测
2020/08/27 Javascript
[03:17]2016完美“圣”典风云人物:冷冷专访
2016/12/08 DOTA
[49:40]2018DOTA2亚洲邀请赛小组赛 A组加赛 TNC vs Newbee
2018/04/03 DOTA
由浅入深讲解python中的yield与generator
2017/04/05 Python
使用XML库的方式,实现RPC通信的方法(推荐)
2017/06/14 Python
Django admin实现图书管理系统菜鸟级教程完整实例
2017/12/12 Python
Python之列表的插入&amp;替换修改方法
2018/06/28 Python
python实现在遍历列表时,直接对dict元素增加字段的方法
2019/01/15 Python
快速排序的四种python实现(推荐)
2019/04/03 Python
对Django中内置的User模型实例详解
2019/08/16 Python
pytorch 图像预处理之减去均值,除以方差的实例
2020/01/02 Python
Python IDLE或shell中切换路径的操作
2020/03/09 Python
在keras里面实现计算f1-score的代码
2020/06/15 Python
详解Scrapy Redis入门实战
2020/11/18 Python
英国奢侈品概念店:Base Blu
2019/05/16 全球购物
DC Shoes俄罗斯官网:美国滑板鞋和服饰品牌
2020/08/19 全球购物
考试作弊被抓检讨书
2014/01/10 职场文书
犯错检讨书
2014/02/21 职场文书
2014年作风建设心得体会
2014/10/22 职场文书
学习普通话的体会
2014/11/07 职场文书
一篇文章带你深入了解Mysql触发器
2021/08/02 MySQL
python_tkinter弹出对话框创建
2022/03/20 Python