mysql 8.0临时表空间争用不源于事务锁,而来自临时表落盘时ibtmp1扩展的全局锁及会话级文件分配互斥;需通过performance_schema i/o等待统计、落盘率>15%、错误日志报错等指标确认,并调大max_heap_table_size与internal_tmp_mem_storage_engine且删旧tmp_table_size配置。

为什么临时表空间会引发事务锁争用
MySQL 8.0 中的临时表空间本身不参与事务锁(ibtmp1 和 temp_N.ibt 是纯临时存储,无 MVCC、无 undo log),但“争用”往往来自两个隐性耦合点:一是多个会话同时触发大结果集排序/分组,密集申请会话级临时表空间文件,触发 #innodb_temp/ 目录下的文件分配互斥;二是当内存不足导致内部临时表频繁落盘时,InnoDB 需要为这些磁盘临时表分配回滚段(从 ibtmp1),而 ibtmp1 的扩展操作在某些版本中存在轻量级全局锁(尤其在 8.0.16–8.0.28 间较明显)。
如何确认争用是否真实存在
别只看 SHOW ENGINE INNODB STATUS 里的 LOCK WAIT —— 临时表空间争用不会出现在那里。真正有效的指标是:
-
SELECT * FROM performance_schema.events_waits_summary_global_by_event_name WHERE event_name LIKE 'wait/io/file/innodb/%temp%';—— 关注count_star和avg_timer_wait是否异常高 -
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_wait_free';若非零,说明 buffer pool 压力已传导至临时表空间调度 - 检查错误日志中是否有
Failed to extend temporary tablespace或Cannot allocate space in temp tablespace - 观察
Created_tmp_disk_tables / Created_tmp_tables比值是否持续 >15%,这是争用前置信号
调整 max_heap_table_size 和 internal_tmp_mem_storage_engine
这是最直接干预点:降低落盘频率 = 减少对 temp_N.ibt 和 ibtmp1 的访问压力。关键不是盲目调大,而是匹配业务峰值查询的中间结果规模:
- 先查当前值:
SELECT @@max_heap_table_size, @@internal_tmp_mem_storage_engine;(默认通常是16777216和TempTable) - 设为相等且合理:例如
SET GLOBAL max_heap_table_size = 268435456;+SET GLOBAL internal_tmp_mem_storage_engine = 'TempTable'; - 必须写入配置文件(如
/etc/mysql/mysql.conf.d/mysqld.cnf),否则重启失效;旧配置中的tmp_table_size要彻底删除,它在 8.0 中被静默忽略 - 注意:该设置只影响新连接,老连接沿用启动时的值,调参后需等待连接自然更替或执行
KILL旧连接
清理残留临时表状态与重置 ibtmp1
升级后或异常关闭易留下孤立元数据,表现为看似无锁却持续卡在临时表创建阶段。这不是事务锁,但效果类似:
- 确认
innodb_temp_tablespaces_dir(默认datadir/#innodb_temp/)目录可写,且磁盘充足 - 查是否有残留:
SELECT NAME FROM information_schema.INNODB_TABLES WHERE NAME LIKE '#sql%';—— 若有,说明存在未清理的 DDL 临时表别名 - 安全做法是:停服务 → 删除
datadir/#innodb_temp/下所有temp_*.ibt文件 → 删除ibtmp1→ 启动 MySQL(它会自动重建) - 切勿在运行中手动删
ibtmp1,会导致实例崩溃
真正棘手的不是参数本身,而是“调参后没立刻生效”和“误把落盘率高当成锁问题”。临时表空间争用没有传统意义上的行锁等待链,它藏在 I/O 等待统计和错误日志里,得用对工具才看得见。











