深度分頁及其替代方案
- 1.深度分頁
- 2.為什么不推薦深度分頁
- 2.1 性能問題(核心原因)
- 2.2 資源消耗對比
- 2.3 實際限制
- 3.深度分頁的替代方案
- 3.1 方案一:Search After(推薦)
- 3.1.1 為什么 Search After 性能更高
- 3.1.2 技術原理簡化
- 3.1.3 關鍵區別
- 3.1.4 適用場景
- 3.2 方案二:Scroll API(適用于大批量導出)
- 3.2.1 詳細解釋
- 3.2.2 類比理解
- 3.2.3 注意事項
- 3.3 方案三:基于時間范圍的分頁
- 3.4 總結:如何選擇分頁方式
1.深度分頁
深度分頁 指的是在 Elasticsearch 中查詢結果集 非常靠后的頁碼(例如第 1000 1000 1000 頁,每頁 10 10 10 條數據,即 from=10000
)。它通常表現為使用 from + size
參數組合來獲取遠端的分頁數據。例如:
GET /my_index/_search
{"from": 10000, // 跳過前10000條"size": 10, // 取10條"query": { ... }
}
2.為什么不推薦深度分頁
2.1 性能問題(核心原因)
Elasticsearch 的分頁原理是:
- 協調節點 需要從所有分片(
shard
)收集滿足條件的文檔。 - 計算全局排序后,臨時存儲
from + size
范圍內的所有文檔。 - 最后返回
size
條結果。
問題:
- 當
from
值很大時(如from=100000
),每個分片需要 先查100000+size
條數據 到內存,再匯總排序。 - 數據量越大,內存和 CPU 消耗呈線性增長,可能導致 內存溢出。
2.2 資源消耗對比
分頁方式 | 內存消耗 | 響應時間 | 適用場景 |
---|---|---|---|
淺分頁(from=0 ) | 低(僅緩存 size 條) | 毫秒級 | 前幾頁數據 |
深度分頁(from=10000 ) | 高(緩存 10000+size 條) | 秒級甚至超時 | 不推薦直接使用 |
2.3 實際限制
- Elasticsearch 默認限制:
from + size ≤ 10,000
(可通過index.max_result_window
調整,但 調高會加劇風險)。 - 即使調高限制,性能也會急劇下降。
3.深度分頁的替代方案
3.1 方案一:Search After(推薦)
原理:
- 利用上一頁的排序值(如
_shard_doc
或自定義字段)作為游標。 - 不跳過記錄,直接定位到下一頁的起始點。
示例:
// 第一頁
GET /blog_posts/_search
{"size": 10,"sort": [ // 必須包含唯一性字段(如 _id){ "publish_date": "desc" },{ "_id": "asc" }]
}// 后續頁(用上一頁最后一條的排序值)
GET /blog_posts/_search
{"size": 10,"search_after": ["2023-04-05", "abc123"], // 上一頁最后記錄的 publish_date 和 _id"sort": [{ "publish_date": "desc" },{ "_id": "asc" }]
}
優點:
- 性能穩定(不受頁碼影響)。
- 適合無限滾動、批量導出等場景。
3.1.1 為什么 Search After 性能更高
用一個生活中的例子來理解:假設你要在圖書館的 100萬本書
中,每次找 10本
符合特定條件的書,并按出版日期排序。
- 傳統分頁(
from/size
)的做法:- 你要第 1000 頁(即第 9991-10000 本書)。
- 圖書管理員必須:
- 先取出前 10000 本書,堆在桌子上(內存)。
- 排序這 10000 本,扔掉前 9990 本。
- 最后給你第 9991-10000 本。
- 問題:每次翻到深頁碼,都要重復 “取書 + 排序 + 扔書” 的過程,桌子(內存)可能堆不下!
- Search After 的做法:
- 你記住 當前看到的最后一本書的出版日期和編號(例如:“
2023-05-01, 編號ABC123
”)。 - 下次直接告訴圖書管理員:“
我要比【2023-05-01, ABC123】更早的10本書
”。 - 圖書管理員:
- 直接定位到這本書的位置(通過索引)。
- 往后數 10 本給你。
- 優勢:不需要臨時存儲前面的 9990 本書!
- 你記住 當前看到的最后一本書的出版日期和編號(例如:“
🚀 簡而言之:Search After 快是因為它 不傻乎乎地從頭數,而是像書簽一樣 “記住位置”,直接跳到下一頁開始的地方。
3.1.2 技術原理簡化
分頁方式 | 工作方式 | 性能消耗 |
---|---|---|
from/size | 每次從頭計算,臨時存儲 from+size 條數據 | 隨 from 增大 指數級上升 |
search_after | 像書簽一樣記住位置,直接跳轉 | 恒定(與頁碼無關) |
3.1.3 關鍵區別
from/size
:- 類似 “每次從第一頁開始翻”
- 計算:
(from + size) × 分片數
→ 內存爆炸 💥
search_after
:- 類似 “記住看到哪里,接著往下讀”
- 計算:
size × 分片數
→ 內存恒定 📊
3.1.4 適用場景
from/size
:- 前幾頁(如首頁、搜索結果前 10 條)
search_after
:- 無限滾動(如微博、朋友圈)
- 深度分頁(如第 100 頁+)
3.2 方案二:Scroll API(適用于大批量導出)
原理:
- 創建快照(
snapshot
),保持查詢上下文。 - 逐批獲取數據,類似數據庫游標。
示例:
// 1. 初始化 Scroll(保留1分鐘)
GET /blog_posts/_search?scroll=1m
{"size": 100,"query": { "match_all": {} }
}// 2. 后續獲取(用返回的 scroll_id)
GET /_search/scroll
{"scroll": "1m", "scroll_id": "DXF1ZXJ5QW5kRmV0Y2gB..."
}// 3. 最后手動清理
DELETE /_search/scroll
{"scroll_id": "DXF1ZXJ5QW5kRmV0Y2gB..."
}
優點: 適合非實時、大數據量導出(如報表生成)。
缺點:
- 不支持實時數據(快照期間數據變化不會反映)。
- 占用服務器資源,用完需手動清理(
DELETE /_search/scroll
)。
3.2.1 詳細解釋
scroll=1m
的含義1m
=1 分鐘
(時間單位,類似30s
=30秒
,2h
=2小時
)- 表示 Elasticsearch 會保留這次 Scroll 查詢的上下文(如排序結果、快照數據)1 分鐘。
- 超時后,Scroll 會自動失效,資源被釋放。
- 為什么需要設置超時?
- Scroll 會占用服務器內存和資源,長時間不釋放可能導致性能問題。
- 設置超時(如
1m
)是讓 Elasticsearch 自動清理 不再使用的 Scroll 查詢。
- 如何調整超時?
- 根據數據量調整,例如:
- 大數據導出:
scroll=5m
( 5 5 5 分鐘) - 小批量查詢:
scroll=30s
( 30 30 30 秒)
- 大數據導出:
- 根據數據量調整,例如:
- 續期 Scroll
如果處理時間較長,可以在每次請求時刷新超時時間:GET /_search/scroll {"scroll": "1m", // 重新設置為1分鐘"scroll_id": "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVY..." }
3.2.2 類比理解
把 Scroll 想象成圖書館的 “臨時書架”:
- 你告訴圖書管理員:“我需要 1 分鐘(
scroll=1m
)來挑書”。 - 1 分鐘內,書架上的書(查詢結果)保持不變,你可以慢慢選。
- 超時后,圖書管理員會把書架清空(釋放資源)。
- 如果你需要更多時間,可以喊:“再給我 1 分鐘!”(續期 Scroll)。
3.2.3 注意事項
- 不要濫用長超時(如
scroll=1h
),否則可能導致集群內存壓力。 - 用完務必清理(即使未超時):
DELETE /_search/scroll {"scroll_id": "DXF1ZXJ5QW5kRmV0Y2gB..." }
- Scroll 適用于離線任務(如導出數據),不適用于實時分頁。
3.3 方案三:基于時間范圍的分頁
原理:
- 用時間字段(如
publish_date
)作為分頁條件。 - 每頁查詢限定時間范圍。
示例:
GET /blog_posts/_search
{"size": 10,"query": {"range": {"publish_date": { "lte": "2023-03-01" } // 第二頁改為 "lte": "上一頁的最小日期"}},"sort": [ { "publish_date": "desc" } ]
}
適用場景:
- 按時間線分頁(如新聞列表)。
3.4 總結:如何選擇分頁方式
場景 | 推薦方案 | 原因 |
---|---|---|
用戶前端分頁(前幾頁) | from + size | 簡單易用,性能可接受 |
深度分頁(如第 100 100 100 頁) | Search After | 避免內存爆炸,性能穩定 |
大數據量導出 | Scroll API | 適合離線處理,但需注意資源釋放 |
按時間滾動 | 時間范圍查詢 | 利用業務字段天然分頁 |
關鍵建議
- 禁止生產環境使用大
from
值(如from=100000
)。- 若必須調整
index.max_result_window
,需評估集群資源。- Search After 是深度分頁的最佳實踐,兼容大多數場景。