Elasticsearch中的分頁查詢限制和近似去重統計


Elasticsearch

前言

最近工作中用到了Elasticsearch,但是遇到幾個挺坑的點,還是記錄下。

深度分頁的問題

es中的普通的查詢from+size,存在查詢數量的10000條限制。

index.max_result_window
The maximum value of from + size for searches to this index. Defaults to 10000. Search requests take heap memory and time proportional to from + size and this limits that memory. See Scroll or Search After for a more efficient alternative to raising this.

es為了減少內存的使用,限制了內存中索引數據的加載,默認10000。也就是
from 10000 size 1
這樣的查詢就是不行的,將會報錯

Result window is too large, from + size must be less than or equal to:[10000] but was [10500]. See the scroll api for a more efficient way to requestlarge data sets. This limit can be set by changing the[index.max_result_window] index level parameter

如何解決

修改默認值

通過設置index 的設置參數max_result_window的值,來改變查詢條數的限制。

curl -XPUT http://127.0.0.1:9200/book/_settings -d '{ "index" : { "max_result_window" : 200000000}}'
使用search_after方法

例子可參考官方https://www.elastic.co/guide/en/elasticsearch/reference/6.8/search-request-search-after.html

如何使用呢,就是設置一個全局唯一的字段,然后在查看的時候加上這個字段的排序。這樣第二次查詢,search_after第一次查詢最后一條的對應的唯一值的值。有點繞哈,看例子

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

比如我的accessId是全局唯一,並且自增的,第二次查詢search_after最新的accessId就好了

GET twitter/_search
{
    "size": 10,
    "query": {
        "match" : {
            "title" : "elasticsearch"
        }
    },
    "search_after": [10],
    "sort": [
        {"accessId": "asc"}
    ]
}

后面的查詢依次類推

scroll 滾動搜索

scroll 查詢可以用來對Elasticsearch有效地執行大批量的文檔查詢,而又不用付出深度分頁那種代價。

游標查詢允許我們 先做查詢初始化,然后再批量地拉取結果。 這有點兒像傳統數據庫中的cursor

游標查詢會取某個時間點的快照數據。查詢初始化之后索引上的任何變化會被它忽略。它通過保存舊的數據文件來實現這個特性,結果就像保留初始化時的索引 視圖 一樣。

深度分頁的代價根源是結果集全局排序,如果去掉全局排序的特性的話查詢結果的成本就會很低。游標查詢用字段_doc來排序。 這個指令讓 Elasticsearch僅僅從還有結果的分片返回下一批結果。

啟用游標查詢可以通過在查詢的時候設置參數 scroll的值為我們期望的游標查詢的過期時間。游標查詢的過期時間會在每次做查詢的時候刷新,所以這個時間只需要足夠處理當前批的結果就可以了,而不是處理查詢結果的所有文檔的所需時間。 這個過期時間的參數很重要,因為保持這個游標查詢窗口需要消耗資源,所以我們期望如果不再需要維護這種資源就該早點兒釋放掉。設置這個超時能夠讓Elasticsearch在稍后空閑的時候自動釋放這部分資源。

GET /old_index/_search?scroll=1m  // 設置查詢窗口一分鍾
{
    "query": { "match_all": {}},
    "sort" : ["_doc"], // 使用_doc字段排序
    "size":  1000
}

這個查詢的返回結果包括一個字段_scroll_id,它是一個base64編碼的長字符串 。現在我們能傳遞字段_scroll_id_search/scroll查詢接口獲取下一批結果:

GET /_search/scroll
{
    "scroll": "1m", // 時間
    "scroll_id" : "cXVlcnlUaGVuRmV0Y2g7NTsxMDk5NDpkUmpiR2FjOFNhNnlCM1ZDMWpWYnRROzEwOTk1OmRSamJHYWM4U2E2eUIzVkMxalZidFE7MTA5OTM6ZFJqYkdhYzhTYTZ5QjNWQzFqVmJ0UTsxMTE5MDpBVUtwN2lxc1FLZV8yRGVjWlI2QUVBOzEwOTk2OmRSamJHYWM4U2E2eUIzVkMxalZidFE7MDs="
} // scroll_id是上次返回的

之后的查詢依次類推

這個游標查詢返回的下一批結果。 盡管我們指定字段size的值為1000,我們有可能取到超過這個值數量的文檔。 當查詢的時候, 字段 size 作用於單個分片,所以每個批次實際返回的文檔數量最大為size * number_of_primary_shards

es中的近似聚合

對於es來講,其中的去重計數。是個近似的值,不像mysql中的是精確值,存在5%的誤差,不過可以通過設置precision_threshold來解決少量數據的精准度

GET /cars/transactions/_search
{
    "size" : 0,
    "aggs" : {
        "distinct_colors" : {
            "cardinality" : {
              "field" : "color",
              "precision_threshold" : 100  // precision_threshold 接受 0–40,000 之間的數字,更大的值還是會被當作 40,000 來處理。
            }
        }
    }
}

示例會確保當字段唯一值在 100 以內時會得到非常准確的結果。盡管算法是無法保證這點的,但如果基數在閾值以下,幾乎總是100%正確的。高於閾值的基數會開始節省內存而犧牲准確度,同時也會對度量結果帶入誤差。

對於指定的閾值,HLL的數據結構會大概使用precision_threshold * 8字節的內存,所以就必須在犧牲內存和獲得額外的准確度間做平衡。

在實際應用中,100的閾值可以在唯一值為百萬的情況下仍然將誤差維持5%以內。

總結

當我們選型es時候,要充分考慮到上面的幾點。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM