临时表不是缓存,而是会话或事务级中间容器;其生命周期绑定会话,无法跨请求共享数据,真需缓存应依赖查询计划复用、物化视图或Redis等外部缓存层。

绝大多数情况不该用临时表“缓存”热点数据——它不是缓存,是会话隔离的中间容器;真要缓存,得靠查询计划复用、内存中物化视图或外部缓存层。
为什么#temp不能当缓存用
本地临时表 #temp 的生命周期绑定当前会话,Web 应用里一个连接池里的连接可能被多次复用,但每次执行存储过程仍是独立会话;##temp 虽全局可见,却极易引发并发写冲突和脏读。所谓“缓存热点数据”,本质是跨请求共享结果,而临时表天生不具备这个能力。
- 每次调用存储过程,
#tmp_stats都是全新创建,上一次的数据彻底丢失 - 若误用
##tmp_stats,A 用户刚算完的统计值,B 用户下一条 SELECT 就可能读到,也可能被覆盖 - 即使加了
DROP TABLE IF EXISTS #tmp_stats,也只解决重复建表报错,不解决“复用”问题
真正能复用中间结果的三种方式
想避免重复计算高频查询(比如“昨日订单总数”“活跃用户数”),该选下面这些路径,而不是硬塞进临时表:
-
sys.dm_exec_query_stats+plan_handle复用:确保参数稳定、SET 选项一致,让 SQL Server 直接复用已编译计划,跳过解析+优化阶段 - 带
ON COMMIT DROP的 PostgreSQL 临时表仅适用于单次事务内多步引用,不是跨请求缓存 - MySQL 可用
CREATE TEMPORARY TABLE配合 event 定时刷新,但仍是“预生成”,非“按需缓存”;且必须配DROP TEMPORARY TABLE IF EXISTS,否则第二次 event 执行直接失败
临时表在什么场景下“看起来像缓存”
某些报表类存储过程内部,会把聚合结果先写入 #daily_summary,再反复 JOIN 或过滤——这其实是**单次执行内的中间结果重用**,不是跨请求缓存。此时要注意:
- 字段类型别隐式退化:MySQL 中含
TEXT的临时表会从MEMORY引擎降级为磁盘MyISAM,查得慢 - SQL Server 中行数超 1000 优先用
#temp而非@table,因优化器对前者有真实统计信息,JOIN 代价估算更准 - 别在
#temp上建太多非聚集索引——建索引本身耗时,且tempdb日志压力会上升
如果真要实现跨请求“缓存热点数据”,该怎么做
临时表不是答案,但可以组合使用:
- 用持久表(如
cache_order_summary)存预计算结果,加last_updated DATETIME2字段,应用层控制刷新频率 - SQL Server 可启用
QUERY_STORE,自动捕获高频查询及其执行计划,配合强制计划指南(Plan Guide)固化高效路径 - 更彻底的解法:把聚合逻辑下沉到应用层,用 Redis 缓存 JSON 结果,数据库只负责原子写入,读走缓存
临时表的定位始终是“单次执行的草稿纸”,不是“共享记事本”。混淆这两者,轻则性能不升反降,重则在高并发下出现不可预测的数据交叉。











