一、为什么单线程恢复越来越“玩不转”
-
体积膨胀:业务日志、JSON 字段、画像表让库体积从几十 GB 轻松跳到 TB 级。
-
时间窗口:凌晨 4 点~6 点的维护区间,不允许“source 到中午”。
-
失败成本:SSH 终端一断开,前功尽弃;重新跑还要再锁表、再停机。
-
审计要求:金融、医疗等行业要求“恢复过程可追踪、可回滚、可重现”,手工操作难以留痕。
myloader 的多线程、断点续做、并行校验特性,正好解决上述痛点。但它不是“一条指令敲下去就万事大吉”的玩具,必须先理解工作流,再谈性能。
二、myloader 的“三段式”工作流
-
主线程预热
-
先读备份目录的元数据文件,拿到库表列表、字符集、插件版本号。
-
按依赖顺序创建库、表结构、视图、触发器、存储过程,这一步是单线程,防止外键依赖错乱。
-
-
工作线程并发
-
依据“数据文件粒度”启动多个工作线程,默认一文件一线程。
-
每个线程内部再按“事务批次”循环插入,批次太大容易把回滚段撑爆,太小又浪费网络往返。
-
-
主线程收尾
-
统一创建外键、索引、函数、事件,避免并发阶段因外键检查导致死锁。
-
可选地生成校验和或执行
ANALYZE TABLE,更新优化器统计信息。
-
理解“先结构→再数据→后索引”的顺序,是阅读错误日志的前提。很多人看到 “Unable to create index” 就慌,其实是收尾阶段才会跑的索引,前面表导入失败它当然建不了——根因往往在更靠前的数据文件里。
三、恢复前的“三件套”检查
-
版本对拍
-
备份端与恢复端的大版本最好相同;跨主版本时,myloader 会尝试做字段类型映射,但
utf8mb3→utf8mb4这种隐含变更仍可能导致 “Column length too big”。
-
-
空间估算
-
数据文件 + 索引膨胀 + undo/redo 同时写入,预留 2.5 倍备份体积最保险;别忘了
tmpdir也要够大,否则大数据文件排序时会报 “No space left on device”。
-
-
权限梳理
-
恢复账号需要
CREATE, DROP, INSERT, ALTER, CREATE ROUTINE, TRIGGER等权限;若目标实例开启binlog且需要级联复制,还要授予SUPER或BINLOG_ADMIN,否则恢复过程无法写binlog导致主从延迟。
-
四、并发参数“由大到小”的漏斗调优法
-
线程数
-
先设 CPU 核心数的 2 倍,观察
iostat的%util;若磁盘队列长期 > -core×2,再下调线程。SSD 一般能吃到 8~16 线程,机械盘 4 线程就饱和。
-
-
每事务查询数
-
先给“几千”起步,出现 “Lock wait timeout” 再往下减;写入瓶颈常是二级索引维护,而非记录行数。
-
-
索引后置
-
用“先禁用二级索引,导入完再集中建索引”模式,可把插入性能提升 30% 以上;但主键必须随数据一起建,否则后续无法保证唯一性。
-
-
关闭外键检查
-
导入前
SET FOREIGN_KEY_CHECKS=0,收尾阶段再打开;否则父子表并行写入时极易死锁。
-
记住口诀:大线程→小事务→后置索引→外键关掉再打开。每调一次都要重新跑同体积的样本,记录耗时与慢查询,别让“感觉快”代替真实数据。
五、在线验证:行数、校验和、业务语义三重门
-
行数比对
-
用
SELECT COUNT(*)对核心表逐表核实;information_schema.tables的估算值在大批量导入后往往不准,别被它骗了。
-
-
校验和
-
备份时加
-M参数会生成md5文件,恢复完在目标库重新计算,逐表比对;若出现不一致,优先检查字符集与sql_mode差异,再考虑是否出现行尾空格、浮点精度问题。
-
-
业务语义
-
行数对得上不代表“钱没丢”;抽样查询最新一笔订单、最早一笔流水,确认金额、状态、时间戳逻辑正确;若业务允许,再跑一遍日终统计,与源库报表对齐。
-
只有三重门都通过,才能宣告“数据层”恢复成功;否则就把恢复实例标记为
read-only,留作后续分析,禁止直接推上线。六、增量恢复与断点续做:让“失败”不再从 0 开始
-
基于时间点的增量
-
先恢复全量备份 → 再拿
binlog做start-datetime到stop-datetime重放;myloader 只负责全量阶段,binlog重放可用原生mysqlbinlog或第三方工具。
-
-
断点续做
-
myloader 不支持“自动”断点续做,但可通过“指定表列表”跳过已完成的表;把失败表单独列文件,再启动第二次恢复,实现“半自动”续跑。
-
-
失败信息收集
-
在
myloader日志里搜索 “ERROR”,“ deadlock ”,“retry” 关键字,定位到具体表、具体文件;多数死锁与“唯一索引+并发顺序”相关,把该表改为单线程即可通过。
-
七、常见翻车场景与速效救心丸
-
“Unknown collation” 报错
-
源库用了新排序规则,目标库版本低;先在目标库升级小版本,或导出时加
--skip-definer并手动改排序规则。
-
-
“Got error 0 from storage engine”
-
90% 是表空间文件残留,恢复前没删干净;
DROP后确认*.ibd文件消失再跑。
-
-
“The used command is not allowed with this MySQL version”
-
用了
binlog参数,但目标实例开read_only=1;先关只读,导入完再开。
-
-
线程数过高导致 “Too many connections”
-
恢复账号独占连接池,别把
max_connections全吃完,导致业务连不进来;单独给恢复账号设max_user_connections上限。
-
八、上线前最后 5 分钟:检查表统计信息、备份新库、打开外键
-
统计信息
-
导入过程索引是后建的,统计信息为空;执行
ANALYZE TABLE或打开innodb_stats_auto_recalc,让优化器别走错索引。
-
-
再备份
-
恢复完的数据库已是“当前最新”,立刻做一次物理或逻辑备份,形成新的“基线”;下次故障就不用再“全量+海量 binlog”。
-
-
打开外键
-
记得
SET FOREIGN_KEY_CHECKS=1,并SHOW ENGINE INNODB STATUS确认无外键错误。
-
-
通知复制拓扑
-
若下游还有从库,确认
Exec_Master_Log_Pos已追上;再打开业务流量,防止“旧从库”被误用。
-
九、总结:把“恢复”纳入日常运维生态
-
备份、恢复、校验三板斧要自动化:
备份成功 → 自动推到校验环境 → 自动跑COUNT(*)与校验和 → 生成报告 → 推送消息。只要一环失败,立刻告警。 -
把“演练”当“上线”做:
每季度挑一个真实从库,按正式流程删库、重建、导入、校验、切流量;演练通过才证明流程有效,否则只是“纸上安全”。 -
文档要常改:
字符集升级、版本跨度、新参数引入都会让旧流程失效;每次演练后更新“失败场景与解决步骤”,让下一位值班工程师不再从零开始。
数据恢复不是“救火”,而是“日常消防演练”。当你把 myloader 的并行恢复、一致性校验、失败续跑都写成脚本、纳入流水线,备份文件就不再是“沉睡的压缩包”,而是随时可拉起、可验证、可回滚的“另一条生产线”。愿下次故障来临时,你不再需要凌晨狂奔机房,只需一条通知确认——“演练通过,数据已就位”。