ES 深度分頁問題及解決方案

深度分頁問題大致可以分爲兩類

  • 隨機深度分頁:隨機跳轉頁面
  • 滾動深度分頁:只能一頁一頁往下查詢

from/size

假設我們現在要從分片數 = 10中,查詢from = 5000 size = 10的數據集(假設爲頁面請求第501頁),那麼需要的數據是多少呢?

5010 * 10 = 50100 條數據,哇塞,我就查10條數據,你爲什麼要拿這麼數據,幹嘛呢,咋不上天呢!

ES其實也不想,但是我們知道ES 數據分佈在每個shard中,每個shard分佈一部分數據,它並不知道數據是如何排序的,因此只有通過協調節點進行以下操作query/fetch:

(1)協調節點發送請求到shard分佈的數據節點,請求數據

(2)每個分片在本地執行查詢,並使用本地的Term/Document Frequency信息進行打分,添加結果到大小爲from + size的本地有序優先隊列中。

(3)每個分片返回個自己優先隊列中所有文檔的ID和排序值給協調節點,協調節點合併這些值到自己的優先隊列中,產生一個全局排序後的列表(注:這裏只先返回_id標識和排序值用於排序,不返回整個數據,避免網絡開銷)

(4)協調節點根據排序後的列表獲取相應的數據進行二次請求(注:這次是根據10個文檔_id進行GET操作)

(5)獲得數據進行返回

從上我們可以看出,在這裏ES已經做了一部分優化了

es 目前支持最大的 skip 值是 max_result_window ,默認 爲 10000 。也就是當 from + size > max_result_window 時,es 將返回錯誤

Scroll

使用scroll,每次只能獲取一頁的內容,然後會返回一個scrollid,根據scrollid可以不斷地獲取下一頁的內容,所以scroll並不適用於有跳頁的情景。但是在真正的使用場景中,第10000條數據已經是很後面的數據了,可以“折衷”一下,不提供跳轉頁面功能,只能下一頁的翻頁。

Scroll方式通過一次查詢請求後維護一個臨時的索引快照的search context,此後的增刪查改操作並不會影響這個快照數據信息,後續的查詢只需要根據遊標去取數據,直到結果集中返回的 hits 字段爲空,就表示遍歷結束。效率比較高。在5.x之後,還可以通過slice分片來實現並行導出。

它的缺點就是維護一個search context需要佔用很多資源,而且在快照建立之後數據變化如刪除和更新操作是不能被感知到的,所以不能夠用於實時和高併發的場景。

search_after 

上述的 scroll search 的方式,官方的建議並不是用於實時的請求,因爲每一個 scroll_id 不僅會佔用大量的資源(特別是排序的請求),而且是生成的歷史快照,對於數據的變更不會反映到快照上。這種方式往往用於非實時處理大量數據的情況,比如要進行數據遷移或者索引變更之類的。那麼在實時情況下如果處理深度分頁的問題呢?es 給出了 search_after 的方式,這是在 >= 5.0 版本才提供的功能。

searchAfter的方式通過維護一個實時遊標來避免scroll的缺點,它可以用於實時請求和高併發場景。

它的缺點是不能夠隨機跳轉分頁,只能是一頁一頁的向後翻,並且需要至少指定一個唯一不重複字段來排序(注:每個文檔具有一個唯一值的字段應該用作排序規範的仲裁器。否則,具有相同排序值的文檔的排序順序將是未定義的。建議的方法是使用字段_id,它肯定包含每個文檔的一個唯一值)。

此外還有一個與scorll的不同之處是searchAfter的讀取數據的順序會受索引的更新和刪除影響而scroll不會,因爲scroll讀取的並不是不可變的快照,而是依賴於上一頁最後一條數據,所以無法跳頁請求,用於滾動請求,於scroll類似,不同之處在於它使無狀態的。

簡而言之就是:ES 通過唯一值字段維護了一個sort排序數據,下一次請求可以根據sort排序值來獲取下一頁數據

 

 

 

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