根本原因是navicat同步默认以repeatable read开启长事务,全表扫描读源表并持有mdl读锁直至任务结束,且无法干预执行计划或隔离级别对mdl无效。
navicat 数据同步会锁生产表,根本原因不是它“想锁”,而是它默认用 repeatable read 隔离级别开启事务 + 全表扫描读取 + 持有元数据锁(mdl),三者叠加在大表或慢查询下必然触发阻塞。
为什么同步过程会持有 MDL 锁且不释放
Navicat 同步任务启动时,会显式执行 START TRANSACTION,随后对源表做 SELECT 扫描。这个事务在结束前会一直持有该表的 MDL 读锁——哪怕你只查 1 行,只要事务没提交,DDL(如 ALTER TABLE)、甚至某些 INSERT/UPDATE 都会被卡在 Waiting for table metadata lock 状态。
- MDL 锁与事务生命周期绑定,不是语句级的;
COMMIT或ROLLBACK才释放,而 Navicat 同步直到整个任务完成才提交 - 即使你勾选了“仅同步新增”,它仍需先全量扫描源表主键来确认哪些是“新增”,这一步就已持锁
- 目标库写入阶段也会开启新事务,同样持有目标表 MDL,导致双向阻塞风险
全表扫描 + 无索引 WHERE 是锁等待的直接导火索
当你在同步设置里没指定有效过滤条件,或 WHERE 字段没索引,Navicat 会走全表扫描读源数据。InnoDB 在 REPEATABLE READ 下会对扫描路径上的所有行加间隙锁(gap lock)或临键锁(next-key lock),不仅拖慢自己,还让其他事务更新相邻主键范围时排队等待。
- 常见误配:
WHERE updated_at > ?但updated_at没建索引 → 全表扫 → 锁膨胀 - 时间戳字段值重复率高(如秒级精度)时,即使有索引,优化器也可能放弃使用,退化为全扫
- Navicat 不支持
FORCE INDEX或IGNORE KEY,无法干预执行计划
为什么改隔离级别(比如设成 READ COMMITTED)基本没用
把同步设置里的事务隔离级别调成 READ COMMITTED,只能略微减少行锁数量,但完全不影响 MDL 锁的持有行为和持续时间——而生产环境最常被卡住的,正是 MDL。
-
READ COMMITTED下,InnoDB 仍会在每个SELECT语句开始时生成新 read view,但 MDL 锁从START TRANSACTION就已申请,跟可见性无关 - DDL 操作(如加字段、改类型)强制要求排他 MDL 写锁,任何正在运行的 DML 事务(包括 Navicat 的同步事务)都会被阻塞,无论隔离级别
- Navicat 界面里那个隔离级别下拉框,只控制它自己生成的 SQL 语句的会话级设置,不影响外部并发事务,也不影响 MySQL 内核对 MDL 的调度逻辑
真正要防锁表,得绕开 Navicat 同步的黑盒事务模型:用 mysqldump --single-transaction 分批导出,再用 mysql 命令行分批导入,全程不依赖长事务,也不申请 MDL 写锁;或者对大表同步,直接切到 pt-online-schema-change 这类工具——它们靠影子表+触发器实现无锁变更,才是生产环境该用的方式。











