主主复制中断后,如何高效对比MySQL大型数据库一致性?
处理大表MySQL双主数据一致性对比的最优方案
我之前处理过好几起类似的大表双主一致性校验的情况,mysqldbcompare这种全量对比工具在数据量上来之后确实容易因为超时、资源耗尽掉链子,给你几个经过实战验证的高效方案,能帮你在短时间内搞定:
一、先通过校验和快速定位差异范围
全量逐行对比太耗时间,先对目标表生成校验和,快速确定是否有差异,以及差异的大致位置,再针对性处理。
1. 用MySQL自带的CHECKSUM TABLE(最快轻量化方案)
如果业务能接受短时间的只读锁,这个是最快的方式,直接在两台服务器上分别执行:
-- 先设置会话隔离级别减少锁影响(仅InnoDB) SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 生成全表校验和,EXTENDED会逐行计算,结果更准确 CHECKSUM TABLE EXAMPLE.large_table EXTENDED;
对比两台机器返回的校验和值:如果一致,说明大表数据无差异;如果不一致,再进行下一步精准排查。
2. 用pt-table-checksum(Percona工具,大表首选)
这是Percona Toolkit里专门针对大表和复制架构的一致性校验工具,核心优势是分块校验,不会锁全表,资源占用低,速度快。
- 安装Percona Toolkit后,在其中一台服务器上执行(如果复制中断,可以分别在两台机器跑命令后对比结果):
pt-table-checksum h=你的主1IP,u=校验用户,p=密码 --databases=EXAMPLE --tables=large_table --chunk-size=200000
- 参数说明:
--chunk-size可以根据服务器性能调整,比如改成20万行一块,加快校验速度;工具会自动生成校验和并存储在临时表,你可以直接对比两台机器的校验结果,定位到具体差异的分块。
二、校验和不一致时,精准定位差异行
如果确定大表有差异,不用全量扫描,用以下工具快速找到具体差异:
1. pt-table-sync(和pt-table-checksum配套)
这个工具可以和pt-table-checksum联动,直接定位并修复差异,先执行--print参数查看差异SQL,不实际修改:
pt-table-sync --print h=主1IP,u=用户,p=密码 h=主2IP,u=用户,p=密码 --databases=EXAMPLE --tables=large_table
它会基于之前的分块校验结果,只对比有差异的块,输出需要修复的INSERT/UPDATE/DELETE语句,比逐行扫描快N倍。
2. 自定义分块对比脚本(适合特殊场景)
如果不想用第三方工具,可以自己写脚本按主键分块对比,比如按ID范围每次取10万行,计算MD5校验和:
import mysql.connector import hashlib def get_chunk_checksum(host, user, pwd, db, table, start_id, end_id): conn = mysql.connector.connect(host=host, user=user, password=pwd, database=db) cursor = conn.cursor() # 把需要对比的字段都列出来,用CONCAT_WS拼接避免空值影响 cursor.execute(f""" SELECT MD5(CONCAT_WS('|', id, col1, col2, col3)) FROM {table} WHERE id BETWEEN {start_id} AND {end_id} ORDER BY id """) checksums = [row[0] for row in cursor.fetchall()] conn.close() # 对块内所有行的校验和再做一次哈希,方便对比 return hashlib.md5(''.join(checksums).encode()).hexdigest() # 假设主键是id,总共有6000万行,每次取10万行 for i in range(0, 60000000, 100000): start = i + 1 end = min(i + 100000, 60000000) sum1 = get_chunk_checksum('主1IP', '用户', '密码', 'EXAMPLE', 'large_table', start, end) sum2 = get_chunk_checksum('主2IP', '用户', '密码', 'EXAMPLE', 'large_table', start, end) if sum1 != sum2: print(f"发现差异,ID范围:{start}-{end}") # 可以在这里进一步逐行对比这个块的具体数据
你还可以改成多线程版本,同时处理多个分块,进一步缩短时间。
三、短时间完成的核心技巧
- 优先排除小表:先对EXAMPLE库的小表用mysqldbcompare快速对比,只把精力放在大表上;
- 利用业务低峰期:尽量在流量最小的时候操作,服务器资源更充足,锁对业务的影响也更小;
- 调整工具参数:比如pt-table-checksum的
--chunk-size可以根据服务器CPU、内存情况调大,加快校验速度; - 避免全量扫描:任何时候都不要直接全量逐行对比,先通过校验和缩小范围是关键。
内容的提问来源于stack exchange,提问作者Hasanuzzaman Sattar
相关产品推荐
相关产品推荐

