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

前言

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

回表

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

  • 若是語句是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」的信息

概念如上,這裏咱們仍是用例子來講明:spa

 


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

code

  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條記錄(步驟一、3和5),回表了兩次(步驟2和4)。
在這個例子中,因爲查詢結果所須要的數據只在主鍵索引上有,因此不得不回表。那麼,有沒有可能通過索引優化,避免回表過程呢?
blog

若是執行的語句是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》中的一段話結束:


相關文章
相關標籤/搜索