索引並非萬能,錯誤的寫法會導致優化器放棄索引轉而全表掃描。本文總結五大典型失效場景、原理解析、EXPLAIN 驗證方法與優化重寫方案。
-- 失效:email 無索引
SELECT * FROM user WHERE phone = '138...' OR email = 'test@xx.com';
-- 優化:為 email 加索引,或改寫為 UNION ALL
SELECT * FROM user WHERE phone = '138...'
UNION ALL
SELECT * FROM user WHERE email = 'test@xx.com' AND phone != '138...';

原則:
OR兩側列必須都有索引才會使用 index merge,否則全表掃描。
-- 索引 idx_name_email(name, email)
-- 生效:使用最左列
SELECT * FROM user WHERE name = '張三';
-- 失效:跳過最左列
SELECT * FROM user WHERE email = 'test@xx.com';
複合索引 (a,b,c) 只有查詢條件包含 a、ab、abc 才能用到索引。
-- 失效
SELECT * FROM user WHERE name LIKE '%三';
-- 生效
SELECT * FROM user WHERE name LIKE '張%';

解決:全文索引、倒序索引 (存儲 reverse(name))、Elasticsearch、Trigram 索引 (pg_trgm)。
-- phone 是 VARCHAR(11)
-- 失效:數字常數導致隱式 CAST
SELECT * FROM user WHERE phone = 13800138000;
-- 生效:字符串常數
SELECT * FROM user WHERE phone = '13800138000';

MySQL 將字符串列與數字比較時,會將列值逐行轉為數字,導致索引不可用。永遠用引號包裹字符串常數。
ANALYZE TABLE 更新統計信息。| 列 | 關注點 |
|---|---|
type | ALL=全表掃描,ref/range/eq_ref=用到索引 |
possible_keys | 理論可用索引 |
key | 實際使用索引 |
key_len | 索引長度,推斷複合索引用了幾列 |
rows | 預估掃描行數 |
Extra | Using index(覆蓋索引)、Using where、Using filesort |
SHOW STATUS LIKE 'Handler_read%';
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_key | 高=好 | -- 索引讀取次數
| Handler_read_rnd_next | 低=好 | -- 全表掃描次數
+-----------------------+-------+