作者:vivo 互联网存储研发团队 - Shang Yongxing
本文介绍了当前DTS应用中,MySQL数据同步使用到的数据一致性校验工具,并对它的实现思路进行分享。
一、背景
在 MySQL 的使用过程中,经常会因为如集群拆分、数据传输、数据聚合等原因产生流动和数据复制。而在通常的数据复制过程中,因为涉及到目标的写入不可控、服务应用的未知问题、人为导致的异常缺陷等,很难保证复制产生的数据与源完全一致。除了通过完善流程与服务应用的能力和可靠性来保障数据一致性外,也需要提供快速有效的数据校验机制,便于发现存在异常的数据位置,服务于后续可能的自动重试或人工修订。
而具体到我们目前使用的数据传输服务DTS(MySQL部分),需要考虑的点:
-
端对端从源集群到目标集群的外部数据校验
-
内部数据校验机制,确保同步的数据正确可靠
二、选型参考
数据一致性校验,即对DTS的数据同步任务在目标产生(复制)的表数据,与在源库的原始数据进行对比,并给出对比结果。若存在不一致的情况则给出具体不一致的数据块,方便用户快速对不一致数据进行处理。它的基本原则是作为独立一个环节,既不能影响同步本身,也不能影响业务数据库的正常使用。
为了达成数据一致性的校验,需要具备以下的能力:
-
校验的范围应当包括库表对象的结构、实际行数据已经其它被任务定义包含在内的内容(索引、视图、存储过程等)。
-
校验应当在保证较小地侵入影响数据库的同时,尽快完成涉及数据的对比检查。
-
校验应当具备精确定位不一致数据块的位置的能力,用于支持后续进行的数据修订。
三、端对端的数据一致性校验
3.1 现有问题
在数据传输的场景中,相关的数据大概率分散在无关的不同实例上,这种情况下想要对两端的数据进行对比分析,比较包括结构、索引、列数据等维度,当然可以通过最直接的逐行逐列地遍历各个表方式,这种方式最直观且可以最精确地对比相关的数据,但显然如果数据总量或数据列的规模较大时,这种逐行对比的方式会存在下列问题:
-
执行耗时长
-
结果时效性差,增量场景几乎无法使用
一般来说如果无法接受“全量”性质的扫描带来的时间开销,可以将“全量”转换为“部分”的验证,本质上一些数据同步场景后执行的点检就是数据的部分校验,这种方式完成速度快,可用于关键数据的快速验证,但这种形式也存在明显的问题:
-
数据集覆盖度难以平衡
-
如果确实存在一些不一致的情况,可能会被遗漏
针对这些问题,也许可以采取一些额外的校验保障,例如数据集大小(行数等)的校验,一定程度上可以降低数据不完整的错判风险。
那么回到我们的实际使用场景中,为了解决数据可靠性的问题,我们调研了几种比较主流的MySQL数据校验方案:
pt本身更常被运维用于检查集群内主从的表数据是否一致,这显然与数据传输的场景存在较大的出入,不符合我们的实际需求。
px在实现上更满足数据传输场景的需求,同时包括上述两者在内,一些公用云的MySQL类型数据库的数据一致性校验也是采用类似部分采用数据进行比对的形式:
-
表的数据分块
- 部分支持动态分块大小来调整负载
-
分块级的数据特征计算→简化对比规模
这个思路在数据遍历的完整性和效率方面均做了一定的取舍,相当于是一种中间状态。
而为了达成满足一些定制场景以及提高使用效率的目的,我们最终是采用了px-table-checksum的实现思路来完成的数据校验实现,接下来以一个具体的表结构来介绍进行数据对比的思路。
sysbench在MySQL的压测场景产生的一种表结构如下:
前文我们已经提到虽然直接使用全表逐行对比会存在时间消耗较大的问题,但如果业务属性上源和目标的表发生变化的频率很低(定时更新类),这种校验也是可以发挥作用的:
SELECT id, k, c, pad FROM sbtest1 limit n;
基于这样简易的抽取逻辑进行逐行的比对是可以做到完全校验的,代价就是时间上的开销非常大。
3.2 实现思路
而我们将要采取的分块获取数据的方式则是在这一基础上进行了优化,我们逐步介绍逻辑,首先这里假定使用的分块大小为10行–chunk_size=10
1.数据分块
mysql> SELECT * FROM sbtest1 FORCE INDEX(`PRIMARY`) WHERE ((`id`>= 1) AND ((`id`) SELECT CONCAT_WS('#', `id`, `k`, CRC32(`c`), CRC32(`pad`), CONCAT(ISNULL(`pad`))) AS CRC FROM `sbtest1` FORCE INDEX(`PRIMARY`) WHERE ((`id`>= 1) AND ((`id`) SELECT CRC32(CONCAT_WS('#', `id`, `k`, CRC32(`c`), CRC32(`pad`), CONCAT(ISNULL(`pad`)))) AS CRC FROM `sbtest1` FORCE INDEX(`PRIMARY`) WHERE ((`id`>= 1) AND ((`id`) SELECT GROUP_CONCAT(CRC32(CONCAT_WS('#', `id`, `k`, CRC32(`c`), CRC32(`pad`), CONCAT(ISNULL(`pad`))))) AS CRC FROM `sbtest1` FORCE INDEX(`PRIMARY`) WHERE ((`id`>= 1) AND ((`id`) SELECT CRC32(GROUP_CONCAT(CRC32(CONCAT_WS('#', `id`, `k`, CRC32(`c`), CRC32(`pad`), CONCAT(ISNULL(`pad`)))))) AS CRC FROM `sbtest1` FORCE INDEX(`PRIMARY`) WHERE ((`id`>= 1) AND ((`id`) SELECT md5(GROUP_CONCAT(CRC32(CONCAT_WS('#', `id`, `k`, CRC32(`c`), CRC32(`pad`), CONCAT(ISNULL(`pad`)))))) AS md5 FROM `sbtest1` FORCE INDEX(`PRIMARY`) WHERE ((`id`>= 1) AND ((`id`) mysql(pg,tidb等),DTS支持的其它数据场景(redis->redis/kv)也是类似的情况,对于异构数据(例如订阅),暂时就没有比较好的方案可以做端对端的校验,需要使用方抽检部分或核心数据。
END
猜你喜欢
-
海量数据处理利器 Roaring BitMap 原理介绍
-
TiKV 源码分析之 PointGet
-
分布式任务调度内的 MySQL 分页查询优化
-
HBase Meta 元信息表修复实践