Yii2 数据库读写分离


在项目数据读写遇到瓶颈时,首先想到的就是分库、分表,然后就是读写分离。


正文

在项目数据读写遇到瓶颈时,首先想到的就是分库、分表,然后就是读写分离。

分库标准是项目分类,针对不同项目,如果用户群体角色也不同,数据不交叉(如项目质量管理和客户数据管理就不交叉),那就可以分成多个库,这样可以从一定程度上减轻库的压力。

分表标准是功能分类,不同的功能尽量分成不同的表,还可以把使用频次高低考虑进来,频次低的也单独建个关联表吧。

接下来是重点:数据库读写分离,主从数据库。

在yii2的项目配置项目中修改,一主多从:

'db' => [
    'class' => 'yii\db\Connection',
    // 主库配置
    'dsn' => 'mysql:host=192.168.0.1;port=3306;dbname=company',
    'username' => 'root',
    'password' => 'root',
    'charset' => 'utf8',
    // 从库配置
    'slaveConfig' => [
        'username' => 'root',
        'password' => 'root',
        'charset' => 'utf8',
    ],
    'slaves' => [
        ['dsn' => 'mysql:host=127.0.0.1;port=3306;dbname=company'],
        ['dsn' => 'mysql:host=127.0.0.2;port=3306;dbname=company']
        ['dsn' => 'mysql:host=127.0.0.3;port=3306;dbname=company']
        ['dsn' => 'mysql:host=127.0.0.4;port=3306;dbname=company']
        ['dsn' => 'mysql:host=127.0.0.5;port=3306;dbname=company']
    ],
],

看一个多主多从配置:

'db' => [
    'class' => 'yii\db\Connection',
    'charset' => 'utf8',
    // 主库配置
    'masterConfig' => [ 
        'username' => 'root',
        'password' => 'root',
        'attributes' => [
            PDO::ATTR_TIMEOUT => 5
        ]
    ],
    'masters' => [ //主库列表  配置单项
        ['dsn' => 'mysql:host=192.168.0.1;port=3306;dbname=company'],
        ['dsn' => 'mysql:host=192.168.0.2;port=3306;dbname=company'],
    ],
    // 从库配置
    'slaveConfig' => [
        'username' => 'root',
        'password' => 'root',
        'attributes' => [
            PDO::ATTR_TIMEOUT => 10
        ]
    ],
    'slaves' => [
        ['dsn' => 'mysql:host=127.0.0.1;port=3306;dbname=company'],
        ['dsn' => 'mysql:host=127.0.0.2;port=3306;dbname=company'],
        ['dsn' => 'mysql:host=127.0.0.3;port=3306;dbname=company'],
        ['dsn' => 'mysql:host=127.0.0.4;port=3306;dbname=company'],
        ['dsn' => 'mysql:host=127.0.0.5;port=3306;dbname=company'],
    ],
],

可以看到,是连接的不同服务器IP。服务器上mysql读写分离的配置看这里:

http://www.yii-china.com/post/detail/283.html

数据库做了读写分离,可不能卡在项目程序里,所以项目相应的读写处也要做读写分离。

models中按库建立文件夹生成model文件。sources中按读写分离建文件夹。

比如读的部分,引入model然后具体读取:

写的,就只负责写入和更新:

如果遇到必须查主库的情况,也有对应方法示例:

public function getKehuInfoById( $id )
{
   return Yii::$app->db->useMaster(function() use ($id) {
        return Kehu::findOne( $id );
    });
}

看一下useMaster源码:

public function useMaster(callable $callback)
{
    $enableSlave = $this->enableSlaves;
    $this->enableSlaves = false;
    $result = call_user_func($callback, $this);
    $this->enableSlaves = $enableSlave;
    return $result;
}

看到这里应该就会发现,只是把 Yii::$app->db->enableSlaves 的属性值改了一下,所以我们也可以这么用:

Yii::$app->db->enableSlaves = false;
$res = Kehu::findOne( $id );
Yii::$app->db->enableSlaves = true;

现在有一种情况是数据库主从同步存在延迟,如果在一个程序线程中存入后就要读取这条数据怎么办呢?数据肯定读不到,只有主库有。 还有一种情况是,数据存入后,通过异步程序读取,现在就取决于数据库主从同步的速度和异步调用的速度中谁快了。这不是人为能把握的: 如果读从库,数据可能还没同步过来,导致读不到数据;如果指定读主库,主库压力还是没解决,那我们设置数据库主从就没有意义了。 我们需要一种解决方案。

其实我们可以这样操作:我们先读从库,如果读不到数据,再去读主库,这样就增加了灵活性,而且照顾了初衷。

如下:

public function getKehuInfoById($id)
{
    $model = Kehu::findOne($id);
    if (empty($model)) {
        $model = Yii::$app->db->useMaster(function() use ($id) {
             return Kehu::findOne($id);
         });
    }
    
    return $model;
}






参考资料

http://blog.sina.com.cn/s/blog_6aba78b40102xc3v.html

http://www.yiichina.com/doc/guide/2.0/db-dao

https://blog.csdn.net/qmhball/article/details/53668113

http://www.yii-china.com/doc/yii2-windows.html?id=284

http://www.yii-china.com/post/detail/283.html


返回