深度分頁優化思路

深度分頁優化思路

思考以下問題

查詢以下SQL的流程是怎么樣的呢?
為什么只查詢10條數據需要7秒?

# 查詢時間7秒
SELECT * FROM user ORDER BY age LIMIT 1000000, 10

問題分析

為什么分頁查詢隨著翻頁的深入,會變得越來越慢。

其實,問題的根本就在于:
第一數據量太大
第二數據庫處理分頁的方法太笨

你以為LIMIT 100000,10是直接跳過后10萬條? 太天真了!

數據庫的真實操作:

第一步: 把整張表的數據全撈出來(全表掃描),按年齡排好序(文件排序)。
第二步: 吭哧吭哧數到第100010條,再給你返回最后10條。

相當于:讓你從新華字典第1頁開始翻,翻到第1000頁才找到字,誰能不炸?

最坑爹環節:回表查數據
如果用了普通索引(比如按年齡建的索引):

  • 先查索引:按年齡找到對應的主鍵ID(快速)
  • 再回表:用ID去主鍵索引里撈完整數據(慢!)

10萬次回表 = 10萬次IO操作,不卡你卡誰?

再說另一個常見的情況——排序

大多數時候,分頁查詢都會帶有排序,比如按時間、按ID排序。

數據庫不僅要查數據,還得根據你的排序要求重新排一次,特別是在數據量大的時候,排序的開銷就變得非常大。

所以,翻越幾百頁的時候,你的查詢可能就開始慢得像蝸牛。

單表場景 limit 深度分頁 的優化方法

核心思路: 繞過全表掃描,直接定位到目標數據!

方案一:子查詢優化

mysql> explain SELECT *-> FROM user-> WHERE id >= ->     (->         SELECT id->         FROM user->         ORDER BY age->         LIMIT 1000000, 1-> 	   )-> limit 10;
+----+-------------+--------------------+------------+-------+---------------+--------------+---------+-------+---------+----------+--------------------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref   | rows   | filtered | Extra       |
+----+-------------+--------------------+------------+-------+---------------+--------------+---------+-------+---------+----------+--------------------------+
|  1 | PRIMARY     | user  | NULL       | range | PRIMARY       | PRIMARY | 8       | NULL  |     10 |   100.00 | Using where |
|  2 | SUBQUERY    | user  | NULL       | ref   | idx_age       | idx_age | 2       | const | 432791 |   100.00 | Using index |
+----+-------------+--------------------+------------+-------+---------------+--------------+---------+-------+---------+----------+--------------------------+
2 rows in set, 1 warning (0.15 sec)

原理: 用覆蓋索引快速找到第100000條的ID,直接從這個ID開始拿數據,跳過前面10萬次回表。

缺點是,不適用于結果集不以ID連續自增的分頁場景。

在復雜分頁場景,往往需要通過過濾條件,篩選到符合條件的ID,此時的ID是離散且不連續的。如果使用上述的方式,并不能篩選出目標數據

方案二:延時關聯

SELECT * FROM user t1
JOIN (SELECT id FROM user ORDER BY age LIMIT 1000000,10) t2 
ON t1.id = t2.id;
mysql> explain SELECT * -> FROM->     user t1->     JOIN (->         SELECT->             id->         FROM->             user->         ORDER BY->             age->         LIMIT 1000000,10->         ) AS t2 ->     ON t1.id = t2.id-> LIMIT 10;
+----+-------------+--------------------+------------+--------+---------------+--------------+---------+-------+---------+----------+--------------------------+
| id | select_type | table      | partitions | type   | possible_keys | key     | key_len | ref   | rows   | filtered | Extra       |
+----+-------------+--------------------+------------+--------+---------------+--------------+---------+-------+---------+----------+--------------------------+
|  1 | PRIMARY     | <derived2> | NULL       | ALL    | NULL          | NULL    | NULL    | NULL  | 432791 |   100.00 | NULL        |
|  1 | PRIMARY     | t1         | NULL       | eq_ref | PRIMARY       | PRIMARY | 8       | t2.id |      1 |   100.00 | NULL        |
|  2 | DERIVED     | user       | NULL       | ref    | idx_age       | idx_age | 9       | const | 432791 |   100.00 | Using index |
+----+-------------+--------------------+------------+--------+---------------+--------------+---------+-------+---------+----------+--------------------------+
3 rows in set, 1 warning (0.12 sec)

原理: 先用索引快速拿到10個目標ID,再一次性聯表查完整數據,減少回表次數。

方案三:索引覆蓋

索引覆蓋(Index Covering)是指一個查詢可以完全通過索引來執行,而無需通過回表來查詢其他字段數據。

例如:

ALTER TABLE user ADD INDEX idx_age_name(age, name);  -- 查詢+排序全走索引
SELECT age, name FROM user ORDER BY age LIMIT 1000000,10;  -- 0.08秒!

精髓: 索引里直接存了所有要查的字段,不用回表,直接起飛!
缺點: 如果每個查詢都建對應的索引的話,會浪費更多的空間存儲索引,也會影響插入時的速度。

方案四:書簽記錄

從原理上說,屬于是一種滾動查詢。也就是說我們必須從第一頁開始查詢,然后獲取本頁最大的記錄 ID,然后再根據大于最大記錄 ID 的數據向后持續滾動。也就是說,我們如果想查詢大于 1000000 后記錄的 10 條,那我們就得知道 1000000 條的 ID。因為 ID 是遞增的,所以直接查詢即可。

SELECT * FROM user WHERE id > 1000000 LIMIT 10; -- 500微秒!

精髓: 每次查詢都用上次查詢結果做書簽,直接走主鍵索引
缺點: 不支持條頁查詢,主鍵必須是自增的。

分庫分表后,翻頁為什么更慢了?

分庫分表的翻頁邏輯

假設訂單表分了3個庫,每個庫分了2張表(共6張表),按用戶ID分片。
當你執行:

SELECT * FROM orders ORDER BY create_time DESC LIMIT 1000000, 10;  

你以為數據庫的操作:
智能跳過100萬條,從6張表各拿10條,合并完事?

實際上的操作:

  • 每張表都老老實實查100萬+10條數據(共600萬+60條)。
  • 把所有數據匯總到內存,重新排序(600萬條數據排序,內存直接炸穿)。
  • 最后忍痛扔掉前650萬條,給你10條結果。

結果: 查一次耗時10秒+,數據庫CPU 100%!


分庫分表翻頁的存在的3個問題

  • 數據分散,全局排序難
    各分片數據獨立排序,合并后可能亂序,必須全量撈數據重排。
  • 深分頁=分片全量掃描
    每張表都要查 offset + limit 條數據,性能隨分片數量指數級下降。
  • 內存歸并壓力大
    100萬條數據 × 6個分片 = 600萬條數據在內存排序,分分鐘OOM!

一句話總結: 分庫分表后,翻頁越深,死得越慘!


3種解決分庫分表深度翻頁方案

方案1:禁止跳頁(青銅方案)

核心思想: 別讓用戶隨便跳頁,只能一頁一頁翻!其實就是上面的書簽記錄

實現方法:
1.第一頁查詢:

-- 按時間倒序,拿前10條  
SELECT * FROM orders  
WHERE user_id = 123  
ORDER BY create_time DESC  
LIMIT 10;  

2.翻下一頁:

-- 記住上一頁最后一條的時間  
SELECT * FROM orders  
WHERE user_id = 123  
AND create_time < '2023-10-01 12:00:00'  -- 上一頁最后一條的時間  
ORDER BY create_time DESC  
LIMIT 10;  

優點:

  • 性能:每頁查詢只掃索引的10條,0回表。
  • 內存:無需全量排序。

缺點:

  • 用戶不能跳頁(比如從第1頁直接跳到第100頁)。
  • 適合Feed流場景(如朋友圈、抖音),不適合后臺管理系統。

方案2:二次查詢法(黃金方案)

核心思想: 把分庫分表的“大海撈針”,變成“精準狙擊”!

實現步驟:
1. 第一輪查詢:每張分片查縮小范圍的數據

-- 每張分片查 (offset / 分片數量) + limit 條  
SELECT create_time FROM orders  
ORDER BY create_time DESC  
LIMIT 166666, 10;  -- 假設總offset=100萬,分6個分片:100萬/6 ≈ 166666  

1.確定全局最小時間戳:
從所有分片結果中,找到最小的 create_time(比如 2023-09-20 08:00:00)。
2.第二輪查詢:根據最小時間戳查全量數據

SELECT * FROM orders  
WHERE create_time >= '2023-09-20 08:00:00'  
ORDER BY create_time DESC  
LIMIT 10;  

優點:

  • 避免全量數據排序,性能提升6倍。
  • 支持跳頁查詢(如直接從100萬頁開始查)。

缺點:

  • 需要兩次查詢,邏輯復雜。
  • 極端情況下可能有誤差(需業務容忍)。

方案3:ES+HBase核彈方案(王者方案)

核心思想: 讓專業的人干專業的事!

  • ES:負責海量數據搜索+分頁(倒排索引碾壓數據庫)。
  • HBase:負責存儲原始數據(高并發讀取無壓力)。
    架構圖:
    圖片

實現步驟:

  1. **寫入時:**訂單數據同時寫MySQL(分庫分表)、ES、HBase。
  2. 查詢時:
GET /orders/_search  
{  "query": { "match_all": {} },  "sort": [{"create_time": "desc"}],  "from": 1000000,  "size": 10  
}  
List<Order> orders = es.search(...); // 從ES拿到10個ID  
List<Order> details = hbase.batchGet(orders); // 從HBase拿詳情  
  • Step2:用ES返回的ID,去HBase批量查數據。
  • Step1:用ES查分頁(只查ID和排序字段)。
    優點:
  • 分頁性能碾壓數據庫,百萬級數據毫秒響應。
  • 支持復雜搜索條件(ES的強項)。
    缺點:
  • 架構復雜度高,成本飆升(ES集群要錢,HBase要運維)。
  • 數據一致性難保證(延遲可能秒級)。

面試怎么答?

1. 面試官要什么?

  • 原理理解: 知道分庫分表后翻頁的痛點(數據分散、歸并排序)。
  • 方案靈活: 根據場景選方案(禁止跳頁、二次查詢、ES+HBase)。
  • 實戰經驗: 遇到過真實問題,用過二次查詢或ES。

2. 標準答案模板

分庫分表后深度分頁的難點在于全局排序和內存壓力。
我們有三種方案:

  • 禁止跳頁: 適合C端Feed流,用連續查詢代替跳頁。
  • 二次查詢法: 通過兩次查詢縮小范圍,適合管理后臺。
  • ES+HBase: 扛住億級數據分頁,適合高并發大廠場景。
    在實際的場景中,訂單查詢需要支持搜索條件,我們最終用ES+HBase,性能從10秒降到50毫秒。”

加分的騷操作:

  • 畫架構圖(分庫分表+ES+HBase數據流向)。
  • 給性能對比數據(ES分頁 vs 數據庫分頁)。
  • 提一致性解決方案(監聽MySQL Binlog同步到ES)。

總結

分庫分表后的深度分頁,本質是 “分布式數據排序” 的難題。

  • 百萬以內數據: 二次查詢法性價比最高。
  • 高并發大廠場景: ES+HBase是唯一選擇。
  • 千萬別硬剛: LIMIT 1000000,10 就是自殺式操作!
    最后一句忠告:
    面試被問分頁,先拍桌子喊出“禁止跳頁”,再掏出ES,面試官絕對眼前一亮!

本文改編自

  • 京東二面:分庫分表后翻頁100萬條,怎么設計?答對這題直接給P7!
  • 《牛券oneCoupon優惠券系統設計》第21小節:優惠券分發失敗記錄深分頁優化

如侵權,請聯系刪除

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/75724.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/75724.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/75724.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

使用 Vite 提升前端開發體驗:入門與配置指南

在現代前端開發中&#xff0c;構建工具的選擇對開發效率和項目性能有著至關重要的影響。Vite 是一個新興的前端構建工具&#xff0c;由 Vue.js 的作者尤雨溪開發&#xff0c;旨在通過利用現代瀏覽器的原生 ES 模塊特性&#xff0c;提供更快的開發服務器啟動速度和更高效的熱更新…

