详解yii2实现分库分表的方案与思路(2)

在分表逻辑基础上稍作改造,即可实现分库。假设分8张表,那么分别是00,01,02,03…07,然后决定分4个库,那么00,01表在00库,02,03表在01库,04,05表在02库,06,07表在03库,根据这个规律对应的计算代码如上。最终ActiveRecord生效的代码都会类似于”select * from wordpress0.order_info1″,这样就可以解决连接dbproxy访问多库的需求了。

那么yii直接访问多Mysql实例怎么做呢,其实类似tableName() ,我们只需要覆盖getDb()方法即可,同时要求我们首先配置好4个mysql实例,从而可以通过yii的application通过IOC设计来生成多个db连接,所有改动如下:

先配置好4个数据库,给予不同的component id以便区分,它们连接了不同的mysql实例,其中dsn里的dbname只要存在即可(防止PDO执行use database时候不存在报错),真实的库名是通过tableName()动态变化的。

'db0' => [ 'class' => 'yii\db\Connection', 'dsn' => 'mysql:host=10.10.10.10;port=6184;dbname=wordpress0', 'username' => 'wp', 'password' => '123', 'charset' => 'utf8', // 'tablePrefix' => 'ktv_', ], 'db1' => [ 'class' => 'yii\db\Connection', 'dsn' => 'mysql:host=10.10.10.11;port=6184;dbname=wordpress2', 'username' => 'wp', 'password' => '123', 'charset' => 'utf8', // 'tablePrefix' => 'ktv_', ], 'db2' => [ 'class' => 'yii\db\Connection', 'dsn' => 'mysql:host=10.10.10.12;port=6184;dbname=wordpress4', 'username' => 'wp', 'password' => '123', 'charset' => 'utf8', // 'tablePrefix' => 'ktv_', ], 'db3' => [ 'class' => 'yii\db\Connection', 'dsn' => 'mysql:host=10.10.10.13;port=6184;dbname=wordpress6', 'username' => 'wp', 'password' => '123', 'charset' => 'utf8', // 'tablePrefix' => 'ktv_', ],

覆写getDb()方法,根据库下标返回不同的数据库连接即可。

class OrderInfo extends \yii\db\ActiveRecord { private static $databaseIndex_ = null; // 分库ID private static $partitionIndex_ = null; // 分表ID /** * 重置分区id * @param unknown $uid */ private static function resetPartitionIndex($uid = null) { $databaseCount = \Yii::$app->params['Order']['databaseCount']; $partitionCount = \Yii::$app->params['Order']['partitionCount']; // 先决定分到哪一张表里 self::$partitionIndex_ = $uid % $partitionCount; // 再根据表的下标决定分到哪个库里 self::$databaseIndex_ = intval(self::$partitionIndex_ / ($partitionCount / $databaseCount)); } /** * 根据分库分表,返回库名.表名 */ public static function tableName() { $database = 'wordpress' . self::$databaseIndex_; $table = 'order_info' . self::$partitionIndex_; return $database . '.' . $table; } /** * 根据分库结果,返回不同的数据库连接 */ public static function getDb() { return \Yii::$app->get('db' . self::$databaseIndex_); }

这样,无论是yii连接多个mysql实例,还是yii连接1个dbproxy,都可以实现了。

网上有一些例子,试图通过component的event机制,通过在component的配置中指定onUpdate,onBeforeSave等自定义event去hook不同的DAO操作来隐式(自动)的变更database或者connection或者tablename的做法,都是基于model object才能实现的,如果直接使用model class的类似updateAll()方法的话,是绕过DAO直接走了PDO的,不会触发这些event,所以并不是完备的解决方案。

这样的方案原理简单,方案对框架无侵入,只是每次DB操作前都要显式的resetPartitionIndex($uid)调用。如果要做到用户无感知,那必须对ActiveRecord类进行继承,进一步覆盖所有class method的实现以便插入选库选表逻辑,代价过高。

补充:关于分库分表的一些实践细节,分表数量建议2^n,例如n=3的情况下分8张表,然后确定一下几个库,库数量是2^m,但要<=表数量,例如这里1个库,2个库,4个库,8个库都是可以的,表顺序坐落在这些库里即可。
为什么数量都是2指数,是因为如果面临扩容需求,数据的迁移将方便一些。假设分了2张表,数据按uid%2打散,要扩容成4张表,那么只需要把表0的部分数据迁移到表2,表1的部分数据迁移到表3,即可完成扩容,也就是uid%2和uid%4造成的迁移量是很小的,这个可以自己算一下。

总结

以上就是关于yii2实现分库分表的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

您可能感兴趣的文章:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/f72502450aca92d5841c97761ef5415a.html