mysql 5.7 中内部临时表锁瓶颈核心在于避免创建而非调大 tmp_table_size;因 memory 引擎强制固定行长、表级锁且超限即落盘 myisam(表级锁),导致内存浪费、频繁落盘与元数据锁争用,加剧并发恶化。

直接结论:MySQL 5.7 中内部临时表引发的锁瓶颈,核心不是调大 tmp_table_size,而是避免它被创建;一旦创建,锁竞争和磁盘落盘会同时恶化并发性能。
为什么 internal_tmp_mem_storage_engine = MEMORY 会加剧锁争用
MySQL 5.7 默认用 MEMORY 引擎处理内存临时表,但它不支持变长字段(如 VARCHAR、TEXT),所有行按最大可能长度预分配——哪怕你只存 3 个字节,也占满整个列定义宽度。这导致:
- 同样数据量下,
MEMORY表比实际需要多占 2–5 倍内存,快速触达tmp_table_size上限 - 一超限就强制落盘为磁盘临时表(默认用
MyISAM或InnoDB),而磁盘临时表建表/写入过程会持metadata lock,阻塞其他 DDL 和部分 DML -
MEMORY引擎本身使用表级锁(LOCK TABLES级别),高并发GROUP BY或ORDER BY查询会排队等待同一把锁
如何确认当前正在用什么引擎建临时表
执行这条语句,看返回值:
SELECT @@internal_tmp_mem_storage_engine;
如果返回 MEMORY(5.7 默认),且你频繁看到 Created_tmp_disk_tables 上升,说明已大量落盘。进一步验证是否真在用临时表:
- 对可疑 SQL 加
EXPLAIN,看Extra字段是否含Using temporary - 运行中查状态:
SHOW STATUS LIKE 'Created_tmp%';,重点关注Created_tmp_disk_tables每秒增长量 > 0.5 就算异常 - 抓取慢日志里带
GROUP BY/ORDER BY/DISTINCT的语句,它们是临时表主力制造者
不升级 MySQL 版本的前提下怎么缓解
MySQL 5.7 无法原生启用 TempTable 引擎(那是 8.0+ 的),但可通过组合策略绕过 MEMORY 的硬伤:
- 给
GROUP BY或ORDER BY字段加联合索引,让排序/分组走索引覆盖,直接避免临时表 —— 这比调参有效十倍 - 把
tmp_table_size和max_heap_table_size设为相同值(例如64M),防止因取小值而“意外”落盘 - 禁用查询缓存(
query_cache_type = 0),因为 QC 在某些场景下会强制触发临时表逻辑 - 对批量统计类查询,改用应用层分页聚合,或拆成多个单条件
COUNT(*)+UNION ALL,避开复杂临时表路径
最容易被忽略的隐性锁点:磁盘临时表的引擎选择
当内存临时表落盘,MySQL 5.7 默认用 MyISAM 建磁盘临时表(除非显式指定 innodb_tmpdir)。而 MyISAM 是表级锁,一个大 GROUP BY 正在写磁盘临时表时,其他任何想读/写同名临时表的查询都得等。更麻烦的是:
- 你无法通过配置切换这个默认行为(5.7 不支持
internal_tmp_disk_storage_engine) - 即使你设了
innodb_tmpdir,也只是把临时表文件放到指定路径,底层仍可能是MyISAM - 所以真正有效的做法是:盯住
Created_tmp_disk_tables,只要它开始涨,就说明锁瓶颈已在发生,此时优化 SQL 比调参数更紧迫











