MySQL水平分表

业务表增长速度较快,单表数据较大,对表的读写有影响。
思路:化整为零,把单表拆解为多表,按指定的算法规则选择表。
好处:能大幅降低单表的数据,读写更快,同时分散了表数据,
SQL语句也分散到不同的表中,可以大幅降低 “锁表” 的情况
以Laravel项目为例,分表读取代码如下: 
public
function changeTable($companyId) { // 分表算法,根据companyId选择分表,请勿修改算法,以免混淆分表数据 $posfix = intval($companyId) % 10; $tableName = $posfix ? "demo_$posfix" : 'demo'; return $this->setTable($tableName); } 调用方法: public function countCompanyData($companyId, $condition) { return $this->changeTable($companyId)->multiwhere($condition)->count(); } 算法实现后,就差表数据迁移了

步骤一,先执行 SHOW CREATE TABLE `demo`; 得到原始表的创建语句,然后,加个下标,就是分表的创建语句了 步骤二,创建所需要的所有分表,需要创建 1~9 编号的分表,这里以 编号1 为例 CREATE TABLE `demo_1` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', `company_id` int(11) unsigned NOT NULL COMMENT '商家ID', `content` varchar(255) NOT NULL COMMENT '内容', `phone` varchar(11) NOT NULL COMMENT '手机号' PRIMARY KEY (`id`), KEY `company_id` (`company_id`), KEY `Phone` (`phone`,`company_id`), ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='演示表'; 步骤三,创建好分表后,执行迁移语句,将原始表(demo表)的数据按分表规则(商家ID取模)迁入对应表(demo_x表) INSERT INTO `demo_1` SELECT * FROM `demo` WHERE MOD(`company_id`, 10) = 1; INSERT INTO `demo_2` SELECT * FROM `demo` WHERE MOD(`company_id`, 10) = 2; INSERT INTO `demo_3` SELECT * FROM `demo` WHERE MOD(`company_id`, 10) = 3; INSERT INTO `demo_4` SELECT * FROM `demo` WHERE MOD(`company_id`, 10) = 4; INSERT INTO `demo_5` SELECT * FROM `demo` WHERE MOD(`company_id`, 10) = 5; INSERT INTO `demo_6` SELECT * FROM `demo` WHERE MOD(`company_id`, 10) = 6; INSERT INTO `demo_7` SELECT * FROM `demo` WHERE MOD(`company_id`, 10) = 7; INSERT INTO `demo_8` SELECT * FROM `demo` WHERE MOD(`company_id`, 10) = 8; INSERT INTO `demo_9` SELECT * FROM `demo` WHERE MOD(`company_id`, 10) = 9; 步骤四,备份原始表(demo表)的数据 步骤五,删除已迁移的数据,留下属于 编号0数据 DELETE FROM `demo` WHERE MOD(`company_id`, 10) != 0;

P.S:修改后所有的SQL操作,都应该含有类似语句 WHERE company_id = xxx ,这样才能找到具体的分表。 如果业务中有和company_id无关的汇总类的查询,那么可以所有分表循环再汇总信息,以统计所有记录为例 public function countTables() { $posfixList = range(0, 9); $sum = 0; foreach ($posfixList as $posfix) { $tableName = $posfix ? "demo_$posfix" : 'demo' ; $count = $this->setTable($tableName)->count(); } $sum += $count; return $sum; }

写在最后:
如果你的数据量特别的大,分表数据迁移需要十多个小时的,那么这期间的数据有可能不会在 insert into ... select 中被迁移
因此最稳妥的方法应该使用如下思路:
1、假定您要在 2023.10.3 的某个时间点上线项目
2、那么您需要在上一天(2023.10.2)完成第一阶段的分表数据迁移,考虑到数据量太大,因此第一阶段的分表数据迁移可能要十几个小时
因此您需要在10.2找运维的小伙伴迁移数据到分表中,将前两天及之前更早的数据迁移完毕,也就是2023-10-1 23:59:59及其之前的所有数据都迁移到分表中
3、完成第一阶段的分表数据迁移了,现在开始第二阶段的迁移吧
找个没人用的时间段发布代码(避免过程中有数据新增),代码发布当天(2023.10.3),
迁移这两天的数据(2023-10-2 00:00:00 ~ 此刻)的所有数据到分表中,一天多的数据量而已,几分钟就搞定了

总结:以上操作,可以避免重复同步数据,避免唯一键冲突,同时可以完整的迁移所有数据,过程中服务无中断
P.S:MySQL使用delete语法删除数据,是不会释放存储空间的,因此新的分表和原表最好名称不同,迁移完成后,可以直接 DROP TABLE 删除原表,释放存储空间
posted @ 2019-08-28 16:12  Funsion Wu  Views(573)  Comments(0Edit  收藏  举报