[轉]limit查詢慢的原因及優化方法


原文出處,刪除與知識無關的作者個人經歷和感想部分

葉不聞《寫在教師節:分頁場景(limit,offset)為什么會慢》
鏈接:https://juejin.im/post/5c4db295e51d4503834d9c43

邏輯算子部分引用了

叄金《SQL優化器執行過程之邏輯算子》
鏈接:http://www.imooc.com/article/278660

問題分析

select * from table where status = xx limit 10 offset 100000;

在分頁場景下,即使有索引,limit請求也會非常慢,在數據量只有10萬的情況下,單機大概2-3秒

索引

我們知道MySQL的索引是b+樹。如果是一個二叉樹,因為無法知道前100個數在樹上的分布情況,所以無法利用二分查找的特性。在b+樹中,可以通過葉子節點組成的鏈表,以O(n)的復雜度查找到第100大的數,但即使是O(n),也不至於這么慢,是否還有其他原因

通過查閱資料得知,InnoDB的索引分為兩種

  • 聚簇索引:包含主鍵索引和對應的實際數據,索引的葉子節點就是數據節點,找到索引也就找到了數據
  • 輔助索引:可以理解為二級節點,其葉子節點還是索引節點,包含了主鍵id,還需要查詢一遍數據

由於MySQL的分層的原因,即使前100000個會扔掉,MySQL也會通過二級索引上的主鍵id,去聚簇索引上查一遍數據,這可是100000次隨機IO,自然慢成哈士奇

分層

了解這個之前需要先了解一個概念,邏輯算子。簡單的介紹一下查詢計划中的一些邏輯算子

  • DataSource:數據源,也就是我們SQL語句中的表。select name from table1中的table1
  • Join:連接,如select * from table1 table2 where table1.name = table2.name就是把兩個表做Join。連接條件是最簡單的等值連接,當然還有其他我們熟知的inner join,left join,right join等等
  • Selection:選擇,如select name from table1 where id = 1中的where后的過濾條件
  • Aggregation:分組,如select sum(score) from table1 group by name中的group by。按照某些列進行分組,分組后可以進行一些聚合操作,比如Max、Min、Sum、Count、Average等等
  • Projection:投影,指搜索的列,如select name from table1 where id = 1中的列name
  • Sort:排序,如select * from table1 order by id里面的order by。無序的數據通過這個算子處理后,輸出有序的數據
  • Apply:子查詢,如select * from (select id,name from table1) as t中的(select id,name from table1) as t。可以進行嵌套查詢。

選擇、投影、連接就是最基本的算子,其中 Join 有內連接,左外右外連接等多種連接方式

select b from t1, t2 where t1.c = t2.c and t1.a > 5

變成邏輯查詢計划之后

  • t1 t2 對應的 DataSource,負責將數據撈上來
  • 上面接個 Join 算子,將兩個表的結果按 t1.c = t2.c連接
  • 再按 t1.a > 5 做一個 Selection 過濾
  • 最后將 b 列投影

下圖是未經優化的表示,所以說不是mysql不想把limit傳遞給引擎層,而是因為划分了邏輯算子,所以導致無法直到具體算子包含了多少符合條件的數據

ps:SELECT執行順序

SQL語句執行順序 MySQL執行順序
1 select distinct from
2 from on
3 join join
4 on where
5 where group by
6 group by having+聚合函數
7 having select distinct
8 union union
9 order by order by
10 limit limit

解決方法

解決方法有2種

  • 根據業務實際需求,看能否替換為下一頁,上一頁的功能,特別在移動端。把limit替換成>id的方式。該id再調用時,需要返回給前端。但是這種有些業務場景不適用
select * from table where status = xx id > 100000 limit 10;
  • 【推薦】嵌套子查詢,先查找數據的主鍵值,因為主鍵在輔助索引上就有,所以不用回歸到聚簇索引的磁盤去拉取。再通過這些已經被limit出來的10個主鍵id,去查詢聚簇索引。這樣只會十次隨機IO。在業務確實需要用分頁的情況下,使用該方案可以大幅度提高性能。通常能滿足性能要求
select xxx from in (select id from table where status = xx limit 10 offset 100000);


免責聲明!

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



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