您的位置:首页 > 其它

master-slave的实现

2013-05-02 16:20 239 查看
今天本来是想研究一下AR模式的, 可是一不小心, 有陷入了master-slave的实现细节当中.研究之后我决定自己写一个master和slave链接的实现.当然首先让我们一起来看看我们常用的数据库查询需要的语句.

$connection=Yii::app()->db;

$command=$connection->createCommand($sql);

$rowCount=$command->execute();

标注为红色的地方引起了我的注意,于是有了以下几个想法:

1) 这个db 明显是Yii 初始化的实例CwebApplication的一个成员变量, 但是在它及它的所有的父类中却找不到db的声明. 直到后来在跟踪查看到CModule中的configure方法时才恍然大悟.

public function configure($config)

{

if(is_array($config))

{

foreach($config as $key=>$value)

$this->$key=$value;

}

}

它竟然用了一个for 循环就把我们在main.php 中所定义的配置变量变成了自己的成员了, 这招实在是精妙.

2) 于是我就想, 既然db是这样变成成员变量的,那我能不能也照样子在/protected/config/main.php配置一下master 和slave就好了呢.

:

'master' => array( //master db

'class' => 'CDBConnection',

'connectionString' => 'mysql:host=127.0.0.1;port=3306;dbname=test',

'emulatePrepare' => true,

'username' => 'root',

'password' => '123',

'charset' => 'utf8',

'tablePrefix' => 'ugc_',

'schemaCachingDuration' => 1,

),

'slave' => array( //slave db

'class' => 'system.db.CDbConnection',

'connectionString' => 'mysql:host=127.0.0.1;port=3306;dbname=test',

'emulatePrepare' => true,

'username' => 'root',

'password' => '123',

'charset' => 'utf8',

'tablePrefix' => 'ugc_',

'schemaCachingDuration' => 1,

),

:

呵呵, 现在随便在controller的action里边加上这一句看看:

$connection=Yii::app()->slave;

ERROR: include(CDBConnection.php) [<a href='function.include'>function.include</a>]: failed to open stream: No such file or directory

结果却是报错了.为什么它的可以, 我的就不行呢.

3) 仔细调试发现, 其他的都一样,就是找不到相应的class 文件.于是为CDbConnection 加上路径,结果就没问题了.即:

CDbConnection 改成 system.db.CDbConnection 这里用到了system的路径别名. 相当于/yii/db/CDbConnection

OK, 到此为止, 配合我们的mysql的主从同步服务器, 我们便可根据自己的需要选择不同的数据库服务器(一般情况下: 主数据库负责写, 从数据库负责读数据), 以达到读写分离的目的. 从而提升数据库整体的服务能力了, 比如:

读的时候:

$connection=Yii::app()->slave;

$command=$connection->createCommand($sql);

$rowCount=$command->execute();

写的时候:

$connection=Yii::app()->master;

$command=$connection->createCommand($sql);

$rowCount=$command->execute();

说道这里该完了吗? 你也许会问: 你这种方法只是自己在调用的时候明确指定了主从, 那AR模式创建的model里边怎么办?

好吧,那我们就继续讨论该问题.

因为所有用Yii工具创建的Model类都继承自CActiveRecord, 所以就想到该在CActiveRecord中找找线索,终于当看到以下这个方法时,突然有了想法.

public function getCommandBuilder()

{

return $this->getDbConnection()->getSchema()->getCommandBuilder();

}

对了,就是它.AR 的model在连接数据的时候都是这样来的, 何不将其重新定制一下呢.于是决定在protected/web下新建一个类文件,继承自CActiveRecord.

<?PHP

class MyActiveRecord extends CActiveRecord

{

private $master = null;

private $slave = null;

public function getMasterDbConnection(){

if ($this->master!===null)

return $this->master;

else if ($this->master instanceof CDbConnection)

return Yii::app()->master;

else

throw new CDbException(Yii::t('yii','Active Record requires a "master" CDbConnection application component.'));

}

public function getSlaveDbConnection()

if ($this->slave!===null)

return $this->slave;

else if ($this->slave instanceof CDbConnection)

return Yii::app()->slave;

else

throw new CDbException(Yii::t('yii','Active Record requires a "master" CDbConnection application component.'));

}

public function getCommandBuilder($master = true) {

if ($master)

return $this->getWriteCommandBuilder(); return $this->getReadCommandBuilder(); }

:

:

:

注:下边就是把CActiveRecord中的所有读写数据苦的函数复写一遍, 根据读还是写, 调用getCommandBuilder函数选择主从数据库.

完了之后, 再把生成的model类的父类换成我们自己写的类MyActiveRecord, 这样就算完了.

补充: 如果修改了父类后, 提示MyActiveRecord没找到, 则在main.php中导入该文件所在的位置;

'import'=>array(

'application.models.*',

'application.components.*',

'application.web.*',

),

}

现在应该是没有问题了吧,呵呵. 继续努力, 与君共勉
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: