第 18 章
临时表与内存表
MySQL 临时表与内存表优化
临时表和基于内存的存储启用高效的中间操作。理解它们的权衡对于查询优化至关重要。
1. 临时存储类型
自动临时表:
MySQL为以下操作创建临时表:
├─ GROUP BY无序
├─ DISTINCT非索引列
├─ 复杂JOIN的中间结果
监控:
SHOW STATUS LIKE 'Created_tmp_tables';
SHOW STATUS LIKE 'Created_tmp_disk_tables';
如果 Created_tmp_disk_tables 很高:
└─ 问题:磁盘I/O比内存慢得多
解决方案:
SET GLOBAL tmp_table_size = 512M;
SET GLOBAL max_heap_table_size = 512M;
显式临时表:
CREATE TEMPORARY TABLE temp_results (
id INT,
name VARCHAR(100),
total DECIMAL(10,2)
);
特点:
├─ 仅对创建会话可见
├─ 连接关闭时自动删除
├─ 可以有索引、约束等
└─ 可以使用任何存储引擎
2. MEMORY vs InnoDB
MEMORY引擎:
CREATE TABLE mem_table (
id INT PRIMARY KEY,
name VARCHAR(100)
) ENGINE = MEMORY;
特点:
├─ 整个表在RAM中
├─ 哈希索引(O(1)查询)
├─ 重启时丢失
├─ 受限于 max_heap_table_size
性能:
├─ 读:非常快(内存访问)
├─ 写:快但单线程
├─ 大小限制:可能溢出到磁盘
InnoDB with缓冲池:
特点:
├─ 持久化到磁盘
├─ ACID事务
├─ 行级锁定
性能:
├─ 读:缓冲池中快,磁盘上慢
├─ 写:中等(重做日志、事务开销)
└─ 可以超过内存大小
选择:
用MEMORY表示:
├─ 小参考表(<100MB)
├─ 会话状态/缓存
└─ 临时聚合(保证能拟合)
用InnoDB表示:
├─ 大临时结果
├─ 复杂多步查询
└─ 需要ACID保证
3. 调优临时表性能
问题:查询创建巨大磁盘临时表
监控:
SHOW STATUS LIKE 'Created_tmp_disk_tables';
-- 结果高:1, 000, 000(很糟!)
解决方案1:增加内存限制
SET GLOBAL tmp_table_size = 2G;
SET GLOBAL max_heap_table_size = 2G;
解决方案2:优化查询避免临时表
❌ 慢(创建临时表):
SELECT DISTINCT country FROM users;
✓ 快(使用索引):
CREATE INDEX idx_country ON users(country);
SELECT DISTINCT country FROM users;
解决方案3:显式临时表+索引
CREATE TEMPORARY TABLE temp_users AS
SELECT * FROM users WHERE age > 18;
CREATE INDEX idx_country ON temp_users(country);
SELECT DISTINCT country FROM temp_users;
解决方案4:批处理
✓ 分批处理(较小临时表):
FOR i = 0 TO 25 DO
SELECT name FROM users
WHERE name LIKE CONCAT(CHAR(65+i), '%');
END FOR
4. 常见用例
去重:
✓ 快速方法:
CREATE TEMPORARY TABLE temp_dedup AS
SELECT MIN(id) as keep_id FROM events GROUP BY event_key;
DELETE FROM events WHERE id NOT IN (SELECT keep_id FROM temp_dedup);
复杂ETL:
提取 → 验证 → 转换 → 加载
每步使用临时表,最后加载到生产表
A/B测试分析:
创建两个临时表(变体A和B)
进行统计比较
5. 内存管理
监控RAM使用:
-- 查看当前临时表活动
SELECT * FROM INFORMATION_SCHEMA.INNODB_TEMP_TABLE_INFO;
-- 监控创建速率
SHOW GLOBAL STATUS LIKE 'Created_tmp%';
配置:
-- 内存设置
max_heap_table_size(默认 16MB)
tmp_table_size(默认 16MB)
这些限制MEMORY引擎表大小
如果超过,转换为基于磁盘的InnoDB
示例:增加大型聚合的内存
SET GLOBAL max_heap_table_size = 512M;
SET GLOBAL tmp_table_size = 512M;
5. 最佳实践
- ✓ 给临时表添加索引——特别是后续查询
- ✓ 增加 tmp_table_size——如果临时表经常溢出
- ✓ 监控 Created_tmp_disk_tables——性能问题指示
- ✓ 用MEMORY表示小查找表——快速哈希查询
- ✓ 批处理大操作——减少临时表大小
- ✗ 不要忘记清理临时表——长时间连接
- ✓ 分析临时表创建——优化创建它们的查询
- ✓ 测试内存配置——为工作负载找到最优
- ✓ 记录复杂逻辑——临时表操作可能难以理解
总结
临时表对于复杂数据处理至关重要,但如果管理不当会成为瓶颈。通过优化查询、使用适当的存储引擎(MEMORY vs InnoDB)和监控创建率,可保持良好性能。对于复杂操作,显式带索引的临时表通常优于隐式MySQL生成的临时表。