临时表不加速dml本身,但能优化复杂前置逻辑;关键在于保持内存驻留、避免落地、合理设计字段/引擎/索引,并监控created_tmp_disk_tables等指标验证效果。

临时表对DML(INSERT/UPDATE/DELETE)本身不提速,但能显著优化“围绕DML的复杂前置逻辑”——比如批量更新前的条件计算、多步数据校验、跨表关联过滤等。关键不是用不用临时表,而是让临时表真正留在内存里、不落地、不拖慢主事务。
为什么直接在DML里建临时表反而更慢
MySQL的CREATE TEMPORARY TABLE是DDL操作,会触发MDL写锁,哪怕只持续几毫秒,在高并发DML场景下也容易成为瓶颈;更常见的是,临时表一旦含TEXT、BLOB字段或行太宽,立刻被降级为磁盘表,后续UPDATE JOIN或DELETE ... IN (SELECT ...)就会变成全表扫描+磁盘随机IO。
- 显式临时表若没加索引,
JOIN或WHERE IN时走不了索引,比直接写子查询还慢 - 临时表默认引擎由
internal_tmp_mem_storage_engine控制(MySQL 8.0+推荐TempTable),但你CREATE TEMPORARY TABLE ... ENGINE=MEMORY时,仍受max_heap_table_size限制,超限就静默转磁盘 - 长事务中不
DROP TEMPORARY TABLE,该表占用的内存不会释放,持续挤压tmp_table_size配额
哪些DML场景真适合用临时表加速
不是所有“带计算的DML”都该上临时表。真正值得建的,是那些“结果集稳定、复用多次、且原表无法高效过滤”的中间数据。
-
UPDATE t1 SET status = 'processed' WHERE id IN (SELECT id FROM huge_log_table WHERE event_type = 'click' AND ts > NOW() - INTERVAL 1 DAY GROUP BY user_id HAVING COUNT(*) > 5)→ 这种嵌套GROUP BY极易落盘,应先存到带索引的临时表 - 需要原子性地执行“先查一批ID → 校验业务规则 → 再批量更新”,而校验逻辑无法下推到单条SQL中(比如调用函数、跨库查数)
- 多张大表
JOIN后生成待更新ID列表,且该列表后续要用于UPDATE和INSERT INTO ... SELECT两步操作
怎么建才不踩坑:字段、引擎、索引三要素
临时表性能断崖往往发生在建表那一刻。重点不是语法多炫,而是字段精简、引擎可控、索引匹配后续DML模式。
- 字段只留DML真正需要的列:比如只需
user_id做IN或JOIN,就别SELECT *带memo TEXT进来 - 显式指定
ENGINE=InnoDB而非MEMORY:虽然MEMORY快,但它不支持事务、崩溃即丢,且一旦有TEXT/BLOB或超限就强制转磁盘;InnoDB临时表在MySQL 8.0+已足够快,还能保证DML一致性 - 主键或唯一索引必须建:哪怕只是
PRIMARY KEY (id),否则UPDATE t1 JOIN tmp ON t1.id = tmp.id会变全表扫描 - 如果后续用
WHERE created_at BETWEEN ? AND ?过滤,就在临时表上建INDEX idx_time (created_at),别依赖隐式排序
必须监控的两个状态变量
别信“我设了256M就肯定走内存”。真实行为得看MySQL自己报的数:
- 执行完DML相关语句后,立刻查
SHOW STATUS LIKE 'Created_tmp%':Created_tmp_disk_tables值非零,说明你的临时表已经落地,优化白做了 - 检查
SHOW VARIABLES LIKE '%table_size%',确认tmp_table_size和max_heap_table_size数值完全一致——差1字节都会取小值,导致ENGINE=MEMORY表提前落盘 - 对关键DML语句跑
EXPLAIN FORMAT=TREE,看是否出现Using temporary(这是内部临时表),再结合Created_tmp_disk_tables判断它到底在内存还是磁盘
最易被忽略的一点:临时表不是银弹。当UPDATE本身涉及大范围行锁或二级索引回表时,临时表带来的收益可能被锁等待吞掉。先用pt-query-digest确认瓶颈真在中间计算,再动手建表。











