案例挖掘:千萬數據表Limit 分頁查詢優化


點擊上方“小羅技術筆記”,關注公衆號

第一時間送達實用乾貨


作者:Tomo@An

來源:http://suo.im/6fuu5k


前言

在各類系統的表格類信息展示的功能中,經常會用到“翻頁”這個操作,在頁面上每次只展示有限的數據,需要看其他數據的時候則像翻書一樣翻到後面的“頁”。在 MySQL 支持的 SQL 語法中對此有特殊的支持,開發人員在實現這類功能的時候很方便:

  • select*fromxxx limit M,N

  • select*fromxxx limit N offset M

這兩類語法代表的意思是一樣的:返回從第 M 開始(不包括這一行)之後的 N 行數據。雖然使用起來很方便,但是這類語句存在查詢性能上的陷阱,需要特別注意一下。

原理簡介

在解釋原理之前,先看一下實際的效果,看看這個“性能的陷阱”是什麼。


兩個語句的內容都非常簡單,差別只在 limit 的部分,第一個語句跳過的行數很少,第二個語句跳過的行數很多,結果是兩個語句的執行時間差了至少 200 倍。PS:limit 配合 order by 使用是一個好習慣,確保結果數據是穩定的

可以看到跳過的行數大幅度增長時,SQL 語句的執行時間也會快速增長,原因其實比較簡單:在處理 limit M,N 的時候,MySQL 會先拿到 M+N 行結果數據,然後再丟棄 M 行數據,展示之後剩下的 N 行數據。所以上圖的第二個語句實際上掃描了 800 多萬行數據,然後丟棄了 800 萬行數據,只展示之後的 1 行結果。

利用[慢查詢分析三部曲][Link 1]的方法嘗試排查一下,explain 和 optimizer_trace 都看不出來差別,但是 profile 裏面能看出來兩者的差距:

    

雖然都只輸出一行結果,但是在 Sending data 階段花費的時間差別很大,其實就是花在掃描 800 萬行數據上去了。

優化策略

針對這個問題,其實有一個比較通用的優化思路:利用 join,先根據主鍵搜索到需要的數據,再通過主鍵關聯到原來的表輸出結果。SQL 可以改寫一下:

SQL 改寫的效果

可以看到查詢時間降到了 1.5s 左右,提升了約 37%,看起來還可以,那麼還有其他的辦法麼?

顯然還是有的,不過這會要求表有自增主鍵。在分頁查詢的時候,記錄上一次查詢結果中的主鍵,然後在 where 條件中添加主鍵的範圍約束。以上面的查詢爲例,上次分頁查詢時的主鍵是 8000001,那麼下次分頁的時候,where 條件中添加一個主鍵約束:id>8000001,再來看看查詢效果:

添加條件之後的效果

可以發現利用主鍵來篩選掉上一次分頁前的所有數據後再用 limit,查詢基本是馬上返回結果的。不過要特別注意,這種方法是根據主鍵的順序先做了一次篩選,不一定會適用於所有的業務場景,理論上 UUID 類的主鍵也可以用,但是改造 SQL 前務必確保查詢結果是符合預期的

總結一下

MySQL 由於本身查詢優化器覆蓋到的場景不夠全,慢查詢的原因也千奇百怪,各類業務 SQL 在上線前儘量多覆蓋一些場景,確保業務功能安全發佈。


長按二維碼關注

點個在看再走唄!

本文分享自微信公衆號 - 小羅技術筆記(javaCodeNote)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章