tidb数据一致性校验实现:sync-diff-inspector优化方案(代码片段)

TiDB_PingCAP TiDB_PingCAP     2023-02-14     582

关键词:

简介

在数据同步的场景下,上下游数据的一致性校验是非常重要的一个环节,缺少数据校验,可能会对商业决策产生非常负面的影响。。Sync-diff-inspector 是 Data Platform 团队开发的一款一致性校验工具,它能对多种数据同步场景的上下游数据进行一致性校验,如多数据源到单一目的(mysql 中分库分表到 TiDB 中)、单一源到单一目的( TiDB 表 到 TiDB 表)等,在数据校验过程中,其效率和正确性是至关重要的。首先我们看下 Sync-diff-inspector 的架构图,对 Sync-diff-inspector 的作用和实现原理有一个大致的认知。

Sync-diff-inspector 2.0 架构图

Why Sync-diff-inspector 2.0?

在 1.0 版本中,我们遇到客户反馈的一些问题,包括:

  • 针对大表进行一致性校验时出现 TiDB 端发生内存溢出。
  • 不支持 Float 类型数据校验的问题。
  • 结果输出对用户不友好,需要对校验结果进行精简。
  • 检验过程中发生 GC,导致校验失败。

造成以上问题的原因与原版的实现方式有关

  • 采用单线程划分 Chunk,该表中所有已被划分的 Chunk 需要等待该表中所有 Chunk 全部被划分才会开始进行比对,这会导致这段时间内,TiKV 的使用率降低
  • Checkpoint 功能将校验过的每个 Chunk 的状态写入数据库,所以写入数据库的 IO 成为校验过程的瓶颈。
  • 当 chunk 范围内的 checksum 不同时,直接进行按行比对,消耗大量 IO 资源。
  • 缺少自适应 GC 的功能,导致正在校验的 Snapshot 被 GC,使得校验失败。

Sync-diff-inspector 2.0 新特性

Chunk 划分

对于比较两个表数据是否相同,可以通过分别计算两个表的 checksum 来判断,但是确定哪一行出现了不同则需要逐行比对。为了缩小 checksum 不一致时需要进行逐行比对的行数, Sync-diff-inspector 采用了折衷的方案:将表按照索引的顺序划分成若干块(chunk),再对每个 chunk 进行上下游数据比对。

chunk 的划分沿用了之前的方法。TiDB 统计信息会以索引作为范围将表划分为若干个桶,再对这些桶根据 chunk 的大小进行合并或切分。切分过程则选择随机行作为范围。

原版 Sync-diff-inspector 采用单线程划分 chunk,已被划分的 chunk 需要等待该表划分完所有 chunk 才会开始比对,这里采用异步划分 chunk 的方法来提高这段时间的资源利用率。这里有两种降低资源利用率的情况:

  1. chunk 划分过程中可能由于 chunk 的预定大小小于一个桶的大小,需要切分这个桶为若干个 chunk,这是个相对比较慢的过程,因此消费端也就是 chunk 的比对线程会出现等待的情况,资源利用率会降低。这里采用两种处理方法:采用多个桶异步划分来提高资源利用率;有些表没有桶的信息,因此只能把整个表当作一个桶来切分,采用多表划分来提高总体的异步划分桶数。
  2. chunk 的划分也会占用一定的资源,chunk 划分过快会一定程度减慢 chunk 比对的速度,因此这里在消费端通过 channel 来限制多表划分chunk的速度。

总结来说,优化后的 Sync-diff-inspector 对 chunk 的划分由三部分组成。如下图所示,这里指定存在 3 个 chunk_iter,每个 chunk_iter 划分一个表,这里通过全局的 channel 调整 chunks_iter 划分的进度。注意这里只按表限流,每个 chunk_iter 开始划分时,会异步划分所有 chunk,当全局的 channel 的 buffer 满了,chunk_iter 会阻塞。当 chunk_iter 的所有 chunk 都进入全局 channel 的 buffer 后,该 chunk_iter 会开始划分下一个表。

Checkpoint 和修复 SQL

Sync-diff-inspector 支持在断点处继续进行校验的功能。Diff 进程每十秒钟会记录一次断点信息,当校验程序在某个时刻发生异常退出的时候,再次运行 Sync-diff-inspector 会从最近保存的断点处继续进行校验。如果在下一次运行时,Sync-diff-inspector 的配置文件发生改变,那么 Sync-diff-inspector 会抛弃断点信息,重新进行校验。

