Navicat自动执行SQL脚本锁表的根本原因是默认启用REPEATABLE READ长事务、全表扫描及全程持有MDL锁,且不开放隔离级别或锁提示控制;导致ALTER/INSERT等操作因等待MDL而阻塞。
navicat 自动执行 sql 脚本(如导入、同步、结构比对)时锁表,根本原因不是脚本本身写得不对,而是它默认用长事务 + 全表扫描 + 持有 mdl(元数据锁),且不暴露隔离级别或锁提示控制权。你没法靠加 nolock 或改会话变量绕过——那压根不是 mysql 的语法,navicat 也不认。
为什么 Navicat 执行脚本会卡住其他 ALTER/INSERT?
它在后台干了三件事:启动一个 REPEATABLE READ 事务、对每张目标表执行 SELECT 获取当前状态、全程持有该表的 MDL 锁。只要这个事务没结束,任何想改表结构(ALTER TABLE)或批量写入(INSERT ... SELECT)的操作都会卡在 Waiting for table metadata lock 状态。
- 即使脚本只读(比如“结构同步”预览),Navicat 仍会开事务并锁 MDL
- WHERE 条件没走索引 → 触发全表扫描 → 行锁升级为表级影响范围,阻塞更广
- 大表导出耗时长 →
mysqldump --single-transaction能避免锁表,但 Navicat 的“转储 SQL 文件”若没勾选该选项,就会退化成LOCK TABLES
怎么查它正在锁哪张表?
别等报错,立刻连上数据库执行:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW()) - TIME_TO_SEC(TRX_STARTED) > 30;
再结合:
SELECT OBJECT_NAME, LOCK_TYPE, LOCK_MODE, LOCK_STATUS FROM performance_schema.metadata_locks WHERE OBJECT_SCHEMA = 'your_db' AND LOCK_STATUS = 'PENDING';
- 重点看
TRX_QUERY字段是否含SELECT+ 表名,且TRX_STARTED时间很老 -
performance_schema.metadata_locks中PENDING状态行,直接告诉你谁在等哪张表的 MDL - 注意:Navicat 进程 ID 可能已断开,但后台残留事务还在 hold 锁——得靠
TRX_IDkill,不是靠客户端 PID
真正能落地的规避方式
放弃让 Navicat “变聪明”,转而控制它的行为边界:
- 所有导出任务,右键数据库 → “转储 SQL 文件” → 勾选
--single-transaction,并**手动在“额外参数”框里补上--skip-add-locks --skip-extended-insert**;不勾就等于默认加LOCK TABLES - 做数据同步前,先确认 WHERE 条件字段有索引;如果必须全表比对,就在同步向导 Step 2 的 “Where Condition” 栏里填
id BETWEEN ? AND ?分段,避开全扫 - 结构同步时,进 Options → 取消勾选
Drop objects not exist in source,否则生成的DROP COLUMN语句一旦执行,会触发隐式提交并释放 MDL,但中间若有其他事务正读这张表,就可能被回滚中断 - 永远不要在生产库直接运行 Navicat 自动生成的“同步 SQL”——它不带
LIMIT、不拆批、不设超时;把预览出的 SQL 拿出来,自己加WHERE id > last_id ORDER BY id LIMIT 5000再执行
最易被忽略的一点:Navicat 的“执行”按钮不是原子操作。它可能已发了 START TRANSACTION,但卡在某个 SELECT COUNT(*) 上不动,此时你关掉窗口,事务还在跑。下次执行前,先清一遍长事务,比调参数管用得多。


















