masterslave的实现

今天本来是想研究一下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.*',

),

}

现在应该是没有问题了吧,呵呵.    继续努力, 与君共勉

原文地址:https://www.cnblogs.com/xiongsd/p/3054857.html