mysql優化:覆蓋索引(延遲關聯)


前言

上周新系統改版上線,上線第二天就出現了較多的線上慢sql查詢,緊接着dba 給出了定位及解決方案,這里較多的是使用延遲關聯去優化。
而我對於這個延遲關聯也是第一次聽說(o(╥﹏╥)o),所以今天一定要學習並產出一篇學習筆記。(^▽^)

回表

我們都知道InnoDB采用的B+ tree來實現索引的,索引又分為主鍵索引(聚簇索引)和普通索引(二級索引)。
那么我們就來看下基於主鍵索引和普通索引的查詢有什么區別?

  • 如果語句是select * from T where ID=500,即主鍵查詢方式,則只需要搜索ID這棵B+樹;
  • 如果語句是select * from T where k=5,即普通索引查詢方式,則需要先搜索k索引樹,得到ID的值為500,再到ID索引樹搜索一次。這個過程稱為回表。

舉個栗子:

 
 

可以看出我們有一個普通索引k,那么兩顆B+樹的示意圖如下:


(注:圖來自極客時間專欄)

當我們查詢 select * from T where k=5 其實會先到k那個索引樹上查詢k = 5,然后找到對應的id為500,最后回表到主鍵索引的索引樹找返回所需數據。
如果我們查詢select id from T where k=5 則不需要回表就直接返回。
也就是說,基於非主鍵索引的查詢需要多掃描一棵索引樹。因此,我們在應用中應該盡量使用主鍵查詢。

覆蓋索引

  • 解釋一: 就是select的數據列只用從索引中就能夠取得,不必從數據表中讀取,換句話說查詢列要被所使用的索引覆蓋。
  • 解釋二: 索引是高效找到行的一個方法,當能通過檢索索引就可以讀取想要的數據,那就不需要再到數據表中讀取行了。如果一個索引包含了(或覆蓋了)滿足查詢語句中字段與條件的數據就叫做覆蓋索引。
  • 解釋三:是非聚集組合索引的一種形式,它包括在查詢里的Select、Join和Where子句用到的所有列(即建立索引的字段正好是覆蓋查詢語句[select子句]與查詢條件[Where子句]中所涉及的字段,也即,索引包含了查詢正在查找的所有數據)。
  • 不是所有類型的索引都可以成為覆蓋索引。覆蓋索引必須要存儲索引的列,而哈希索引、空間索引和全文索引等都不存儲索引列的值,所以MySQL只能使用B-Tree索引做覆蓋索引
  • 當發起一個被索引覆蓋的查詢(也叫作索引覆蓋查詢)時,在EXPLAIN的Extra列可以看到“Using index”的信息

概念如上,這里我們還是用例子來說明:

 


(注:圖來自極客時間專欄)
現在,我們一起來看看這條SQL查詢語句的執行流程: select * from T where k between 3 and 5

  1. 在k索引樹上找到k=3的記錄,取得 ID = 300;
  2. 再到ID索引樹查到ID=300對應的R3;
  3. 在k索引樹取下一個值k=5,取得ID=500;
  4. 再回到ID索引樹查到ID=500對應的R4;
  5. 在k索引樹取下一個值k=6,不滿足條件,循環結束。

在這個過程中,回到主鍵索引樹搜索的過程,我們稱為回表。可以看到,這個查詢過程讀了k索引樹的3條記錄(步驟1、3和5),回表了兩次(步驟2和4)。
在這個例子中,由於查詢結果所需要的數據只在主鍵索引上有,所以不得不回表。那么,有沒有可能經過索引優化,避免回表過程呢?

如果執行的語句是select ID from T where k between 3 and 5,這時只需要查ID的值,而ID的值已經在k索引樹上了,因此可以直接提供查詢結果,不需要回表。也就是說,在這個查詢里面,索引k已經“覆蓋了”我們的查詢需求,我們稱為覆蓋索引。
由於覆蓋索引可以減少樹的搜索次數,顯著提升查詢性能,所以使用覆蓋索引是一個常用的性能優化手段。
需要注意的是,在引擎內部使用覆蓋索引在索引k上其實讀了三個記錄,R3~R5(對應的索引k上的記錄項),但是對於MySQL的Server層來說,它就是找引擎拿到了兩條記錄,因此MySQL認為掃描行數是2。

延遲關聯

上面介紹了那么多 其實是在為延遲關聯做鋪墊,這里直接續上我們本次慢查詢的sql:


我們都知道在做分頁時會用到Limit關鍵字去篩選所需數據,limit接受1個或者2個參數,接受兩個參數時第一個參數表示偏移量,即從哪一行開始取數據,第二個參數表示要取的行數。 如果只有一個參數,相當於偏移量為0。
當偏移量很大時,如limit 100000,10 取第100001-100010條記錄,mysql會取出100010條記錄然后將前100000條記錄丟棄,這無疑是一種巨大的性能浪費。

當有這種寫法時,我們可以采用延遲關聯來進行優化,重點關注:SELECT id FROM qa_question WHERE expert_id = 69 AND STATUS = 30 ORDER BY over_time DESC LIMIT 0, 10, 這里其實利用了索引覆蓋,where條件后的expert_id 是有添加索引的,這里查詢id 可以避免回表,大大提升效率。

結語

工作中會遇到各種各樣的問題,對於一個研發來說最重要的是能夠從這些問題中學到什么。好久沒有寫博客了,究其原因還是自己變得懶惰了。 ( ̄ェ ̄;)
最后以《高性能Mysql》中的一段話結束:



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM