分表如何保证丝滑?------ 从一个「纯新增」场景聊到大厂主流方案
最近搞了个设备数据分表的需求,踩了点小坑,顺便思考了下大厂的主流玩法,整理记录一下。
一、 起因:一个「无删改」的简单分表需求
我们项目有个设备数据表,之前数据量不大,单表+索引+分区足以解决。 但随着设备数上去了,数据越堆越多。 某天一看已经2000w条设备数据了
领导一看,说做分表!
我稍加思索了下分表核心要保证的问题,一是保证数据不丢失,二是保证数据一致性,而我们的这个设备表,只做数据记录,没有修改,没有删除
那么这个场景就很纯粹了:
- 设备表只有 新增 + 查询 操作,没有更新、没有删除,完美避开了最麻烦的数据变更问题
- 要把老库的历史数据同步到新库分表,同时保证新数据双写不丢
- 要求 零业务感知、零丢数、可重试
我的「丝滑」解决方案思路
既然是纯新增场景,那就没必要搞复杂的中间件,我直接用「双写 + 存量同步 + 灰度切换」,步骤简单直接:
- 分表规则:我们的数据表基于设备id划分,所以分表是采用业务id的方式,之前有了解过也可以按照日期规则来分表,具体可看业务需求选择合适场景
- 先上双写:core 业务包先升级,写入老库的同时,双写到新库的分表(新库只写不读,不影响业务)
- 存量同步 :写个定时任务,我们的id是自增 的,所以我采用了
ID 分页+创建时间过滤的方式同步历史数据。用 Redis 存同步断点 ID,支持断点续跑 - 灰度切换:存量同步完成后,先切测试账户查新库,验证数据一致;没问题再全量切换,最后删掉老库写入逻辑,完事
- 预建分表 + INSERT IGNORE 兜底 :同步前先统计老库的设备类型和 ID,提前建好新库的分表;批量插入用
INSERT IGNORE,就算偶尔有重复数据也能自动忽略,不怕同步重试翻车。
这套方案的核心就是 「用时间戳来分别处理存量和增量数据」 ------ 双写启动时间点之前的走存量同步,之后的走业务双写,两者完全不重叠,理论上不会丢数据。
二、 踩坑:SUM (TABLE_ROWS) 真是个「坑货」
同步任务跑完,我信心满满去核对数据量,结果傻眼了:新库分表总和比老库筛选后的数据少了 6.5 万条!
第一反应是:完了,同步漏数据了?赶紧去查代码日志,没报错啊,断点续跑也正常。折腾了半天,才发现是统计方式的锅!
我一开始统计新库数据量用的是这句 SQL:
sql
sql
SELECT SUM(TABLE_ROWS) FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'gps_data' AND TABLE_NAME LIKE 'gps_data_%';
后来才想起来,INFORMATION_SCHEMA.TABLES 里的 TABLE_ROWS 是 MySQL 的估算值,不是精准数!尤其是分表多、批量插入频繁的场景,误差能到 10%~30%,6.5 万的差距就是这么来的。
最终解决:UNION ALL 才是王道
想要精准统计分表总数,别偷懒,要么写个存储过程遍历所有分表 COUNT(*),要么直接用 UNION ALL 手动统计:
sql
sql
SELECT COUNT(*) FROM (
SELECT id FROM gps_data_1_1001 UNION ALL
SELECT id FROM gps_data_2_1002 UNION ALL
-- 把所有分表列出来
) AS temp;
这么一查,新库和老库的数据量就对上了。
三、 其他思考:如果有删改,又该如何解决?
这个场景虽然不算特别有深度,但是也比纯业务场景有其他值得思考的点。
更加复杂的业务场景靠我这个方案肯定是行不通的,于是去研究了下大厂的主流分表方案,发现核心都是 「存量同步 + Binlog 实时同步 + 业务双写兜底」
小场景 vs 大厂场景:核心差异对比
| 对比维度 | 我的纯新增小场景 | 大厂有删改复杂场景 |
|---|---|---|
| 核心难点 | 无删改,只需保证新增不丢 | 删改频繁,要保证数据实时一致 |
| 同步核心 | 业务双写 + 存量 ID 分页同步 | Binlog 实时同步 + 存量同步 + 双写兜底 |
| 关键技术 | Redis 断点、INSERT IGNORE 去重 | Canal/Maxwell 解析 Binlog、最终一致性校验 |
| 数据一致性 | 绝对一致(纯新增无冲突) | 实时一致(毫秒级延迟) |
| 改造成本 | 低,复用现有代码 | 中,需要引入中间件 |
大厂方案的核心逻辑:Binlog 才是灵魂
大厂玩分表,很少只靠业务双写,核心是抓数据库的 Binlog 日志 ------ 这玩意儿相当于数据库的「操作流水账」,所有增删改操作都记在上面。
用 Canal 这类中间件伪装成 MySQL 从库,实时读取 Binlog,解析出操作内容,再同步到新库的对应分表。这种方式 零业务侵入,不用改一行业务代码,还能捕获所有数据变更,包括 DBA 手动改的数据。
当然,大厂也会保留业务双写作为兜底,万一 Binlog 同步出问题,双写能保证数据不丢,最后再用补偿任务做一致性校验,完美闭环。
五、 感受
- 分表/数据迁移是重中之重,测试用例,各种情况一定要考虑周全
- 提供灰度方案,在出问题时无缝回退,最大程度减少切换时对用户的影响
- 分析复杂问题,拆分简化,一步一步执行