归档需分段执行且绕开主库写压力:用主键或时间戳游标分页(每批5k–10k行),从库只读查询,主库单独执行写操作;历史表须严格LIKE原表并同步索引。

归档时事务过大导致主从延迟飙升
直接用单个 DELETE 或 INSERT ... SELECT 操作归档几百万行,会生成超长事务,binlog 写入阻塞、从库重放卡顿、主从延迟轻松突破 600 秒——这不是配置问题,是事务粒度失控的必然结果。你看到的“主从延迟 800 秒”,本质是归档语句锁住了大量行,且未提交,从库只能干等。
必须分段执行,但不能只靠 LIMIT + OFFSET
LIMIT 10000 OFFSET 0 看似安全,实际在大表上越往后偏移,扫描成本越高(MySQL 仍需跳过前面所有行)。尤其当归档条件涉及非索引字段或范围较大时,OFFSET 会退化成全表扫描前 N 行,CPU 和 IO 双重浪费。
- 改用基于主键/时间戳的游标式分页:每次用上一批最后一条的
id或gmt_create作为下一批起点,例如WHERE gmt_create 123456789 - 确保该字段有高效索引(最好是联合索引的最左列),避免回表或排序
- 每批控制在 5k–10k 行以内,实测超过 2w 行就容易触发 long transaction 报警
归档过程要绕开主库写压力
归档本质是读多写少的操作,但默认走主库会导致写锁竞争、redo log 暴涨、buffer pool 脏页堆积。别让归档和业务更新抢同一套资源。
- 归档脚本连接从库做
SELECT(只读),再用独立连接主库做INSERT INTO ... SELECT或DELETE—— 但注意:跨库操作无法保证原子性,需加幂等控制 - 更稳妥的做法是:先从从库导出归档数据到临时文件(
mysqldump --where="..."),再用LOAD DATA INFILE导入历史表,主库只承担最终的DROP和RENAME - 禁止在业务高峰期执行;建议配合
pt-archiver工具,它内置了休眠、限流、事务拆分和失败重试逻辑
历史表结构必须与原表严格一致
看起来只是复制结构,但漏掉一个索引、一个 NOT NULL 约束,或者字符集不一致(比如原表是 utf8mb4_0900_ai_ci,历史表建成了 utf8mb4_general_ci),都会导致后续归档迁移失败,或引发隐式类型转换使索引失效。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
正确做法是始终用 CREATE TABLE biz_order_history LIKE biz_order,而不是手写 CREATE TABLE 语句。如果需要删减索引以提升插入速度,也应在归档完成后、数据导入完毕再执行 DROP INDEX,而非建表时省略。
最容易被忽略的是:归档过程中,原表可能新增了索引,但历史表没同步——这会让后续增量归档的 INSERT 性能断崖下跌。建议把索引同步纳入归档脚本的前置检查项。










