mysql创建临时表本身不加表级锁,但常因隐式操作触发表锁:如未走索引的insert...select引发全表扫描并持意向排他锁(ix),同名表误解析、lock tables未释放或mdl锁冲突等。

临时表创建为什么会触发表级锁
MySQL在创建临时表时本身不加表级锁,但问题常出在CREATE TEMPORARY TABLE语句执行前的隐式操作上。比如:当语句中包含SELECT ... INTO OUTFILE、INSERT ... SELECT或子查询未走索引时,InnoDB可能因无法确定扫描范围而退化为全表扫描——此时即使目标是临时表,原表仍会被加上意向排他锁(IX),若并发高且事务未及时提交,就卡住其他对同一基表的写操作。
更隐蔽的是TEMPORARY表名与普通表同名(如都叫tmp_orders)且会话未显式指定数据库时,MySQL可能误解析为操作基表;或者使用了LOCK TABLES后未UNLOCK TABLES,导致后续临时表语句被阻塞在等待表锁状态。
检查是否真由临时表语句引发锁冲突
别急着改代码,先确认锁源。用root权限执行:
SHOW FULL PROCESSLIST;
重点关注State列为Writing to tmp table、Copying to tmp table或Locked的线程,并比对Info字段是否含CREATE TEMPORARY、INSERT ... SELECT等关键词。再查:
SELECT * FROM information_schema.INNODB_TRX WHERE TRX_STATE = 'LOCK WAIT';
看TRX_QUERY是否指向临时表相关SQL;同时运行:
SHOW ENGINE INNODB STATUS\G
在LATEST DETECTED DEADLOCK或TRANSACTIONS段里找“temp”或“tmp”字样。
- 如果
PROCESSLIST里有大量Sleep线程且Time值很高,大概率是上游事务没提交,临时表语句被堵在等待行锁/间隙锁 -
State显示Creating sort index却卡住,说明ORDER BY或GROUP BY在临时表上触发了磁盘临时表,而磁盘临时表生成过程会短暂持有元数据锁(MDL) -
Info字段截断看不到完整语句?必须加FULL——SHOW FULL PROCESSLIST,否则你看到的可能是CREATE TEMPORARY TAB...这种无效信息
避免临时表引发锁冲突的实操要点
核心原则:不让临时表操作牵连基表锁,也不让其自身成为锁瓶颈。
- 禁用
INSERT ... SELECT建临时表:改用CREATE TEMPORARY TABLE ... AS SELECT,前者会在源表加读锁(尤其在REPEATABLE READ下可能加间隙锁),后者只对源表加一致性读视图,不加锁 - 确保
SELECT子句带有效索引:哪怕只是WHERE id IN (1,2,3),也要确认id是主键或有索引,否则InnoDB会扫全表并锁整张基表 - 显式指定临时表引擎:
CREATE TEMPORARY TABLE tmp_foo ENGINE=MEMORY AS SELECT ...,避免InnoDB临时表因内存不足自动转成磁盘表,后者会触发MDL锁升级 - 用完即删:不要依赖会话结束自动清理,显式加
DROP TEMPORARY TABLE tmp_foo;,防止长连接下临时表堆积占用资源 - 避免在存储过程中混用
TEMPORARY和普通表同名:哪怕加了IF NOT EXISTS,MySQL 8.0+仍可能因MDL锁冲突报错ERROR 1050 (42S01): Table 'xxx' already exists
线上已卡死时的紧急处理
不是所有情况都能等应用层重试——得快速切掉源头。
先定位ID:SHOW FULL PROCESSLIST; 找到State异常且Info含临时表操作的线程ID(Id列)。
若该线程Command是Query且State为Locked或Writing to tmp table,直接杀:
KILL <code>12345</code>;
注意:KILL杀的是连接,不是单条语句;如果业务用了连接池,杀完后新请求可能立刻重建连接并重放相同SQL,需同步在应用侧限流或降级。
更稳妥的做法是临时调整参数(仅限紧急):
SET SESSION tmp_table_size = 268435456; -- 提高内存临时表上限,减少磁盘落盘
但别动max_heap_table_size全局值,它会影响所有会话,可能挤占缓冲池。
真正容易被忽略的点:临时表本身不会跨会话可见,但它的元数据(如列定义)在创建瞬间会触发对information_schema的MDL读锁——如果此时有另一个会话正在执行ALTER TABLE或OPTIMIZE TABLE,就会相互等待。所以,运维类DDL操作务必避开业务高峰。