MYSQL基本語法使用

目錄 一、mysql之DML 增加語句 刪除語句和truncate 更新語句 replace語句 select查詢語句 二、select多種用法 查詢時的別名使用 分組 分組后的篩選 結果排序 分頁功能 分表 多表關聯查詢 練習題 一、單表查詢 二、多表查詢 前面已經學習了mysql的安裝和基本語…

自動化測試selenium(Java版)

1.準備工作 1.1.下載瀏覽器 自動化測試首先我們要準備一個瀏覽器,我們這里使用谷歌(chrome)瀏覽器. 1.2.安裝驅動管理 每一個瀏覽器都是靠瀏覽器驅動程序來啟動,但是瀏覽器的版本更新非常快,可能我們今天測試的是一個版本,第二天發布了一個新的版本,那么我們就要重構代碼,很…

HarmonyOS Next應用架構設計與模塊化開發詳解

引言 在HarmonyOS Next開發中&#xff0c;合理的應用架構設計和模塊化開發是構建高效、可維護應用的關鍵。本文將深入探討HarmonyOS Next應用的架構設計思路&#xff0c;并通過實際代碼示例展示如何實現模塊化開發。 應用架構設計 HarmonyOS Next應用通常采用分層架構設計&…

伊利工業旅游4.0,近距離感受高品質的魅力

3月24日&#xff0c;在2025年第112屆全國糖酒會&#xff08;簡稱春糖&#xff09;前夕&#xff0c;伊利集團“可感知高品質探尋薈”活動在成都召開&#xff0c;記者走進伊利在西南地區最大的乳制品生產基地—邛崍工廠&#xff0c;零距離見證液態奶、酸奶、冷飲等乳制品的誕生&a…

測試用例生成平臺通過大模型升級查詢功能,生成智能測試用例

在測試工作中&#xff0c;查詢功能是各類系統的核心模塊&#xff0c;傳統的測試用例編寫往往耗時且重復。如何讓老舊平臺煥發新活力&#xff1f;本文將結合大模型技術&#xff0c;通過用戶輸入的字段信息&#xff0c;自動化生成高效、精準的測試用例。同時&#xff0c;我們還將…

基于javaweb的SpringBoot雪具商城系統設計與實現(源碼+文檔+部署講解)

技術范圍&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬蟲、數據可視化、小程序、安卓app、大數據、物聯網、機器學習等設計與開發。 主要內容&#xff1a;免費功能設計、開題報告、任務書、中期檢查PPT、系統功能實現、代碼編寫、論文編寫和輔導、論…

【AI學習筆記】Coze平臺實現將Excel文檔批量導入數據庫全過程

背景前搖&原視頻教程&#xff1a; 最近看到很多同學都在用Coze平臺操作數據&#xff0c;我也想了解一下工作流的搭建和數據處理過程&#xff0c;但是一下子又看不懂太復雜的邏輯&#xff0c;于是上B站搜索相關的基礎教程。 Coze官方教程&#xff1a; 之前有看過Coze平臺…

【Axure高保真原型】縱向圖片輪播

今天和大家分享縱向圖片輪播的原型模版&#xff0c;載入后會自動循環輪播&#xff0c;鼠標移入圖片后停止輪播&#xff0c;可以通過點擊上下箭頭&#xff0c;向上或向下滑動切換上一張或下一張圖片&#xff0c;也可以點擊右側小圓點快速切換至對應圖片……具體效果可以打開下方…

力扣32.最長有效括號(棧)

32. 最長有效括號 - 力扣&#xff08;LeetCode&#xff09; 代碼區&#xff1a; #include<stack> #include<string> /*最長有效*/ class Solution { public:int longestValidParentheses(string s) {stack<int> st;int ans0;int ns.length();st.push(-1);fo…

如何在 React 項目中使用React.lazy和Suspense實現組件的懶加載?

大白話如何在 React 項目中使用React.lazy和Suspense實現組件的懶加載&#xff1f; 在 React 項目里&#xff0c;有時候組件功能多、體積大&#xff0c;要是一次性把所有組件都加載進來&#xff0c;網頁加載速度就會變慢。而 React 提供了 React.lazy 和 Suspense 這兩個好東西…

ffmpeg-將多個視頻切片成一個新的視頻

使用 ffmpeg 工具可以輕松完成將多個視頻切片合并為一個新的視頻。以下是實現這一目標的具體步驟和命令。 步驟概覽 1、將多個視頻切片。 2、創建文本文件列出切片的視頻片段。 3、使用 ffmpeg 合并這些切片為一個新的視頻。 一&#xff1a;安裝 ffmpeg 確保你的系統中已經安…

【第2月_day10】Pandas數據查看與選擇

以下是專為小白設計的 Pandas數據查看與選擇 學習內容&#xff0c;從基礎到應用逐步講解&#xff0c;附帶清晰示例和注意事項&#xff1a; 一、數據查看&#xff1a;快速了解你的數據 1. head() 和 tail() 作用&#xff1a;查看數據的前幾行或后幾行&#xff0c;默認顯示5行。…

Jetpack LiveData 使用與原理解析

一、引言 在 Android 開發中&#xff0c;數據的變化需要及時反映到界面上是一個常見的需求。然而&#xff0c;傳統的方式可能會導致代碼復雜、難以維護&#xff0c;并且容易出現內存泄漏等問題。Jetpack 組件中的 LiveData 為我們提供了一種優雅的解決方案&#xff0c;它是一種…

Unity2D 五子棋 + Photon聯網雙人對戰

開發環境配置 Unity版本2022.3 創建Photon賬號以及申請Photon中國區服務 官網申請賬號&#xff1a;Multiplayer Game Development Made Easy Photon Engine 中國區服務&#xff1a; 光子引擎photonengine中文站 成都動聯無限科技有限公司(vibrantlink.com) 導入PUN2插件以及…

(UI自動化測試web端)第二篇:元素定位的方法_css定位之屬性選擇器

看代碼里的【find_element_by_css_selector( )】( )里的表達式怎么寫&#xff1f; 文章介紹了第四種寫法屬性選擇器 &#xff0c;你要根據網頁中的實際情況來判斷自己到底要用哪一種方法來進行元素定位。每種方法都要多練習&#xff0c;全都熟了之后你在工作當中使用起來元素定…

預編譯能否 100%防 sql 注入?

&#x1f31f; 什么是 SQL 注入&#xff1f; SQL 注入&#xff08;SQL Injection&#xff09;是指攻擊者利用特殊輸入&#xff0c;讓數據庫執行它本來不應該執行的代碼&#xff0c;從而獲取或篡改數據。 就像在考試的時候偷偷改題目&#xff0c;讓老師改成你想要的內容&#…

第十五章 | Layer2、Rollup 與 ZK 技術實戰解析

&#x1f4da; 第十五章 | Layer2、Rollup 與 ZK 技術實戰解析 ——構建下一代高性能區塊鏈應用&#xff0c;從 Solidity 到 zkSync&#xff01; ? 本章導讀 Layer2 和零知識證明&#xff08;ZK&#xff09;正成為區塊鏈發展的核心方向。 隨著主網 Gas 居高不下、TPS 無法滿…

2025-03-26 學習記錄--C/C++-PTA 6-3 求鏈式表的表長

合抱之木&#xff0c;生于毫末&#xff1b;九層之臺&#xff0c;起于累土&#xff1b;千里之行&#xff0c;始于足下。&#x1f4aa;&#x1f3fb; 一、題目描述 ?? 6-3 求鏈式表的表長 本題要求實現一個函數&#xff0c;求鏈式表的表長。 函數接口定義&#xff1a; &…

【Linux】Linux_Ubuntu與Windows之間的文件傳輸

一、Linux終端命令的復制粘貼 1.打開linux 終端&#xff0c;輸入以下命令&#xff1a;&#xff08;注意&#xff0c;需要聯網&#xff09; 2.命令行下載&#xff1a; sudo apt-get autoremove open-vm-tools 3.命令行安裝&#xff1a; sudo apt-get install open-vm-tools-…