必须分块、带顺序、控节奏执行归档,否则引发长事务、主从延迟、binlog打爆等问题;需确认时间字段(如created_at)存在且已索引,null值会导致漏归档;推荐使用pt-archiver工具,支持断点续传与流量压制,手动方案须按主键分页并加sleep避免i/o尖峰。

不能直接 DELETE 或 INSERT ... SELECT 全量跑,必须分块、带顺序、控节奏;否则必然触发长事务、主从延迟、binlog打爆、锁等待超时。
确认归档字段是否可用且被索引
归档依赖时间或业务序号字段,但不是有字段就行——得能高效定位边界、不漏不重、不乱序。
-
created_at、log_time这类字段必须存在,且已建INDEX(最好是联合查询常用字段的前缀索引) - 用
SHOW CREATE TABLE order_log检查索引定义;若只有id,需验证其与时间是否强一致:执行SELECT id, created_at FROM order_log ORDER BY id LIMIT 100和ORDER BY created_at LIMIT 100对比,确认无批量回填、replace into 导致的 ID 跳变 - 若字段含 NULL 或大量空值,
WHERE created_at 会跳过这些行,归档后源表残留数据难清理
用 pt-archiver 分批迁移+删除(推荐首选)
它天然解决“查—插—删”三步的原子性、断点续传、流量压制问题,比手写存储过程更稳。
- 先试跑(加
--no-delete):pt-archiver --source h=localhost,D=prod,t=order_log --dest h=archive-host,D=archive,t=order_log_2023 --where "created_at -
--bulk-delete启用批量删而非逐行,避免 delete 触发 N 次二级索引更新;但目标库必须有相同主键,否则无法精准定位已归档行 -
--sleep 0.2是关键调节项:值低于 0.1 容易压垮从库 IO 和 binlog 网络;高于 0.5 会导致单次归档周期拉长,增加业务窗口压力 - 务必在归档前确认
max_allowed_packet≥ 64M(尤其跨实例时),否则INSERT批量失败报Packets larger than max_allowed_packet are not allowed
不用 pt-archiver 时的手动分块方案
适用于无法装 Percona Toolkit 或需嵌入业务代码的场景,核心是“按主键/时间双条件分页”,避免 OFFSET 越往后越慢。
- 先查最小归档起点:
SELECT MIN(id) FROM order_log WHERE created_at - 循环执行(每次处理 5000 行):
INSERT INTO archive_db.order_log_2023 SELECT * FROM prod_db.order_log WHERE created_at = ? AND id ,再 <code>DELETE同范围;?值由上一轮最大id推进 - 每次
DELETE后加DO SLEEP(0.1)(存储过程中)或应用层time.Sleep(100 * time.Millisecond),防止 I/O 尖峰 - 严禁用
ORDER BY created_at LIMIT 5000直接删——InnoDB 可能因 MVCC 版本链导致重复或遗漏,必须锁定主键范围
归档后空间没释放?别信 DATA_LENGTH
INFORMATION_SCHEMA.TABLES 里的 DATA_LENGTH 和 INDEX_LENGTH 是估算值,DELETE 后不会立刻下降;真正释放要靠 InnoDB 的后台 purge 线程和后续 OPTIMIZE TABLE(但千万级表慎用,会锁表)。
- 确认空间回收是否生效,看
df -h磁盘使用率,或SELECT FILE_NAME, TABLESPACE_NAME, ALLOCATED_SIZE FROM INFORMATION_SCHEMA.FILES WHERE TABLESPACE_NAME = 'innodb_system' - 如果用了分区表,
ALTER TABLE order_log DROP PARTITION p202301是瞬时操作,DATA_LENGTH会立即更新,且不产生 undo log - 归档完记得
ANALYZE TABLE order_log,否则优化器可能沿用旧统计信息,误判索引选择性











