临时表默认使用当前会话的default_storage_engine值,而非全局默认引擎;若会话中执行过set session default_storage_engine = 'myisam',则临时表即为myisam,易因磁盘文件损坏、无事务保护等问题在大查询中失败。

临时表默认用什么引擎?看这个配置项
MySQL 创建 CREATE TEMPORARY TABLE 时,**不显式指定 ENGINE 的情况下,用的是当前会话的 default_storage_engine 设置值**,不是全局默认、也不是服务器启动时的默认——它会继承你当前连接生效的引擎。很多同学以为“既然 MySQL 5.6+ 默认是 InnoDB”,临时表就一定是 InnoDB,结果在 MyISAM 为主的老库上跑出奇怪行为,根源就在这儿。
验证方式很简单:
SELECT @@session.default_storage_engine;或执行
SHOW CREATE TABLE (注意:临时表只在当前会话可见,别在其他会话查)。
- 如果返回
InnoDB,那没意外;但若返回MyISAM,哪怕你全局设了innodb_default,只要会话里执行过SET SESSION default_storage_engine = 'MyISAM',临时表就照用 MyISAM - 某些 ORM 或中间件(如旧版 Django + MySQL 连接池)会在建连后主动切引擎,容易埋雷
- 安全做法永远是显式声明:
CREATE TEMPORARY TABLE t1 (...) ENGINE=InnoDB;
MyISAM 临时表在大查询中崩得快,原因很实在
MyISAM 临时表对大结果集特别敏感,不是因为慢,而是容易直接报错或静默失败——典型表现是 ERROR 1169 (23000): Duplicate entry '...' for key 'PRIMARY'(明明没主键还报重复)、ERROR 1034 (HY000): Incorrect key file for table,甚至 Lost connection to MySQL server during query。
根本原因是:MyISAM 临时表仍依赖磁盘文件(.MYD/.MYI),且**不支持行锁、崩溃恢复和事务回滚**。一旦查询中途被 kill、OOM killer 干掉进程、或磁盘空间不足,临时文件残留、索引损坏概率极高;而 InnoDB 临时表走的是 ibtmp1 共享临时表空间(或独立 innodb_temp_data_file_path),所有操作受事务日志保护,异常中断也能干净清理。
- 大 GROUP BY / ORDER BY / DISTINCT 查询易触发 MyISAM 临时文件写满(尤其
tmp_table_size和max_heap_table_size设得小) - MyISAM 临时表无法使用
ALTER TABLE ... DISABLE KEYS加速导入——它压根不支持该语法 - 若查询含
UNION或子查询嵌套深,MySQL 内部可能多次创建/销毁临时表,MyISAM 的开销呈指数增长
强制用 InnoDB 临时表要注意两个硬限制
想让所有临时表都走 InnoDB?光改 default_storage_engine 不够,还得确认两个关键配置是否放开:
-
internal_tmp_disk_storage_engine必须为InnoDB(MySQL 8.0.13+ 引入):它控制“内存不够时自动落盘的内部临时表”用什么引擎。默认是InnoDB,但老版本或手动改过就可能还是MyISAM -
tmp_table_size和max_heap_table_size要足够大:即使引擎是 InnoDB,若结果集小于这两个值的较小者,MySQL 仍优先用内存引擎(MEMORY);只有超限时才落盘——这时才真正用到你指定的ENGINE - 注意:InnoDB 临时表不支持 FULLTEXT 索引,也不支持 GIS 类型字段(
POINT,POLYGON),如果查询里有MATCH ... AGAINST或空间函数,强制设ENGINE=InnoDB会直接报错
怎么快速判断某次大查询用了哪种临时表?
别猜,用 EXPLAIN FORMAT=TREE 或开启 optimizer_trace 最直接。但更轻量的办法是开 slow log 并加参数:
SET GLOBAL log_output = 'TABLE'; SET GLOBAL slow_query_log = ON; SET GLOBAL long_query_time = 0;然后查
mysql.slow_log,重点看 sql_text 里有没有 CREATE TEMPORARY TABLE,再结合 EXPLAIN 输出里的 Using temporary 提示。
一个容易被忽略的信号:如果 SHOW PROCESSLIST 里看到状态是 Creating tmp table 且持续时间长,基本可断定正在用磁盘临时表;此时立刻 KILL 掉并检查引擎类型,比等它超时强得多。
临时表这事,表面是引擎选择,实际是数据生命周期管理——InnoDB 把它当事务一部分管,MyISAM 当普通表随便扔。真要处理 GB 级中间结果,别省那句 ENGINE=InnoDB。











