如何在elasticsearch裏面使用深度分頁功能

前面的文章提到過es默認的from+size的分頁方式返回的結果數據集不能超過1萬點,超過以後返回的數據越多性能就越低。併發

這是由於es要計算類似度排名,須要排序整個整個結果集,假設咱們有一個index它有5個shard,如今要讀取1000到1010之間的這10條數據,es內部會在每一個shard上讀取1010條數據,而後返回給計算節點,這裏有朋友可能問爲啥不是10條數據而是1010條呢?這是由於某個shard上的10條數據,可能尚未另外一個shard上top10以後的數據類似度高,因此必須所有返回,而後在計算節點上,從新對5050條數據進行全局排序,最後在選取top 10出來,這裏面排序是很是耗時的,因此這個數量實際上是指數級增加的,到後面分頁數量越多性能就越降低的厲害,並且大量的數據排序會佔用jvm的內存,頗有可能就OOM了,這也是爲何es默認不容許讀取超過1萬條數據的緣由。jvm

那麼問題來了,我就是想要深度的分頁數據應該怎麼辦? es裏面提供了兩種方式來讀取深度分頁的數據:elasticsearch

(1)離線的讀取深度分頁數據的Scroll方法高併發

(2)可以用於實時和高併發場景的searchAfter方法(5.x以後)性能

Scroll方式在前面的文章提到過,它經過一次查詢請求後維護一個索引快照的search context,而後每次再去批量的讀取數據,效率比較高。在5.x以後,還能夠經過slice分片來實現並行導出。code

它的缺點就是維護一個search context須要佔用不少資源,並且在快照創建以後數據變化如刪除和更新操做是不能被感知到的,因此不可以用於實時和高併發的場景。排序

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

它的缺點是不可以隨機跳轉分頁,只能是一頁一頁的向後翻,而且須要至少指定一個惟一不重複字段來排序。內存

此外還有一個與scorll的不一樣之處是searchAfter的讀取數據的順序會受索引的更新和刪除影響而scroll不會,由於scroll讀取的是不可變的快照。資源

下面來看下如何使用searchAfter:

咱們先查詢一頁數據:

GET twitter/_search
{
    "size": 10,
    "query": {
        "match" : {
            "title" : "elasticsearch"
        }
    },
    "sort": [
        {"date": "asc"},
        {"_id": "desc"}
    ]
}

注意,上面用了兩個字段來排序,第一個是業務字段可能不惟一,可是第二個id字段必定惟一不重複的。只有這樣才能確保searchAfter的翻頁順序讀取。

另外searchAfter的from字段必定要設置成0,否則會有問題。

第一個請求發出以後,咱們須要獲取第一個請求裏面最後一條的數據的date和id,而後把這個信息傳送到下一個批次,依次類推直到把全部的數據處理完。

以下第二個請求的查詢體:

GET twitter/_search
{
    "size": 10,
    "query": {
        "match" : {
            "title" : "elasticsearch"
        }
    },
    "search_after": [1463538857, "654323"],
    "sort": [
        {"date": "asc"},
        {"_id": "desc"}
    ]
}

總結:

本篇文章介紹瞭如何在es裏面使用深度分頁的功能,並對比了scroll和searchAfter的優缺點及不一樣之處,瞭解這些知識以後,咱們就能夠在適合的場景下正確的選擇最優的處理方式。

相關文章
相關標籤/搜索