临时表操作本身不加行锁、不加MDL写锁,但易因前置或伴随动作间接触发锁;CREATE TEMPORARY TABLE仅加短暂MDL读锁,不阻塞其他会话DDL;其数据不参与InnoDB事务管理,故无行锁。

临时表操作本身不加行锁、不加MDL写锁,但容易因前置或伴随动作间接触发——这是绝大多数人误判锁源的根源。
CREATE TEMPORARY TABLE 为什么不加 MDL 写锁
MySQL 对 CREATE TEMPORARY TABLE 的处理是会话隔离的:该语句只在当前连接内注册元数据,不写入 information_schema 或磁盘字典表,也不通知其他会话。因此不会触发 MDL 写锁(即阻塞其他线程 DDL 的那种排他锁)。
但它仍会加一个轻量级的 MDL 读锁(MDL_SHARED),仅用于防止当前会话在创建过程中被 DROP DATABASE 等极端操作中断——这个锁生命周期极短,几乎不影响并发。
- 验证方式:
SELECT * FROM performance_schema.metadata_locks WHERE OBJECT_SCHEMA = 'your_db' AND LOCK_DURATION = 'TRANSACTION';通常查不到临时表记录 - 注意:如果
CREATE TEMPORARY TABLE ... SELECT中的SELECT部分扫描了普通表,那个普通表会被加上 MDL 读锁 + 行锁/意向锁,锁归属不在临时表上,而在基表上 - MyISAM 临时表例外:它仍会走表锁路径,但仅限本会话,不影响其他连接
为什么临时表自身不产生行锁
临时表数据完全驻留在会话内存(或临时文件系统路径如 /tmp/#sql_*),InnoDB 的事务系统不管理它。没有聚簇索引、没有 undo log、不参与 MVCC —— 所以 INSERT INTO tmp SELECT ... 或 UPDATE tmp SET ... 这类操作,InnoDB 引擎根本不会为其生成行锁记录。
- 你可以用
SELECT * FROM performance_schema.data_locks WHERE OBJECT_SCHEMA = 'performance_schema';查证:结果为空或只含系统表锁 - 但若 SQL 中混用临时表和普通表(如
UPDATE orders o JOIN tmp_orders t ON o.id = t.id SET o.status = 'done'),那么orders表照常加行锁,锁冲突来源仍是基表 - 临时表上的
ORDER BY/GROUP BY若溢出到磁盘,会短暂持有 MDL 锁(类型为MDL_BACKUP),此时可能卡住 DDL,但不是行锁
哪些“看似临时表”的操作实际会加锁
真正引发锁冲突的,往往不是 CREATE TEMPORARY TABLE 本身,而是它前后包裹的上下文逻辑。
-
INSERT INTO tmp SELECT * FROM orders WHERE status = 'pending':如果orders.status没索引,全表扫描会为每行加意向排他锁(IX),并发 UPDATE 就会被堵 - 同名临时表与基表共存(如都叫
tmp_user)且未指定库名,MySQL 可能解析成基表,执行DROP TEMPORARY TABLE tmp_user实际变成DROP TABLE tmp_user,触发 MDL 写锁 - 在已执行
LOCK TABLES orders WRITE的会话里建临时表,会话被阻塞在等待表锁释放,SHOW PROCESSLIST显示 State =Locked,但锁源是显式LOCK TABLES,不是临时表
最常被忽略的一点:临时表语句执行慢,往往不是它自己锁了谁,而是它暴露了上游事务没提交、基表索引缺失、或磁盘临时表 I/O 瓶颈——排查时紧盯 INFORMATION_SCHEMA.INNODB_TRX 和 SHOW ENGINE INNODB STATUS\G 里的 TRX_QUERY 字段,别被 “tmp” 字样带偏方向。


















