「在適當的表、適當的欄位上建立適當數量的索引」——這句口訣易記卻難行。濫建索引會拖垮寫入效能、佔用儲存、干擾優化器;欠建則導致全表掃描、應用超時。本文從表級決策、欄位特徵識別、數量平衡點三個維度,提供可量化、可驗證的判斷框架。
判斷公式:「常用查詢回傳比例」 = 典型 WHERE 條件回傳行數 / 表總行數
/*+ FULL(t) */ 或統計資訊修正-- 1. 收集統計資訊
EXEC DBMS_STATS.GATHER_TABLE_STATS('SCHEMA', 'BIG_TABLE', CASCADE=>TRUE);
-- 2. 取得典型查詢計畫
EXPLAIN PLAN FOR SELECT /*+ INDEX(big_table idx_col) */ * FROM big_table WHERE col = :val;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
-- 3. 強制全表掃描對比
EXPLAIN PLAN FOR SELECT /*+ FULL(big_table) */ * FROM big_table WHERE col = :val;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
-- 4. 實際執行統計(需 Enterprise Edition)
-- 啟用 SQL Trace 或使用 DBMS_SQLTUNE 對比 Elapsed Time、Buffer Gets、Disk Reads
決策:若索引計畫 Cost 明顯低於全表掃描,且實測一致,則建立索引。
| 表類型 | 索引策略 |
|---|---|
| 日誌/稽核表(僅寫、極少查) | 僅 PK,其餘不建 |
| 資料倉庫事實表 | 維度鍵建位圖索引、分區鍵 LOCAL 索引、不建 B-tree 高基數欄位 |
| 高頻 OLTP 核心表 | PK、FK、高選擇率業務欄位、複合索引支援 Top-N 排序 |
| 暫存/中介表 | 視ETL流程:載入前 DISABLE 索引、載入後 REBUILD |
DISTINCT 個數 / 總行數 > 0.9(接近唯一)CREATE INDEX idx_order_cust_status ON orders(customer_id, status); 先高選擇率後低選擇率CREATE BITMAP INDEX idx_order_status ON orders(status);CREATE INDEX idx_active_orders ON orders(status) INDEXING PARTIAL; 僅索引 status='ACTIVE' 的分區JOIN ON 條件欄位、外鍵欄位HASH JOIN 暴力掃描、NESTED LOOPS 驅動表無索引導致全表掃描-- 查詢:SELECT * FROM orders WHERE cust_id=:cid ORDER BY order_date DESC FETCH FIRST 10 ROWS ONLY
-- 最佳索引:CREATE INDEX idx_order_cust_dt ON orders(cust_id, order_date DESC);
-- 避免:FILESORT、全表掃描後排序
-- 查詢:WHERE UPPER(email) = 'USER@EXAMPLE.COM'
CREATE INDEX idx_user_upper_email ON users(UPPER(email));
-- 查詢:WHERE SUBSTR(id_no,1,1) = 'A'
CREATE INDEX idx_id_first_char ON users(SUBSTR(id_no,1,1));
查詢必須**完全匹配**索引定義的運算式,否則不會使用。
WHERE col IS NULL 無法使用單欄 B-tree 索引CREATE INDEX idx_col_not_null ON users(CASE WHEN col IS NULL THEN 1 END); 或複合索引加常數 CREATE INDEX idx_col_const ON users(col, 1);每個索引約增加 1.5-3 倍 的 DML 邏輯 I/O(視索引高度、分裂頻率)。
-- 監控表級 DML 與索引維護比例
SELECT segment_name, logical_reads, physical_reads, db_block_changes
FROM v$segment_statistics
WHERE owner = 'SCHEMA' AND statistic_name IN ('logical reads','physical reads','db block changes')
ORDER BY db_block_changes DESC;
| 系統類型 | 查詢/寫入比 | 索引數量傾向 | 策略 |
|---|---|---|---|
| 資料倉庫 / BI | 極高 / 極低 | 多(10-20+) | 載入時 DISABLE 索引,載入後 REBUILD;大量位圖索引、分區索引 |
| 混合負載 (HTAP) | 中等 | 適中(5-10) | 核心查詢路徑建複合索引,定期評估 INDEX_USAGE 刪除未用索引 |
| 高併發 OLTP | 低 / 高 | 少(3-6) | 僅 PK、FK、高選擇率業務鍵、關鍵排序複合索引;嚴禁低選擇率單欄索引 |
| 日誌/串流寫入 | 近零 / 極高 | 極少(1-2) | 僅 PK,考慮 IOT 或分區表無二級索引 |
DBA_INDEX_USAGE (19c+) 或 V$OBJECT_USAGE (監控模式) 找出長期未用索引ANALYZE INDEX ... VALIDATE STRUCTURE 檢查 HEIGHT、LF_ROWS/DEL_LF_ROWS 比例ALTER INDEX ... REBUILD ONLINE(企業版)、COALESCE(輕量合併葉節點)ALTER INDEX ... UNUSABLE 觀察 1 週 → 確無影響 → DROP INDEXINDEX_USAGE、DML 延遲、空間增長索引優化沒有終點,只有持續的「觀測-假設-驗證」循環。記住:最好的索引是「剛好夠用」的索引。