選擇性 = 滿足條件的行數 / 總行數。值越小(越接近 0),索引越有效;值越大(接近 1),全表掃描越划算。
CREATE TABLE r2 (
id INT DEFAULT NULL,
id1 INT DEFAULT NULL,
cname VARCHAR(32) DEFAULT NULL,
KEY id1 (id1)
) ENGINE=MyISAM;
-- 總行數
SELECT COUNT(*) FROM r2; -- 250,001 (V1)
-- 條件 id1=1 的行數
SELECT COUNT(*) FROM r2 WHERE id1=1; -- 83,036 (V2)
-- 選擇性 = 83036 / 250001 = 0.3321 (33.21%)
EXPLAIN SELECT COUNT(cname) FROM r2 WHERE id1=1;
+----+-------------+-------+------+---------------+------+---------+-------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+-------+--------+-------------+
| 1 | SIMPLE | r2 | ref | id1 | id1 | 5 | const | 81371 | Using where |
+----+-------------+-------+------+---------------+------+---------+-------+--------+-------------+
結果:MySQL 選擇走索引(type=ref),預估掃描 81,371 行(接近實際 83,036)。
Cost = IO_Cost + CPU_Cost
IO_Cost ≈ 預估訪問頁數 × IO_權重
CPU_Cost ≈ 預估處理行數 × CPU_權重
若索引為**二級索引**,MySQL 還需考慮**回表成本**(每行隨機 IO 讀取聚簇索引)。
ANALYZE TABLE 及時更新,優化器預估行數接近真實值。id1, cname 且建立 KEY idx_id1_cname (id1, cname),可避免回表,成本進一步下降。| 手段 | 語法 | 場景 |
|---|---|---|
| 強制索引 | SELECT * FROM t FORCE INDEX (idx) WHERE ... | 優化器誤判、統計過期 |
| 忽略索引 | SELECT * FROM t IGNORE INDEX (idx) WHERE ... | 確知全表掃描更快 |
| 更新統計 | ANALYZE TABLE t; | 大量增刪改後 |
| 調整成本常數 | SET optimizer_switch='index_condition_pushdown=on'; | 5.6+ ICP 下推優化 |
EXPLAIN 實測為準。ANALYZE TABLE** 保持統計資訊新鮮。Handler_read_next / Handler_read_rnd_next** 觀察實際索引 vs 全掃行為。MySQL 索引選擇性決策基於**成本模型**而非固定百分比。33% 選擇性走索引並非 Bug,而是優化器在該資料分布、表大小、統計資訊下的理性選擇。DBA 應掌握 EXPLAIN、覆蓋索引、統計維護三板斧,引導優化器做出最佳決策。