一、MySQL中的回表是什么?
我的回答:
MySQL回表指的是在查詢使用非聚簇索引也就是二級索引時,葉子節點只存儲了索引列的值和主鍵Id,若要查詢其他字段,就要根據主鍵去聚簇索引查詢完整的數據。這個過程就是回表。比如用name的二級索引查age,要先通過name找到主鍵id,再用這個主鍵id查詢age。回表會增加 IO ,所以可以建立覆蓋索引來包含所需要的字段,避免回表。我練過用聯合索引包含其他字段,減少了回表,查詢快了不少~
回答重點(官方答案):
“回表” 是指在使用二級索引(非聚簇索引)作為條件進行查詢時,由于二級索引中只存儲了索引字段的值和對應的主鍵值,無法得到其它數據。如果要查詢數據行中的其它數據,需要根據主鍵去聚簇索引查找實際的數據行,這個過程被稱為回表。
二、MySQL中使用索引一定有效嗎?如何排查索引效果?
我的回答:
MySQL中使用索引不一定有效,比如索引字段上有函數運算、使用了like '%xxx'這類前綴模糊匹配查詢、類型不匹配,或者統計信息不準確等, 都可能導致索引失效。
排查索引效果,我們可以使用explain分析SQL執行計劃,重點看type字段,(如all表示全表掃描)、key字段(是否為null),以及rows估算值
再通過show status like ‘Handler%’; 查看索引使用次數,對比查詢前后的Handler_read_key和Handler_read_rnd_next等指標。之前我在練習時,發現一條SQL有索引,但是執行慢,用explain發現是因為對索引字段做了函數操作,調整后索引生效,查詢效率顯著提升,所以現在寫SQL后都會習慣性用這些方法檢驗索引效果,避免出校“有索引但不生效”的情況。
回答重點(官方答案):
索引不一定有效。
例如查詢條件中不包含索引列、低基數列索引效果不佳,或查詢條件復雜且不匹配索引的順序。
對于一些小表,MySQL 可能選擇全表掃描而非使用索引,因為全表掃描的開銷可能更小。
最終是否用上索引是根據 MySQL 成本計算決定的,評估 CPU 和 I/O 成本最終選擇用輔助索引還是全表掃描。有時候確實是全表掃描成本低所以沒用上索引。但有時候由于一些統計數據的不準確,導致成本計算誤判,而沒用上索引。
排查索引效果的方法:使用 EXPLAIN 命令,通過在查詢前加上 EXPLAIN ,可以查看 MySQL 選擇的執行計劃,了解是否使用了索引、使用了哪個索引、估算的行數等信息。
主要觀察 EXPLAIN 結果以下幾點:
- type(訪問類型):這個屬性顯示了查詢使用的訪問方法,例如 ALL 、index 、range 等。當查詢使用索引時,這個屬性通常會顯示為 index 或 range ,表示查詢使用了索引訪問。如果這個值是 ALL ,則表示查詢執行了全表掃描,沒有使用索引。
- key(使用的索引):這個屬性顯示了查詢使用的索引,如果查詢使用了索引,則會顯示索引的名稱。如果這個值是 NULL ,則表示查詢沒有使用索引。
- rows(掃描的行數):這個屬性顯示了查詢掃描的行數,需要評估下掃描量。
擴展知識
確定索引真的生效了嗎?
索引失效的場景有很多,也是面試官經常喜歡問的,可以根據具體場景進行排查,典型場景可以分為以下幾點(實際索引的選擇會根據 mysql 優化器的成本評定,答案最后會提到):
使用了聯合索引卻不符合最左前綴
舉個例子:小魚對 user 表建立了一個聯合索引為 name_age_id 的聯合索引。
他使用以下 SQL 查詢 select * from user where age = 10 and id = 1;
這樣的寫法恰恰不滿足最左前綴原則,索引就失效啦。索引中使用了運算
例如這個 SQL select * from user where id + 3 = 8 。這樣會導致全表掃描計算 id 的值再進行比較,使得索引失效。索引上使用了函數也會失效
例如:select * from user where LOWER (name) like 'cong%'; 。
這樣也會導致索引失效,索引參與了函數處理,會導致去全表掃描。like 的隨意使用
例如:select * from user where name like '% cong%'; 因為索引是從左到右來進行排序查找的,占位符直接放在了最左邊開頭,可能會導致直接全表掃描,這種情況就會導致索引失效。or 的隨意使用
user 當前只有一個索引 name 。此時執行以下 SQL:
select * from user where name = 'cong' or age = 18; 這可能也會導致索引失效,因為 age 沒有索引。隨意的字段類型使用
不小心將 varchar 類型的 name 條件匹配了 int 類型字段。SQL 是這樣的 select * from user where name = 1; ,在代碼中涉及隱式轉換!等于 select * from user where CAST (name AS signed int) = 1; ,這就變成了第三條索引上使用了函數,導致索引失效。
除此之外還有隱式字符編碼轉換的問題,即聯表查詢的時候,如果不同表之間的關聯字段字符編碼不一致,也會導致隱式轉換編碼,等于變相用上了函數,使得索引失效。不同的參數也會導致索引失效
這個就是我在回答重點里面說的 “是否用上索引是根據 MySQL 成本計算決定的”。不同的參數 MySQL 評估成本不一致,有時候會選擇使用索引,有時候會選擇全表掃描,特別是在復雜查詢(聯表、子查詢、需要回表等)的情況下。
比如根據商品從訂單表查詢,收集商品對應的所有買家的訂單信息。如果傳入的商品 id 是個熱點商品,占據這家店鋪 80% 的銷量,那么本次查詢對訂單表很可能是全表查詢,如果是冷門商品,則很可能是走索引查詢。
8) 表中兩個不同字段進行比較
例如這樣的 SQL : select * from user where id > age; ,將 id 跟 age 字段做了比較,索引失效。
9) 使用了 order by
當 order by 后面跟的不是主鍵或者不是覆蓋索引會導致不走索引。
為什么索引生效了反而查詢變慢了呢?
確認是否選對了索引!MySQL 根據優化器會評估成本選擇對應的索引,但有時候 MySQL 因為估計值不準確,導致選錯了索引,因此查詢速度反而更慢。
三、在MySQL中建立索引時需要注意哪些事項?
我的回答:
我會注意:
1.按需創建索引:根據實際查詢需求,在where,join,order by等子句的字段上建索引,避免冗余。
2.會優先給唯一性高的,如id、手機號建立索引,像性別低選擇性字段建立索引意義不大。
3.若是聯合索引,遵循最左前綴原則,把選擇性高的字段放在前面,比如(a,b,c)的索引能支持a、a+b、a+b+c的查詢
4.避免失效場景,不在字段上使用函數,避免使用like %xxx、保證類型匹配,防止索引失效
5.權衡性能:索引會加快查詢,但是會降低增刪改的速度,還會占用磁盤空間,因此寫入頻繁的表要控制索引數量。
我練習的時候,給訂單表的user_id 和 create_time建立聯合索引,發現遵循最左原則的查詢能命中索引,但是加了低選擇性的status字段后反而失效,刪除后性能恢復了,這讓我明白索引設計要結合實際場景,不能盲目添加。
類型匹配解釋:
加了低選擇性的status字段,索引失效解釋:
重點回答(官方答案)
不能盲目建立索引,索引并不是越多越好,索引會占用空間,且每次修改的時候可能都需要維護索引的數據,消耗資源。
對于字段的值有大量重復的不要建立索引。比如說:性別字段,在這種重復比例很大的數據行中,建立索引也不能提高檢索速度。但是也不絕對,例如定時任務的場景,大部分任務都是成功,少部分任務狀態是失敗的,這時候通過失敗狀態去查詢任務,實際上能過濾大部分成功的任務,效率還是可以的。
對于一些長字段不應該建立索引。比如 text、longtext 這種類型字段不應該建立索引。因為占據的內存大,掃描的時候大量加載至內存中還耗時,使得提升的性能可能不明顯,甚至可能還會降低整體的性能,因為別的緩存數據可能因為它被踢出內存,下次查詢還需要從磁盤中獲取。
當數據表的修改頻率遠大于查詢頻率時,應該好好考慮是否需要建立索引。因為建立索引會減慢修改的效率,如果很少的查詢較多的修改,則得不償失。
對于需要頻繁作為條件查詢的字段應該建立索引。在 where 關鍵詞后經常查詢的字段,建立索引能提高查詢的效率,如果有多個條件經常一起查詢,則可以考慮聯合索引,減少索引數量。
對經常在 order by、group by、distinct 后面的字段建立索引。這些操作通常需要對結果進行排序、分組或者去重,而索引可以幫助加快這些操作的速度。
擴展知識
MySQL 索引的最左前綴匹配原則是什么?
回答重點
MySQL 索引的最左前綴匹配原則指的是在使用聯合索引時,
查詢條件必須從索引的最左側開始匹配。如果一個聯合索引包含多個列,查詢條件必須包含第一個列的條件,然后是第二個列,以此類推。
底層原理:因為聯合索引在 B+ 樹中的排列方式遵循 “從左到右” 的順序,例如聯合索引 (first_name, last_name, age) 會按照 (first_name, last_name, age) 的順序在 B+ 樹中進行排序。
MySQL 在查找時會優先使用 first_name 作為匹配依據,然后依次使用 last_name 和 age 。因此,組合索引能夠從左到右依次高效匹配,跳過最左側字段會導致無法利用該索引。
按照 (first_name, last_name, age) 的順序在 B+ 樹中的排列方式 (大致的示意圖) 如下