该功能的完整性和正确性依赖于在 Chunk 划分过程中定义的全局有序性和连续性。相比于原版,Sync-diff-inspector 2.0 实现的 checkpoint 不需要记录每个 chunk 的状态,只需要记录连续的、最近校验完成的 chunk 的状态,大大减少了需要记录的数据量。chunk 的全局有序特性由一个结构体组成,结构体包含了该 chunk 属于第几个表,属于该表的第几个桶到第几个桶(如果该 chunk 由两个或多个桶合并而成,则记录桶的首末),这个桶被切分成多少个 chunk,这个 chunk 是切分后的 chunk 的第几个。同时这种特性也可以判断两个 chunk 是不是连续的。每次断点时钟触发时,会选择已完成比对的连续的 chunk 的最后一个 chunk 作为检查点,写入该 chunk 的信息到本地文件。

当校验出不同行时,Sync-diff-inspector 会生成修复 SQL 并保存在本地文件中。因为检验的 chunk 是乱序且并行的,所以这里为每个 chunk 创建(若该 chunk 存在不同行)一个文件来保存修复 SQL,文件名是该 chunk 的全局有序的结构体。修复 SQL 和 checkpoint 的记录肯定存在先后顺序:

  1. 如果先写入修复 SQL 的记录,那么此时程序异常退出,这个被写入修复 SQL 但没被 checkpoint 记录的 chunk 会在下一次生成,一般情况下,这个修复 SQL 文件会被重新覆盖。但是由于桶的切分是随机分的,因此尽管切分后的 chunk 个数固定,上一次检查出的不同行在切分后 chunks 的第三个,这次可能跑到了第四个chunk 的范围内。这样就会存在重复的修复 SQL。
  2. 如果先写入 checkpoint,那么此时程序异常退出,下一次执行会从该 checkpoint 记录的 chunk 的后面范围开始检验,如果该 chunk 存在修复 SQL 但还没有被记录,那么这个修复 SQL 信息就丢失了。

这里采用了先写入修复 SQL 记录,下一次执行时会将排在 checkpoint 记录的 chunk 后的所有修复 SQL 文件(文件是以该 chunk 的全局有序结构体命名,因此可以很容易判断两个 chunk 的先后顺序)都移到 trash 文件夹中,以此避免出现重复的修复 SQL

二分校验和自适应 chunkSize

大表做 checksum 和切分成 chunks 做 checksum 的性能损耗在于每次做 checksum 都会有一些额外消耗(包括一次会话建立传输的时间),如果把 chunk 划分的很小,那么这些额外消耗在一次 checksum 花费的时间占比会变大。通常需要把 chunk 的预定大小 chunkSize 设置大一些,但是 chunkSize 设置的过大,当上下游数据库对 chunk 做 checksum 的结果不同时,如果对这个大 chunk 直接进行按行对比,那么开销也会变得很大。

在数据同步过程中,一般只会出现少量的数据不一致,基于这个假定,当校验过程中,发现某个 chunk 的上下游的 checksum 不一致,可以通过二分法将原来的 chunk 划分成大小接近的两个子 chunk,对子 chunk 进行 checksum 对比,进一步缩小不一致行的可能范围。这个优化的好处在于,checksum 对比所消耗的时间和内存资源远小于逐行进行数据比对的消耗,通过 checksum 对比不断的缩小不一致行的可能范围,可以减少需要进行逐行对比的数据行,加快对比速度,减少内存损耗。并且由于每次计算 checksum 都相当于遍历一次二分后的子 chunk,理论上不考虑多次额外消耗,二分检验的开销相当于只对原 chunk 多做两次 checksum。

由于做一次 checksum 相当于遍历范围内的所有行,可以在这个过程中顺便计算这段范围的行数。这样做是因为 checksum 的原理是对一行的数据进行 crc32 运算,再对每一行的结果计算异或和,这种 checksum 的无法校验出三行重复的错误,在索引列不是 unique 属性的情况下是存在这种错误的。同时计算出每个 chunk 的行数,可以使用 limit 语法定位到该 chunk 的中间一行数据的索引,这是二分方法使用的前提。

但是 chunkSize 也不能设定的过大,当一次二分后两边的子 chunk 都存在不同行,那么会停止二分,进行行比对。过大的 chunk 就更有可能同时包含多个不同行,二分校验的作用也会减小。这里设置每张表默认的 chunkSize 为 50000 行,每张表最多划分出 10000 个 chunk。

索引处理

上下游数据库的表可能会出现 schema 不同,例如下游表只拥有一部分上游的索引。不恰当的索引的选择会造成一方数据库耗时加大。在做表结构校验时,只保留上下游都有的索引(若不存在这种索引,则保留所有索引)。另一方面,某些索引包含的列并不是 unique 属性的,可能会有大量的行拥有相同的索引值,这样 chunk 会划分的不均匀。Sync-diff-inspector 在选择索引时,会优先选择 primary key 或者 unique 的索引,其次是选择重复率最低的索引

where 处理

假设存在一张表 create table t (a int, b int, c int, primary key (a, b, c));

并且一个划分后的 chunk 范围是 ((1, 2, 3), (1, 2, 4)]

原版 Sync-diff-inspector 会生成 where 语句:

  • ((a > 1) OR (a = 1 AND b > 2) OR (a = 1 AND b = 2 AND c > 3))
  • ((a < 1) OR (a = 1 AND b < 2) OR (a = 1 AND b = 2 AND c <= 4))

可以优化为 (a = 1) AND (b = 2) AND ((c > 3) AND (c <= 4))

自适应 GC

在原版 Sync-diff-inspector 中,校验过程中可能会出现大量表被 GC 导致校验失败。Sync-diff-inspector 工具支持自适应 GC 的功能,在 Diff 进程初始化阶段启动一个后台 goroutine,在检验过程中不断的更新 GC safepoint TTL 参数,使得对应的 snapshot 不会被 GC,保证校验过程的顺利进行。

处理 Float 列

根据 float 类型的特性,有效精度只有6位,因此在 checksum SQL 中对 float 类型的列使用 round(%s, 5-floor(log10(abs(column)))) 取 6 位有效数字作为 checksum string 的一部分,当 column 取特殊值为 0 时,该结果为 NULL,但是 ISNULL(NULL) 也作为 checksum string 的一部分,此时不为 true,这样可以把 0 和 NULL 区分开来。

用户交互优化

Sync-diff-inspector 显示如下信息:

  • 将日志写入到日志文件中。
  • 在前台显示进度条,并提示正在比较的表。
  • 记录每个表校验相关结果,包括整体对比时间、对比数据量、平均速度、每张表对比结果和每张表的配置信息。
  • 生成的修复 SQL 信息。
  • 一定时间间隔记录的 checkpoint 信息。

其效果如下图:

具体细节可参考 overview

性能提升

基于以上的优化手段,我们进行了性能测试,在 Sysbench中, 构造 668.4GB 数据,共 190 张表,每张表一千万行数据,测试结果如下:

从测试结果可以看出,Sync-diff-inspector 2.0 相比于原版, 校验速度有明显提升,同时在TiDB 端内存占用显著减少

未来展望

开放性的架构

在 Sync-diff-inspector 中我们定义了 Source 抽象,目前只支持 TiDB 端到 TiDB 端,MySQL 端到 MySQL 端以及 MySQL 端到 TiDB 端的数据一致性校验,但是在未来,通过实现 Source 对应的方法,可以适配多种其他数据库进行数据一致性校验,例如 Oracle, Aurora 等。

支持更多类型

由于部分列类型特殊,目前 sync-diff-inspector 暂不支持(例如 json,bit,binary,blob )。需要在 checksum SQL 语句中对它们特殊处理,例如对于 json 类型的列,需要通过 json_extract 提取出现在 json 中的每一个 key 的值。

更激进的二分 checksum

新版 sync-diff-inspector 采用二分 checksum 方法来减小逐行比对的数据量,但是在发现二分后的两个 chunk 都存在不一致数据时就停止继续二分,进行逐行比对。这种方法比较悲观,认为此刻 chunk 可能存在多个不一致的地方。但是根据实际情况,sync-diff-inspector 的应用场景一般是只存在少量不一致的情况,更加激进的做法是,继续二分,最后得到的是一组拥有最小行数(默认 3000 行)的且存在不一致数据的 chunk 数组,再对这些数组分别进行逐行比对。

tidb一个大数据实时计算的存储利器(代码片段)

...节点负责一部分数据的存储和处理。它支持ACID事务、强一致性和高可用性,并提供灵活的配置选项以适应各种工作负载需求。PD:是TiDB的元数据管理组件 查看详情

技术分享两个单机mysql该如何校验数据一致性

参考技术A业务有两个MySQL集群是通过MQ进行同步的,昨晚MQ出现异常,报了很多主键冲突,想请dba帮忙校验一下两个集群的数据是否一致。当接到这个需求的时候并没当回事,隐约有点印象pt-table-checksum能通过dsn实现MySQL的数据校... 查看详情

复制数据一致性校验

复制数据一致性校验借鉴:https://segmentfault.com/a/1190000004309169mysql学习:http://www.itdks.com/dakashuo/playback/267怎么保证数据复制一致半同步(5.7losszeroreplication)异步类的复制:1、要安装规范来2、要敏锐的观察力3、对于异常切换要有数据... 查看详情

tidb-使用ticdc将数据同步至下游mysql中(代码片段)

一、TICDCTiCDC是一款通过拉取TiKV变更日志实现的TiDB增量数据同步工具,具有将数据还原到与上游任意TSO一致状态的能力,同时提供开放数据协议(TiCDCOpenProtocol),支持其他系统订阅数据变更。和前面学习的Tidbbinlog不同&... 查看详情

技术分享mysql表数据校验

...用MySQL的黑洞引擎,改下SQL如下:对于表要计算校验数据一致性的需求,首选第二种自己写SQL的方法。 查看详情

htapx云原生:tidb加速释放数据价值,实现数据敏捷

2022年6月28日,企业级开源分布式数据库厂商PingCAP举行“TiDBV6暨PingCAP云战略发布会”,发布了TiDBV6版本,分享了TiDB技术演进路线,并与合作伙伴一起解读了云原生时代的数据库解决方案和生态战略,为广大MySQ... 查看详情

tidb的数据类型查询问题

...样查询会很慢select*fromtablewherenamein('445','666')数据类型一致时会很快 查看详情

tidb-使用tidbbinlog实现数据复制(代码片段)

...解为Mysql的Binlog主从服务模式,并且TiDBBinlog还支持将数据发送到Kafka中,这又类似与Canal中间件。目前TIDBBinlog集群主要分为Pump和Drainer两个组件,以及binlogctl工具。TiDBBinlog整体架构注意:TiDBBinlog与TiDBv5.0版本开始... 查看详情

tidb初级课程体验2(分布式数据库引擎)

TIDB在TIKV中的数据逻辑表的呈现是一个需要学习的地方,TIKV中行的信息是通过keyvalue来组成的,而在逻辑和物理之间进行实现的过程中tidb做了如下的工作。1 存储的表必须有主键,通过主键也就是ROW_ID来实现一个表的逻辑有... 查看详情

windows下拷贝数据,校验数据一致性的脚本

脚本中使用的md5sum工具下载地址如下:http://www.pc-tools.net/win32/md5sums/#!/usr/bin/python#coding=utf-8importosimportshutil#获取某个文件的md5值defgetmd5(dir):cmd=‘C:\\Users\\Administrator\\Desktop\\md5sum.exe‘+‘‘+dirmd5sum 查看详情

极简实现tidb冷热数据分层存储|he3团队访谈

参加Hackathon可以接触到内核、工具、生态各个领域中志同道合的小伙伴,通过他们的项目学习到非常好的创意。大家的想法都很奇妙,充满了创新力,在平时的研发过程中,很少能接触到这些,Hackathon能够帮... 查看详情

zabbix+tidb:可实现水平扩展(代码片段)

什么是tidbTiDB是开源分布式关系型数据库,是一款同时支持在线事务处理与在线分析处理的融合型分布式数据库产品。具备水平扩容或者缩容、金融级高可用、实时HTAP、云原生的分布式数据库、兼容MySQL5.7协议和MySQL生态等重... 查看详情

建议收藏7000+字的tidb保姆级简介,你见过吗(代码片段)

TIDB简介什么是TIDBTiDB是一个分布式NewSQL数据库。它支持水平弹性扩展、ACID事务、标准SQL、MySQL语法和MySQL协议,具有数据强一致的高可用特性,是一个不仅适合OLTP场景还适合OLAP场景的混合数据库。TiDB是PingCAP公司自主设计、研发... 查看详情

数据质量

...数据模型数据分布2、执行质量度量完整性准确性唯一性一致性及时性有效性 (单列、跨列、跨行、跨表)数据质量设计原则1、关键环节注入监控流程比如数据源接入、多维模型等2、重要数据:事务数据、主数据、基础数据... 查看详情

大数据大数据组件tidb原理+实战篇(代码片段)

文章目录1.TiDB引入1.1.数据库技术发展简史1.2.从MySQL到TiDB1.3.TiDB概述1.4.数据库种类简介2.TiDB架构特性2.1.TiDB整体架构2.2.TiDB核心特性2.3.存储和计算能力3.TiDB安装部署3.1.TiDB-Local单机版3.2.TiDB-Docker集群版4.TiDB实践案例4.1.TiDB-SQL操作4.2... 查看详情

利用pt-table-checksum校验数据一致性(代码片段)

...MySQL主服务器的数据和从服务器的数据是否一致呢,数据一致性才是最重要的,有人很好奇的问,如果数据不一致,就肯定没有两个YES的出现啦,我想说,不一定的,因为当slave出现错误时,可以通过SETGLOBALsql_slave_skip_counter=N来... 查看详情

tidb查询优化及调优系列tidb查询计划简介(代码片段)

...化器的优化流程。查询计划(executionplan)展现了数据库执行SQL语句的具体步骤,例如通过索引还是全表扫描访问表中的数据,连接查询的实现方式和连接的顺序等。查阅及 查看详情

主从校验工具pt-table-checksum和pt-table-sync工作原理

  pt-table-checksum和pt-table-sync是常用来做MySQL主从数据一致性校验的工具,pt-table-checksum只校验数据,不能对数据进行同步;pt-table-sync不仅可以检验数据,还可以将不一致的数据同步。工作原理  两个工具的工作原理基本差不... 查看详情