|
在数据修复场景中,"修复容易,回滚困难"一直是个痛点。执行完修复 SQL 后发现结果不符合预期,却没有现成的回退手段。gt-checksum v4.0.0 新增反向回滚 SQL 生成能力,让每一次修复都有"后悔药"。
genRollSQL 是 gt-checksum v4.0.0 新增的核心参数,配合 maxRollRowNum 和 rollFileDir 一起使用,用于在生成修复 SQL 的同时自动生成反向回滚 SQL。
| 参数 | 默认值 | 可选值 | 说明 |
|---|---|---|---|
genRollSQL | OFF | ON / OFF / 自定义表名 | 控制是否生成回滚 SQL |
maxRollRowNum | 10000 | 正整数 | 单表待修复行数超过该阈值时不生成回滚 SQL |
rollFileDir | rollsql | 目录路径 | 回滚 SQL 文件存储目录 |
三种 genRollSQL 模式的区别:
| 模式 | 行为 |
|---|---|
OFF | 不生成回滚 SQL(默认行为) |
ON | 对所有校验的表生成回滚 SQL |
| 自定义表名 | 仅对指定的表生成回滚 SQL,支持逗号分隔多个表名,支持 % 通配符 |
使用方式非常简单,在配置文件 gc.conf 中添加几行:
genRollSQL=ON
rollFileDir=rollsql
maxRollRowNum=10000
在生产环境中执行数据修复,面临的风险远比测试环境复杂:
场景一:审计与合规
在金融、医疗等对数据变更审计要求严格的行业,不仅需要知道"改了什么",还需要知道"怎么改回去"。回滚 SQL 是修复操作可审计性的重要组成部分。
场景二:修复逻辑误判
在复杂的类型映射、字符集转换场景下,校验工具判定的"差异"可能只是格式差异而非真实数据差异(例如 DECIMAL 精度差异、utf8mb4_general_ci vs utf8mb4_0900_ai_ci 的 collation 差异)。修复后业务验证不通过,需要回退。
场景三:修复导致业务中断
修复 SQL 中的 DELETE 或 UPDATE 可能与正在运行的业务事务产生锁冲突,或者修复了业务正在使用的数据行,导致业务报错。此时需要快速回退恢复原状。
gt-checksum 的回滚 SQL 生成采用反向映射策略:对每条修复 SQL,生成一条语义相反的回滚 SQL。
核心映射关系:
| 修复 SQL 类型 | 回滚 SQL 类型 | 说明 |
|---|---|---|
DELETE FROM ... WHERE ... | INSERT INTO ... VALUES(...) | 目标端删除的行,回滚时重新插入 |
INSERT INTO ... VALUES(...) | DELETE FROM ... WHERE ... | 目标端插入的行,回滚时删除 |
TRUNCATE TABLE ... | 特殊处理 | 仅在目标端整表为空时生成(详见 2.3) |
DELETE → INSERT 的转换过程:
修复 SQL 中的 DELETE 语句包含 WHERE 条件,其中的列-值对就是被删除行的数据。回滚生成器解析 WHERE 子句,提取所有列名和对应的值,重新组装为 INSERT INTO 语句:
-- 修复 SQL(DELETE)
DELETE FROM `db1`.`orders` WHERE `id` = 1001 AND `order_no` = 'ORD-20260101';
-- 回滚 SQL(INSERT)
INSERT INTO `db1`.`orders`(`id`,`order_no`) VALUES(1001,'ORD-20260101');
对于有 LIMIT 子句的 DELETE(无主键表场景),回滚生成器会先剥离 LIMIT,再提取 WHERE 条件中的列值对。
INSERT → DELETE 的转换过程:
修复 SQL 中的 INSERT 语句包含列名和值的完整映射。回滚生成器解析列名和值列表,仅使用主键列(或唯一键列)构建 DELETE 的 WHERE 条件:
-- 修复 SQL(INSERT)
INSERT INTO `db1`.`orders`(`id`,`order_no`,`amount`) VALUES(1001,'ORD-20260101',99.50);
-- 回滚 SQL(DELETE,仅使用 PK 列 id)
DELETE FROM `db1`.`orders` WHERE `id` = 1001;
NULL 值的特殊处理:
在 WHERE 条件中,NULL 值不能用 = NULL 表达,回滚生成器会自动转换为 IS NULL:
-- 修复 SQL
DELETE FROM `db1`.`users` WHERE `id` = 500 AND `email` IS NULL;
-- 回滚 SQL
INSERT INTO `db1`.`users`(`id`,`email`) VALUES(500,NULL);
当目标端表在校验开始前整表为空(行数为 0)时,修复 SQL 会是大量 INSERT 语句。如果逐行生成回滚 DELETE,文件会非常大。此时 gt-checksum 采用一种更高效的策略:生成单条 TRUNCATE TABLE 回滚语句,忽略 maxRollRowNum 参数限
制。
<strong>关键安全约束:只有校验开始前已确认目标端整表为空(精确 COUNT(*) 为 0)时,才会生成 TRUNCATE 回滚。程序不会</strong>因为某个 chunk 的目标端为空就推断整张表为空——否则目标端非空但恰好某个 chunk 无数据时,会误生成整表 TRUNCATE
回滚,这将导致回滚时丢失目标端原有的有效数据。
同时,当 TRUNCATE 回滚已生成后,后续的逐行 INSERT 修复对应的回滚 DELETE 会被抑制,避免回滚时先 TRUNCATE 再逐行 DELETE 的重复操作。
⚠️ 重要提醒:TRUNCATE 属于 DDL 操作,执行时会隐式提交当前事务。使用 TRUNCATE 回滚 SQL 前需人工评估其影响。
文件存储结构
回滚 SQL 文件统一存储在 rollFileDir 目录(默认 rollsql)下,文件命名规则为:
rollsql/table.{schema}.{table}.rollback-{TYPE}-{seq}.sql
其中 {TYPE} 为 INSERT、DELETE 或 TRUNCATE,{seq} 为文件序号。例如:
rollsql/table.db1.orders.rollback-INSERT-1.sql
rollsql/table.db1.orders.rollback-DELETE-1.sql
rollsql/table.db1.users.rollback-TRUNCATE-1.sql
文件内容格式
每个回滚 SQL 文件包含完整的事务边界和会话设置:
SET FOREIGN_KEY_CHECKS=0;
SET UNIQUE_CHECKS=0;
BEGIN;
DELETE FROM `db1`.`orders` WHERE `id` = 1001;
DELETE FROM `db1`.`orders` WHERE `id` = 1002;
COMMIT;
BEGIN;
DELETE FROM `db1`.`orders` WHERE `id` = 1003;
COMMIT;
自动滚动切分
当单个回滚 SQL 文件的语句数量或文件大小超过阈值(由 fixTrxNum 和 fixTrxSize 参数控制)时,会自动创建新的文件。这确保了单个文件不会过大,便于人工审计和分批执行。
与 datafix=table 的配合关系
当 datafix=table(在线修复模式)时,修复 SQL 会直接在目标端执行。但回滚 SQL 始终只写文件,不在线执行——这是刻意的安全设计。回滚 SQL 写入 rollFileDir 目录后,需要人工审计确认无误,再通过 repairDB ./rollsql 执行回滚。
对于没有主键和唯一键的表,修复 SQL 中可能出现多条 DELETE ... WHERE ... LIMIT 1 语句,其 WHERE 条件完全相同(因为同一组值可能在目标端出现多次)。gt-checksum 的回滚写入器内置了 mergeDuplicateDeleteLimits 优化:
-- 优化前:3 条相同 WHERE 的 DELETE
DELETE FROM `db1`.`log_data` WHERE `ts` = '2026-01-01' AND `msg` = 'test' LIMIT 1;
DELETE FROM `db1`.`log_data` WHERE `ts` = '2026-01-01' AND `msg` = 'test' LIMIT 1;
DELETE FROM `db1`.`log_data` WHERE `ts` = '2026-01-01' AND `msg` = 'test' LIMIT 1;
-- 优化后:合并为 1 条,LIMIT 累加
DELETE FROM `db1`.`log_data` WHERE `ts` = '2026-01-01' AND `msg` = 'test' LIMIT 3;
这个优化仅在表无主键/唯一键时生效,因为有主键表的每条 DELETE 的 WHERE 条件天然不同。
当 resume=ON 且任务中断后续传时,回滚 SQL 的处理同样遵循安全原则:
COMMIT 边界,截断后续不完整的内容生成回滚 SQL 后,需要人工审计确认无误,然后通过 repairDB 工具执行:
# 审计回滚 SQL 文件
ls -la rollsql/
# 使用 repairDB 执行回滚
repairDB ./rollsql
repairDB 会自动识别目录中的 SQL 文件并按顺序执行,支持断点续传——如果回滚执行过程中断,再次运行会跳过已成功的文件。
在配置文件 gc.conf 中设置相关参数:
# 开启回滚 SQL 生成
genRollSQL=ON
rollFileDir=rollsql
maxRollRowNum=10000
# 其他必要参数
checkObject=data
datafix=file
tables=db1.*
srcDSN=user:ENC[...]@tcp(10.0.0.1:3306)/db1
dstDSN=user:ENC[...]@tcp(10.0.0.2:3306)/db1
chunkSize=10000
$ gt-checksum -c gc.conf
Initializing gt-checksum
Reading configuration files
...
[CHECK] db1.orders: checksum mismatch in chunk 0-10000
[FIX] db1.orders: DELETE 3 rows, INSERT 5 rows
[ROLL] db1.orders: Writing rollback SQL (DELETE=5, INSERT=3)
[CHECK] db1.users: checksum mismatch in chunk 0-10000
[FIX] db1.users: DELETE 1 row, INSERT 0 rows
[ROLL] db1.users: Writing rollback SQL (DELETE=0, INSERT=1)
...
校验完成后,查看回滚 SQL 目录:
$ ls -la rollsql/
total 24
drwxr-xr-x 2 user user 4096 Jun 15 10:30 .
drwxr-xr-x 6 user user 4096 Jun 15 10:30 ..
-rw-r--r-- 1 user user 1256 Jun 15 10:30 table.db1.orders.rollback-DELETE-1.sql
-rw-r--r-- 1 user user 856 Jun 15 10:30 table.db1.orders.rollback-INSERT-1.sql
-rw-r--r-- 1 user user 312 Jun 15 10:30 table.db1.users.rollback-INSERT-1.sql
查看回滚 SQL 内容:
$ cat rollsql/table.db1.orders.rollback-INSERT-1.sql
SET FOREIGN_KEY_CHECKS=0;
SET UNIQUE_CHECKS=0;
BEGIN;
INSERT INTO `db1`.`orders`(`id`,`order_no`,`amount`) VALUES(1001,'ORD-20260101',99.50);
INSERT INTO `db1`.`orders`(`id`,`order_no`,`amount`) VALUES(1002,'ORD-20260102',150.00);
COMMIT;
如果只需要对特定表生成回滚 SQL,可以使用自定义表名模式:
genRollSQL="db1.orders, db1.user%"
这将仅为 db1.orders 和 db1.user 开头的表生成回滚 SQL,其他表不生成。支持 % 通配符(匹配任意字符序列)。
确认需要回退时,使用 repairDB 执行回滚 SQL:
$ repairDB ./rollsql
[REPAIR] Processing: table.db1.orders.rollback-INSERT-1.sql ... OK
[REPAIR] Processing: table.db1.orders.rollback-DELETE-1.sql ... OK
[REPAIR] Processing: table.db1.users.rollback-INSERT-1.sql ... OK
Rollback completed: 3 files executed successfully
当目标端表在校验开始前为空时(例如从零开始的数据迁移验收),回滚 SQL 会生成单条 TRUNCATE TABLE:
$ cat rollsql/table.db1.orders.rollback-TRUNCATE-1.sql
SET FOREIGN_KEY_CHECKS=0;
SET UNIQUE_CHECKS=0;
BEGIN;
TRUNCATE TABLE `db1`.`orders`;
COMMIT;
此时不会为该表生成逐行 DELETE 的回滚文件,因为 TRUNCATE 已经足以清空整张表。
1. 生产环境建议开启回滚 SQL
对于数据修复任务,无论使用 datafix=file(导出修复 SQL 文件)还是 datafix=table(在线修复),都建议开启 genRollSQL=ON。回滚 SQL 的生成开销很小,但在紧急回退场景下价值巨大:
genRollSQL=ON
rollFileDir=rollsql
2. 大表场景适当调整 maxRollRowNum
maxRollRowNum 用于控制单表待修复行数的回滚阈值。默认值 10000 适用于大多数场景。如果表很大但仍然需要回滚保障,可以调高该值:
# 允许单表最多 50000 行差异时也生成回滚 SQL
maxRollRowNum=50000
注意:maxRollRowNum 对 TRUNCATE 回滚不生效——目标端整表为空时始终生成 TRUNCATE 回滚。
3. 仅对关键表生成回滚 SQL
如果不需要对所有表生成回滚 SQL(例如只关心核心业务表),使用自定义表名模式减少文件量:
genRollSQL="db.orders, db.user%, db.account%"
4. 回滚 SQL 执行前务必人工审计
回滚 SQL 始终只写文件、不在线执行。执行前请:
# 1. 查看生成了哪些回滚文件
ls -la rollsql/
# 2. 检查关键表的回滚内容
cat rollsql/table.db.orders.rollback-*.sql
# 3. 确认无误后使用 repairDB 执行
repairDB ./rollsql
5. 回滚后重新校验
执行回滚后,建议重新运行一次校验,确认数据已恢复到修复前的状态:
# 先清空旧的回滚目录(因为这次不需要回滚了)
rm -rf rollsql/
# 重新校验
gt-checksum -c gc.conf
6. 结合 resume 使用时注意回滚文件累积
断点续传模式下,续传时旧的回滚文件会被安全截断或清理后重新生成。但如果多次中断-续传,可能会产生较多的回滚文件。建议在任务正常完成后整理回滚目录,保留最终版本即可。
1. 仅适用于数据校验模式
回滚 SQL 生成仅在 checkObject=data 模式下生效。struct(结构校验)、trigger(触发器校验)、routine(存储过程/函数校验)不支持回滚 SQL 生成。
2. 必须配合 datafix=file 或 datafix=table
只有当 datafix 设置为 file 或 table 时,回滚 SQL 才会被生成。datafix=none(仅校验不修复)模式下不会生成回滚 SQL,因为没有修复操作也就不需要回退。
3. rollFileDir 目录非空时的行为
resume=OFF 模式:如果 rollFileDir 目录已存在且非空,程序会报错退出,避免覆盖旧的回滚文件resume=ON/ASK 模式:允许目录非空,续传逻辑会处理已有文件(截断或清理)4. 无主键表的回滚 INSERT 精度限制
对于没有主键和唯一键的表,回滚 INSERT 仅能基于修复 DELETE 的 WHERE 条件中可用的列值对,可能无法完全还原原始行的所有列值(如果 DELETE 语句的 WHERE 条件不包含所有列)。
5. TRUNCATE 回滚的 DDL 隐式提交风险
TRUNCATE TABLE 是 DDL 操作,执行时会隐式提交当前事务并释放表锁。在使用 TRUNCATE 回滚 SQL 时,需确认该行为不会对业务产生意外影响。建议在业务低峰期执行回滚操作。
6. 配置一致性
回滚 SQL 的目标端表名基于校验时的表映射规则。如果校验和回滚之间修改了表映射配置(如 srcdb.*:dstdb.*),可能导致回滚 SQL 中的表名与实际表名不匹配。
gt-checksum v4.0.0 的反向回滚 SQL 生成能力,从根本上解决了数据修复"单向执行、无法回退"的痛点。通过 genRollSQL 一键开启,每条修复 SQL 都会自动生成对应的反向操作,写入结构化的回滚文件中。
针对不同场景,回滚机制做了精细化设计:
回滚 SQL 始终只写文件、不在线执行,配合 repairDB ./rollsql 即可快速回退。整个过程可审计、可控制,满足生产环境的安全合规要求。
一句话总结:genRollSQL=ON,让每一次修复都有后悔药。
相关阅读
gt-checksum v4.0.0 新功能解读系列文章(1):断点续传——大任务中断不再从头跑
gt-checksum v4.0.0 新功能解读系列文章(2):自定义数据类型映射——适配复杂迁移场景
gt-checksum v4.0.0 发布:可续跑、可回滚、可审计的数据校验与修复能力全面升级
合作电话:010-64087828
社区邮箱:greatsql@greatdb.com


