收到負載告警,load average 280+,CPU 336%,IO/內存正常。經驗判斷:高 CPU 低 IO 多為索引問題。定位高頻 SQL,發現優化器選擇 index_merge 且掃描行數尚可但執行計劃不優。將兩個單列索引合併為聯合索引,CPU 瞬降至 12% 以下。本文完整復盤優化過程。
-- 高頻 SQL SELECT count(1) FROM a, b WHERE a.id = b.video_id AND b.state = 1 AND b.column_id = '81'; -- 原執行計劃 EXPLAIN ...\G *************************** 1. row *************************** table: b type: index_merge possible_keys: columnid_videoid,column_id,state,video_time_stamp,idx_videoid key: column_id,state key_len: 4,4 rows: 100 Extra: Using intersect(column_id,state); Using where *************************** 2. row *************************** table: a type: eq_ref key: PRIMARY rows: 1 Extra: Using where; Using index
表 b 使用 index_merge 合併 column_id 和 state 兩個單列索引,state 基數極低(Cardinality=5),索引效果極差。
SHOW INDEX FROM b\G Key_name: column_id (Cardinality: 8374) Key_name: state (Cardinality: 5) -- 只有 5 個不同值,幾乎無區分度
ALTER TABLE b DROP INDEX column_id, DROP INDEX state; ALTER TABLE b ADD INDEX idx_columnid_state (column_id, state);
EXPLAIN ...\G *************************** 1. row *************************** table: b type: ref possible_keys: columnid_videoid,idx_videoid,idx_columnid_state key: idx_columnid_state key_len: 8 ref: const rows: 199 Extra: Using where
type 從 index_merge 變為 ref,單一索引查找。ref 從 NULL 變為 const,常數等值查找。rows 從 100 降為 199(實際掃描更少)。state 只有 5 個值,索引幾乎等同全表掃描,參與 merge 純屬負擔。(column_id, state) 直接定位符合條件的行,且 state 在索引中可直接過濾,無需回表。CPU 持續高、IO/內存正常 → 十有八九是索引問題。
index_merge、Using temporary、Using filesort、全表掃描的高頻 SQL。pt-index-usage 或 sys.schema_unused_indexes 清理無用索引。| 指標 | 優化前 | 優化後 |
|---|---|---|
| 執行計劃類型 | index_merge | ref |
| 使用索引 | column_id, state | idx_columnid_state |
| 掃描行數 | 100 | 199 |
| 執行耗時 | 0.07s | 0.00s |
| CPU 佔用 | 336% | <12% |