大表頻繁執行複雜查詢,涉及多表 JOIN、篩選、排序,執行計劃顯示 Using temporary; Using filesort,響應時間秒級甚至超時。
filesort
SELECT c.name, r.balance, ...other columns
FROM customers c
JOIN receivables r ON c.customer_id = r.customer_id
WHERE r.balance > 0
AND c.postcode > '98000'
ORDER BY c.name;
執行計劃痛點:
customers 全表掃描(無 postcode 索引)receivables 全表掃描(無 balance 索引)ORDER BY c.name → Using filesorttmp_table_size) → Using temporary
-- 步驟 1:創建臨時表,預先 JOIN、篩選、排序
CREATE TEMPORARY TABLE tmp_cust_balance AS
SELECT c.customer_id, c.name, c.postcode, r.balance, ...other columns
FROM customers c
JOIN receivables r ON c.customer_id = r.customer_id
WHERE r.balance > 0
ORDER BY c.name; -- 關鍵:建表時完成排序
-- 步驟 2:創建針對性索引(臨時表支持索引!)
ALTER TABLE tmp_cust_balance ADD INDEX idx_postcode (postcode);
-- 步驟 3:輕量級查詢臨時表
SELECT * FROM tmp_cust_balance WHERE postcode > '98000';
-- 步驟 4:會話結束自動清理,或顯式 DROP
DROP TEMPORARY TABLE IF EXISTS tmp_cust_balance;
| 指標 | 原始查詢 | 臨時表方案 | 提升 |
|---|---|---|---|
| 掃描行數 | ~500萬 (兩表全掃) | ~5萬 (臨時表篩選) | 100x |
| 排序操作 | Filesort 磁盤 | 建表時內存排序 | 消除 filesort |
| 響應時間 | 8.2 秒 | 0.05 秒 | 160x |
| 併發影響 | 長事務阻塞 | 極短鎖持有 | 大幅降低 |
-- 顯式指定引擎(默認 Memory,數據大時自動轉 MyISAM)
CREATE TEMPORARY TABLE tmp_xxx ENGINE=InnoDB SELECT ...;
-- InnoDB 臨時表:支持事務、行鎖、崩潰安全,適合大數據量
-- Memory 引擎:全內存,極快,但受 max_heap_table_size 限制,不支持 BLOB/TEXT
ALTER TABLE ... ADD INDEX,**查詢模式決定索引**WHERE + ORDER BY + GROUP BY 列
-- 監控臨時表大小
SHOW STATUS LIKE 'Created_tmp_disk_tables'; -- 磁盤臨時表計數
SHOW STATUS LIKE 'Created_tmp_tables'; -- 總臨時表計數
-- 控制參數
tmp_table_size = 64M
max_heap_table_size = 64M
-- 超過此大小自動轉 MyISAM 磁盤表,性能驟降
MySQL 8.0+ 支持 WITH 子句,語義等價但更優雅:
WITH cust_balance AS (
SELECT c.customer_id, c.name, c.postcode, r.balance
FROM customers c
JOIN receivables r ON c.customer_id = r.customer_id
WHERE r.balance > 0
)
SELECT * FROM cust_balance WHERE postcode > '98000';
DROP,語句級作用域,無洩漏風險臨時表是**「以空間換時間、以預計算換即時計算」**的經典優化手法。關鍵在於:
Created_tmp_tables 暴增WITH CTE,兼具性能與可讀性合理使用,能將複雜報表查詢從「秒級」推向「毫秒級」,是 DBA 與開發必備的利器。