导出千万级表必须走从库而非主库,否则select会触发mvcc版本链遍历、占用buffer pool、加剧锁竞争,尤其在存在未提交事务或长查询时易被阻塞并拖慢主库写入;navicat需配置专用只读连接指向延迟为0的从库,并用主键分页(如where order_id > x order by order_id limit 1000)逐批导出,禁用自动limit和压缩,确保排序字段有索引,导出后须验证主库锁等待与响应性能是否正常。

导出千万级表必须走从库,不能连主库
直接在主库上执行导出查询,哪怕只是 SELECT *,也会触发 MVCC 版本链遍历、占用 buffer pool、放大锁竞争——尤其当表上有未提交事务或长查询时,SELECT 可能被阻塞,反过来又拖慢主库写入。Navicat 默认连接的是你配置的主库地址,不会自动识别读写分离。
实操建议:
- 在 Navicat 新建一个**专用只读连接**,指向业务从库(确认该从库延迟
- 连接高级设置里勾选
read_only=1或在初始化命令中加SET SESSION transaction_read_only = ON;,防误操作 - 导出前先在该连接中执行
SHOW SLAVE STATUS\G(MySQL)或SELECT pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn();(PostgreSQL),确认同步无积压
用游标分页替代 LIMIT,避免深分页锁表
Navicat 的「导出向导」若基于你写的 SELECT * FROM orders LIMIT 1000000, 1000 导出,数据库要扫描前 100 万行才能取后 1000 行,期间可能持有间隙锁或行锁,尤其在 RR 隔离级别下,会显著拉长锁持有时间,影响主键更新类业务。
正确做法是让导出逻辑本身支持游标分页,而不是依赖 Navicat 自动拼接:
- 先手动执行
SELECT * FROM orders WHERE order_id > 0 ORDER BY order_id ASC LIMIT 1000;,记下最后一行的order_id - 下一批改写为
SELECT * FROM orders WHERE order_id > 123456789 ORDER BY order_id ASC LIMIT 1000; - 把每批结果用 Navicat 的「导出结果」单独保存为 CSV/Excel,不勾选「All records」,而是导出当前页(即已查出的 1000 行)
- 步长控制在 1000–2000,避免单次查询响应过长触发超时
禁用 Navicat 自动 LIMIT 和压缩,防止隐式锁升级
Navicat 查询编辑器右下角默认开启 Limit rows,且导出向导若没手动选「All records」,会沿用这个限制——表面看是只导 1000 行,但背后它仍可能对全表做元数据扫描或预估行数,引发 MDL 锁;启用「压缩导出」还会延长连接空闲时间,间接增加锁等待窗口。
关键动作:
- 执行导出 SQL 前,务必取消勾选编辑器右下角的
Limit rows(或设为 0) - 导出向导第二步「Options」中,明确选择
All records,而非Current page - 在导出设置里关闭
Compress output file(尤其导出 CSV/SQL 时) - 如果导出目标是 SQL 文件,确保生成语句不含
INSERT INTO ... SELECT类型,这类语句在 InnoDB 中会持锁更久
导出后验证是否真避开了锁影响
导出完成不等于生产没被干扰。真正要确认的是:导出过程中,线上下单、支付回调等核心 DML 是否响应正常、无排队、无慢日志突增。
检查方式:
- 导出启动前后 5 分钟,查 MySQL 的
SHOW PROCESSLIST;,确认没有大量State: Sending data或Locked的线程堆积 - 对比导出时段与平时的
Innodb_row_lock_time_avg(可通过SHOW GLOBAL STATUS LIKE 'Innodb_row_lock%';获取),增幅超过 2 倍需警惕 - 在从库上导出时,如果发现主库
Threads_running明显升高,说明从库 IO 或 SQL 线程压力反向传导到了主库 binlog dump 线程,得调低从库的slave_parallel_workers
最易被忽略的一点:游标分页导出虽然不锁表,但如果导出脚本里没加 ORDER BY order_id,或者用了非主键字段排序,数据库可能退化为 filesort + 临时表,照样吃内存、占 IO、拖慢从库同步——千万级场景下,排序字段必须是已索引的主键或唯一键。











