求求你別再用offset和limit分頁了

2022-06-06 09:21:10 字數 1924 閱讀 3677

不需要擔心資料庫效能優化問題的日子已經一去不復返了。

隨著時代的進步,隨著野心勃勃的企業想要變成下乙個 facebook,隨著為機器學習**收集盡可能多資料的想法的出現,作為開發人員,我們要不斷地打磨我們的 api,讓它們提供可靠和有效的端點,從而毫不費力地瀏覽海量資料。

如果你做過後台開發或資料庫架構,你可能是這麼分頁的:

如果你真的是這麼分頁,那麼我不得不抱歉地說,你這樣做是錯的。

你不以為然?沒關係。slack、shopify 和 mixmax 這些公司都在用我們今天將要討論的方式進行分頁。

我想你很難找出乙個不使用 offset 和 limit 進行資料庫分頁的人。對於簡單的小型應用程式和資料量不是很大的場景,這種方式還是能夠「應付」的。

如果你想從頭開始構建乙個可靠且高效的系統,在一開始就要把它做好。

今天我們將**已經被廣泛使用的分頁方式存在的問題,以及如何實現高效能分頁。

1offset 和 limit 有什麼問題?

正如前面段落所說的那樣,offset 和 limit 對於資料量少的專案來說是沒有問題的。

但是,當資料庫裡的資料量超過伺服器記憶體能夠儲存的能力,並且需要對所有資料進行分頁,問題就會出現。

為了實現分頁,每次收到分頁請求時,資料庫都需要進行低效的全表掃瞄。

什麼是全表掃瞄?全表掃瞄 (又稱順序掃瞄) 就是在資料庫中進行逐行掃瞄,順序讀取表中的每一行記錄,然後檢查各個列是否符合查詢條件。這種掃瞄是已知最慢的,因為需要進行大量的磁碟 i/o,而且從磁碟到記憶體的傳輸開銷也很大。

也就是說,為了獲取一頁的資料:

10萬行中的第5萬行到第5萬零20行

需要先獲取 5 萬行。這麼做是多麼低效?

如果你不相信,可以看看這個例子:

左邊的 schema sql 將插入 10 萬行資料,右邊有乙個效能很差的查詢和乙個較好的解決方案。只需單擊頂部的 run,就可以比較它們的執行時間。第乙個查詢的執行時間至少是第二個查詢的 30 倍。

資料越多,情況就越糟。看看我對 10 萬行資料進行的 poc。

現在你應該知道這背後都發生了什麼:offset 越高,查詢時間就越長。

2替代方案

你應該這樣做:

這是一種基於指標的分頁。

你要在本地儲存上一次接收到的主鍵 (通常是乙個 id) 和 limit,而不是 offset 和 limit,那麼每一次的查詢可能都與此類似。

為什麼?因為通過顯式告知資料庫最新行,資料庫就確切地知道從**開始搜尋(基於有效的索引),而不需要考慮目標範圍之外的記錄。

比較這個查詢:

和優化的版本:

返回同樣的結果,第乙個查詢使用了 12.80 秒,而第二個僅用了 0.01 秒。

要使用這種基於游標的分頁,需要有乙個惟一的序列字段 (或多個),比如惟一的整數 id 或時間戳,但在某些特定情況下可能無法滿足這個條件。

我的建議是,不管怎樣都要考慮每種解決方案的優缺點,以及需要執行哪種查詢。

如果需要基於大量資料做查詢操作,rick james 的文章提供了更深入的指導。

如果我們的表沒有主鍵,比如是具有多對多關係的表,那麼就使用傳統的 offset/limit 方式,只是這樣做存在潛在的慢查詢問題。我建議在需要分頁的表中使用自動遞增的主鍵,即使只是為了分頁。

別再用offset和limit分頁了

終於要對mysql優化下手了,本文將對分頁進行優化說明,希望可以得到乙個合適你的方案 分頁這個話題已經是老生常談了,但是有多少小夥伴一邊是既希望優化的自己的系統,另一邊在專案上還是保持自己獨有的個性。優化這件事是需要自己主動行動起來的,自己搞測試資料,只有在測試的路上才會發現更多你未知的事情。本文咔...

Redis 求求你,別再問跳表了

跳表 skiplist 是乙個特殊的鍊錶,相比一般的鍊錶,有更高的查詢效率,可比擬二叉查詢樹,平均期望的查詢 插入 刪除時間複雜度都是o log n 許多知名的開源軟體 庫 中的資料 結構均採用了跳表這種資料結構 我們拿我們以前的有序鍊錶相比 我們可以很清楚的看出,有序鍊錶的查詢比較慢,時間複雜度為...

redis面試題,求求你別再問我redis了

室友有個大佬,一說redis就問我為什麼使用redis,redis單執行緒為什麼這麼快,redis redis的部分面試題,自己整理的,當然實際工作中使用的redis比較簡單,但是了解redis部分底層還是有必要的 什麼是redis redis 是乙個使用 c 語言寫成的,開源的基於記憶體的,單執行...