業界公認:應用程序/SQL 優化佔 60%-80% 性能提升,硬件/OS/參數僅佔 20%-40%。優化介入越早(設計階段),成本越低、收益越大。
| 技術 | 適用場景 | 代價 |
|---|---|---|
| 水平分表 | 單表超大,按範圍/哈希分區 | 應用層需路由,跨分區查詢複雜 |
| 垂直分表 | 冷熱列分離,寬表變窄表 | 主鍵關聯查詢,事務一致性難保證 |
| 冗余列 | 頻繁 JOIN 的維度屬性下推 | 數據同步邏輯複雜,佔用空間 |
| 派生列/物化視圖 | 複雜聚合計算預計算 | 刷新延遲,存儲開銷 |
含 NULL 的列不會被普通 B*Tree 索引收錄(除非建立位圖索引或函數索引)。
WHERE col IS NULL 或 IS NOT NULL 導致全表掃描。
對策: 設計階段定義 NOT NULL DEFAULT 'N/A';或創建函數索引 CREATE INDEX idx ON t(NVL(col, 'NULL'))。
-- 錯誤:函數/運算作用在索引列上
SELECT * FROM emp WHERE first_name || ' ' || last_name = 'George Bush';
SELECT * FROM emp WHERE sal * 12 > 30000;
SELECT * FROM emp WHERE UPPER(ename) = 'SMITH';
-- 正確:將運算移至常量側
SELECT * FROM emp WHERE first_name = 'George' AND last_name = 'Bush';
SELECT * FROM emp WHERE sal > 2500;
SELECT * FROM emp WHERE ename = 'SMITH'; -- 假設數據已統一大小寫
原則: 索引列保持「純淨」,不參與任何表達式計算。
-- 危險:數字列與字符串比較
WHERE empno = '7369' -- Oracle 隱式 TO_NUMBER('7369'),索引可能失效
-- 安全:顯式轉換或字面量匹配
WHERE empno = 7369
WHERE char_col = '7369'
LIKE 'ABC%':可用索引範圍掃描LIKE '%ABC%':全表掃描(考慮 Oracle Text / 函數索引)-- 可能導致全表掃描
SELECT * FROM t WHERE a = 1 OR b = 2;
-- 改寫為 UNION ALL(若互斥)或 UNION
SELECT * FROM t WHERE a = 1
UNION ALL
SELECT * FROM t WHERE b = 2 AND a != 1;
DBMS_STATS.GATHER_TABLE_STATS,設置 METHOD_OPT => 'FOR ALL COLUMNS SIZE AUTO' 收集直方圖。EXPLAIN PLAN + DBMS_XPLAN.DISPLAY 分析 Cardinality 偏差。SQL Plan Baseline / Outline / Hint。| 現象 | 可能原因 | 對策 |
|---|---|---|
| 全表掃描 | 無索引、統計信息缺失、函數操作索引列、小表 | 建索引、收集統計、改寫 SQL、Hint |
| 索引範圍掃描回表過多 | 選擇率高、聚簇因子差 | 考慮全表掃描、重建表/索引、覆蓋索引 |
| NESTED LOOPS 慢 | 驅動表大、被驅動表無索引 | 調整驅動順序、建索引、改 HASH JOIN |
| SORT MERGE JOIN | 無索引、大表連接 | 建索引轉 NLJ、或並行 HASH JOIN |
| TEMP 表空間暴增 | 大排序、HASH JOIN 溢寫 | 增加 PGA、優化 SQL 減少排序 |
總結: SQL 優化無銀彈,但「範式設計正確、統計信息準確、WHERE 子句保持索引列純淨、善用執行計劃驗證」這四大支柱能解決 80% 以上性能問題。養成 EXPLAIN PLAN 先行的習慣,是從寫 SQL 到寫好 SQL 的分水嶺。