MySQL 分頁查詢優化——延遲關聯優化


目錄

1.   InnoDB表的索引的幾個概念

2.   覆蓋索引和回表

3.   分頁查詢

4.   延遲關聯優化

寫在前面

下面的介紹均是在選用MySQL數據庫和Innodb引擎的基礎開展。我們先來學習索引的幾個概念,幫助我們理解延遲關聯優化的加快分頁查詢速度的原因。

一、Innodb表的索引的幾個概念

InnoDB表是基於聚簇索引建立的。

索引一般分為主鍵索引和普通索引(輔助索引),聚簇索引並不是主鍵索引這樣的單獨的索引類型,而是一種數據存儲方式。通俗的來說,單獨的索引是存儲了索引信息的B+Tree,而聚簇索引是在同一個結構中保存了B+Tree和數據行,即通過主鍵索引B+Tree的結構建立數據文件(網上的說法是索引和數據存儲在同一個文件中),因此聚簇索引是一種數據存儲方式。

如果對於索引的概念不是很熟悉,建議去查閱相關資料學習,索引是一個很龐大的知識結構。

 

Innodb表以主鍵索引建立后綴名為.MYD表存儲文件,普通索引亦以B+Tree實現,並保存在后綴名為.MYI的文件中,具體結構圖如下所示:

 

 

 


 

在查詢時選用主鍵索引和普通索引有什么不一樣呢?

以下面語句為例:

·       select * from table where id = 5;id為主鍵索引) 檢索過程如上圖綠色箭頭所示,直接在主索引樹上根據主鍵檢索

·       select * from table where name = "Gates"; name為普通索引)檢索過程如上圖紅色箭頭所示,先根據普通索引檢索普通索引樹得到id5,然后再拿得到的id到主索引樹檢索得到結果。在這個過程中,回到主鍵索引樹搜索的過程,我們稱為回表。

 

從這里可以看出,基於非主鍵索引的查詢需要多掃描一棵索引樹。因此,我們在應用中應該盡量使用主鍵查詢。

解釋完普通索引和主鍵索引的檢索過程,讓我們來看看什么是覆蓋索引。

二、覆蓋索引和回表。

什么是覆蓋索引

查詢的列被所建的輔助索引所覆蓋,無需回表。用大白話解釋就是,要查的數據直接可以從索引樹上就能取得,無需回表查找。

注意:不是所有類型的索引都可以成為覆蓋索引。覆蓋索引必須要存儲索引的列,而哈希索引、空間索引和全文索引等都不存儲索引列的值,所以MySQL只能使用B-Tree索引做覆蓋索引

結合上圖的例子:

·       select id from table where name = "Gates"; 即為一個覆蓋索引。

 

三、分頁查詢使用場景

需求:查詢最近 7 天的訂單,並做分頁。訂單表數據量:3000W

未經優化的SQL

select * from t_trade_order

where create_time between '2019-10-17' and '2019-10-25'

limit 1000000, 10;

 

 

 

 

 

 

根據explain輸出的結果可知,這是一條慢查詢,在業務環境中不允許出現這樣的慢查詢。

 

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

 

終於轉入正題了,那我們應該怎樣優化呢?

 

四、延遲關聯優化

《高性能MySQL》書中其實也討論這個情況:

 

 

 

延遲關聯優化:通過使用覆蓋索引查詢返回需要的主鍵,再根據主鍵關聯原表獲得需要的數據。

 

select * from t_trade_order t

inner join (

     select id from t_trade_order

    where create_time between '2019-10-17' and '2019-10-25'

     limit 1000000, 10

) e

on t.id = e.id;

 

 

 

根據explain分析,查詢時間僅為0.31,比普通的分頁查詢快了一個數量級。

 

想必大家會想知道,為什么延遲關聯對比普通分頁查詢可以起到這樣的優化效果呢?

這就涉及到上面所講的覆蓋索引和回表這兩個重要概念了!

 

我們來看看這兩條語句的執行流程就很清楚了。

優化前:

select * from t_trade_order

where create_time between '2019-10-17' and '2019-10-25'

limit 1000000, 10;

create_time建表時被設置為普通索引)

 

1.create_time索引樹上找到create_time=‘2019-10-17’的記錄,取得其id

2.再到主索引樹查到對於id的記錄

3.如數量小於10,更新時間,循環步驟12

4. 。。。

5.create_time索引樹取下一個值create_time='2019-10-25',不滿足條件,循環結束。

6.查詢結果放棄前1000000行,返回10

 

顯然,普通的分頁查詢是逐一通過普通索引獲得id然后回表查詢,每次回表進行一次IO,造成相當大的性能浪費。

 

優化后

select * from t_trade_order t

inner join (

    select id from t_trade_order

   where create_time between '2019-10-17' and '2019-10-25'

    limit 1000000, 10

) e

on t.id = e.id;

 

1.使用覆蓋索引,select id from t_trade_order where create_time between '2019-10-17' and '2019-10-25' limit 1000000, 10查詢結果放棄前1000000行,返回10查詢出符合查詢范圍的id

2、回表關聯,根據獲得的id關聯主索引表,批量匹配得到結果。(只需回到主索引表一次)

 

由此可知,通過使用覆蓋索引查詢返回需要的主鍵,再根據這些主鍵關聯原表獲得需要的行,這可以減少MySQL回表的次數,也避免了MySQL直接在原表上掃描那些需要丟棄的行數(實則在普通索引樹上掃描,速度快很多)。

 

文章寫到這里,希望對大家理解延遲關聯優化有些許幫助。

 

 

 


免責聲明!

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



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