1. 一个真实的性能事故现场
去年双十一大促时,我们的订单系统突然出现响应延迟。监控显示数据库服务器CPU飙到90%,磁盘IO持续爆红。DBA抓取到一条诡异的SQL正在疯狂创建临时表:
这个存储过程在测试环境运行良好,却在生产环境酿成灾难。根本原因是:临时表创建频率过高导致内存碎片化,最终触发磁盘临时表转换。接下来我们详细拆解这个"血案"。
2. 临时表的双重面孔
2.1 内存临时表(The Good)
当结果集较小时,MySQL使用Memory引擎创建临时表:
执行计划显示"Using temporary",但整个过程在内存完成,速度堪比高铁。
2.2 磁盘临时表(The Bad)
当数据超过tmp_table_size(默认16MB)时,画风突变:
此时MySQL会改用MyISAM引擎创建磁盘临时表,性能断崖式下跌,就像早高峰的地铁换乘。
3. 六大作死姿势排行榜
3.1 连环创建案
文章开头的存储过程就是典型反模式。每次循环都创建/销毁临时表,相当于在高速公路上频繁启停汽车。
优化方案:
3.2 隐式转换陷阱
这个隐式转换会导致全表扫描,解决方案:
4. 高阶玩家必备技巧
4.1 查询重写艺术
原查询:
优化后:
4.2 引擎选择策略
强制使用内存临时表:
但要注意Memory引擎的短板:
- 不支持BLOB/TEXT类型
- 哈希索引不适合范围查询
- 默认使用固定长度行存储
5. 关联技术:索引的救赎
当看到"Using temporary"时,先别急着甩锅给临时表。合适的索引可能让临时表消失:
更好的复合索引:
6. 监控与调优工具箱
6.1 实时诊断命令
6.2 性能参数调优
7. 避坑指南:什么时候该用临时表
7.1 推荐场景
- 分页缓存:缓存复杂查询的中间结果
- 数据清洗:ETL过程中的暂存操作
- 会话级计算:用户自定义报表生成
7.2 替代方案
- 使用CTE(公共表表达式)
- 利用物化视图(MySQL需通过触发器实现)
- 应用层缓存(Redis/Memcached)
8. 血的教训总结
- 临时表不是洪水猛兽,但要用在刀尖上
- 监控Created_tmp_disk_tables指标如同检查汽车油表
- 复杂的多步骤操作,CTE可能是更好的选择
- 永远在开发环境测试真实数据量
- 定期检查隐式类型转换和字符集问题
最后分享一个真实案例:某电商平台通过优化临时表使用,将订单导出性能从45分钟提升到3分钟。他们做了三件事:
- 将循环内的临时表改为批处理
- 增加复合索引减少中间结果集
- 调整tmpdir到内存文件系统
记住:临时表就像手术刀,用得好能救命,用不好会要命。保持敬畏,善用工具,方能在数据库优化的江湖中立于不败之地。