MySQL 索引失效場景總結

查詢條件有 or

假設在 customer_name 字段設置了普通索引,執行以下 sql:

# type: ref, possible_keys: idx_customer_name, key: idx_customer_name
# idx_customer_name 索引生效
explain select id, customer_name, company_name from t_user_info where customer_name = 'test_name'

# type: ref, possible_keys: idx_customer_name, key: idx_customer_name
# idx_customer_name 索引生效
explain select id, customer_name, company_name from t_user_info where customer_name = 'test_name' and company_name = 'test_name'

# type: all, possible_keys: idx_customer_name, key: null
# idx_customer_name 索引不生效,使用全表掃描
explain select id, customer_name, company_name from t_user_info where customer_name = 'test_name' or company_name = 'test_company'

like 查詢以 % 開頭

假設在 customer_name 字段設置了普通索引,執行以下 sql:

# type: all, possible_keys: null, key: null
# idx_customer_name 索引不生效
explain select id, customer_name, company_name from t_user_info where customer_name like '%name'

# type: range, possible_keys: idx_customer_name, key: idx_customer_name
# idx_customer_name 索引生效
explain select id, customer_name, company_name from t_user_info where customer_name like 'test%'

如果希望以 % 開頭仍使用索引,則需要使用覆蓋索引,即只查詢帶索引字段的列

# type: index, possible_keys: null, key: idx_customer_name
# idx_customer_name 索引生效
# id 是主鍵,idx_customer_name 構成的 b+tree 除了有 customer_name,也包含用於指向對應行的 id
explain select id, customer_name from t_user_info where customer_name like '%name'

索引列參與運算

假設 id 字段爲主鍵,執行以下 sql:

# type: const, possible: primary, key: primary
# idx_id 索引生效
explain select id, customer_name, company_name from t_user_info where id = 2

# type: all, possible: null, key: null
# idx_id 索引不生效
explain select id, customer_name, company_name from t_user_info where id + 1 = 2

索引列使用函數

假設在 customer_name 字段設置了普通索引,執行以下 sql:

# type: ref, possible_keys: idx_customer_name, key: idx_customer_name
# idx_customer_name 索生效
explain select id, customer_name, company_name from t_user_info where customer_name = '查理一世'

# type: all, possible_keys: null, key: null
# idx_customer_name 索引不生效
explain select id, customer_name, company_name from t_user_info where substr(customer_name, 1, 3) = '查理一'

類型轉換

假設在 customer_name 字段設置了普通索引,執行以下 sql:

# type: all, possible_keys: idx_customer_name, key: null
# idx_customer_name 索引不生效
explain select id, customer_name, company_name from t_user_info where customer_name = 10

這是因爲 mysql 會自動對字段執行類型轉換函數,如上 sql 相當於

select id, customer_name, company_name from t_user_info where cast(customer_name as signed) = 10

兩列做比較

如果兩個列數據都有索引,但在查詢條件中對兩列數據進行了對比操作,則會導致索引失效

假設在 customer_name、company_name 字段設置了普通索引,執行以下 sql,僅作示例:

# type: range, possible_keys: idx_customer_name, key: idx_customer_name
# idx_customer_name 索引生效
explain select id, customer_name, company_name from t_user_info where customer_name > '查理一世'

# type: all, possible_keys: null, key: null
# idx_customer_name 索引生效
explain select id, customer_name, company_name from t_user_info where customer_name > company_name

聯合索引不滿足最左匹配原則

聯合索引遵從最左匹配原則,所謂最左匹配原則,就是如果 SQL 語句用到了聯合索引中的最左邊的索引,那麼這條 SQL 語句就可以利用這個聯合索引去進行匹配。值得注意的是,當遇到範圍查詢(>、<、between、like)時就會停止匹配

假設對 a、b、c 字段建立聯合索引 idx_a_b_c,執行 sql 如下:

# type: ref, possible_keys: idx_a_b_c, key: idx_a_b_c, ref: const
# idx_a_b_c 索引生效,a 字段能用到索引
explain select * from test_table where a = 1

# type: ref, possible_keys: idx_a_b_c, key: idx_a_b_c, ref: const, const
# idx_a_b_c 索引生效,a、b 字段能用到索引
explain select * from test_table where a = 1 and b = 2

# type: ref, possible_keys: idx_a_b_c, key: idx_a_b_c, ref: const, const, const
# idx_a_b_c 索引生效,a、b、c 字段能用到索引
explain select * from test_table where a = 1 and b = 2 and c = 3

# type: ref, possible_keys: idx_a_b_c, key: idx_a_b_c, ref: const, const, const
# idx_a_b_c 索引生效,a、b、c 字段能用到索引,優化器會調整 a、b、c 的順序,從而用上索引
explain select * from test_table where b = 2 and c = 3 and a = 1

# type: range, possible_keys: idx_a_b_c, key: idx_a_b_c, ref: null, key_len: 75
# a 字段類型爲 varchar(18),字符集爲 utf8mb4,1 個字符佔 4 個字節,佔用 4*18=72 字節
# varchar 爲變長數據類型,額外佔用 2 個字節
# 字段默認爲 null,額外佔用 1 個字節
# 因此 key_len = 72 + 2 + 1 = 75,可判斷 idx_a_b_c 索引生效,但只有 a 字段用到索引
explain select * from test_table where a > 1 and b = 2 and c = 3

我們知道索引是用 B+Tree 實現的,如果只對 a 字段建立普通索引,那麼 B+Tree 根據 a 字段排序。如果對 a、b、c 建立聯合索引,那麼首先根據 a 字段排序,如果 a 字段值相同,再根據 b 字段排序,如果 b 字段值也相同,再根據 c 字段排序。因此,使用聯合索引必須按照從左到右,也就是字段排序的順序,只有先用了 a,才能接着使用 b,使用了 b 才能接着使用 c

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章