柚子快報激活碼778899分享:SQL|使用游標(biāo)分頁優(yōu)化深分頁
柚子快報激活碼778899分享:SQL|使用游標(biāo)分頁優(yōu)化深分頁
業(yè)務(wù)場景:需要將有 500 萬條記錄的表 my_table 中的 content 字段取出。這顯然需要分頁取出,我們規(guī)定每一頁取出 1000 條記錄。
LIMIT ... OFFSET ... 語法
SELECT content FROM my_table LIMIT 1000000, 1000;
因為 B+ 樹的非葉子節(jié)點(diǎn)中不存儲節(jié)點(diǎn)的記錄數(shù),所以 MySQL 并不能準(zhǔn)確地知道 1000001 條記錄存在于那個葉子節(jié)點(diǎn)中。因此,MySQL 為了找到第 1000001 條記錄,需要將前 1000000 條記錄所在的所有 B+ 樹的葉子結(jié)點(diǎn)都遍歷一遍。然后再從第 1000001 條記錄開始向后繼續(xù)遍歷 B+ 樹的葉子節(jié)點(diǎn),直至取出 1000 條并返回,隨著記錄數(shù)的增大,這個過程顯然是極其緩慢的。
其時間復(fù)雜度為
O
(
n
+
m
)
O(n + m)
O(n+m),其中
n
n
n 為獲取的第一條記錄的偏移量,
m
m
m 為需要獲取的記錄數(shù)。
游標(biāo)分頁方法
面對這種情況,我們可以考慮使用游標(biāo)分頁。
在第一次請求時,我們獲取前 1000 條記錄:
SELECT id, content FROM my_table LIMIT 1000;
在請求之后,我們記錄下自增主鍵 id 的最大值(且后續(xù)每次請求都記錄 id 的最大值);再下一次請求時,我們使用自增主鍵 id 的最大值來過濾數(shù)據(jù)(例如當(dāng)上一次請求的 id 最大值為 1000000 時):
SELECT id, content FROM my_table WHERE id > 1000000 LIMIT 1000;
此時,MySQL 需要找到的不再是第 1000001 條記錄,而是 id 大于 1000000 的第 1 條記錄。因為 B+ 樹的非葉子節(jié)點(diǎn)都包含數(shù)據(jù)索引,所以我們只需要從根節(jié)點(diǎn)開始向下走到葉子節(jié)點(diǎn),即可找到 id 大于 1000000 的第 1 條記錄,然后從該條記錄開始向后繼續(xù)遍歷 B+ 樹的葉子節(jié)點(diǎn),直至取出 1000 條并返回即可。
其時間復(fù)雜度為
O
(
log
?
n
+
m
)
O(\log n + m)
O(logn+m),其中
n
n
n 為獲取的第一條記錄的偏移量,
m
m
m 為需要獲取的記錄數(shù)。
需要注意的是,當(dāng)我們使用游標(biāo)分頁的方法時,無法直接獲取到指定頁數(shù),而是必須從前往后逐頁遍歷。這與 ES 的 scroll 是類似的。
柚子快報激活碼778899分享:SQL|使用游標(biāo)分頁優(yōu)化深分頁
參考閱讀
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。