ES系列12、ES的scroll Api及分頁實例

1.官方api

1.Scroll概念

Version:6.1javascript

英文原文地址:Scrollhtml

當一個搜索請求返回單頁結果時,可使用 scroll API 檢索體積大量(甚至所有)結果,這和在傳統數據庫中使用遊標的方式很是類似。java

不要把 scroll 用於實時請求,它主要用於大數據量的場景。例如:將一個索引的內容索引到另外一個不一樣配置的新索引中。node

2.Client support for scrolling and reindexing(滾動搜索和索引之間的文檔重索引)

一些官方支持的客戶端提供了一些輔助類,能夠協助滾動搜索和索引之間的文檔重索引:web

Perl數據庫

​ 參閱 Search::Elasticsearch::Client::5_0::Bulk 和 Search::Elasticsearch::Client::5_0::Scrollapi

Python數組

​ 參閱 elasticsearch.helpers.*緩存

NOTE:從 scroll 請求返回的結果反映了初始搜素請求生成時的索引狀態,就像時間快照同樣。對文檔的更改(索引、更新或者刪除)只會影響之後的搜索請求。session

3.基本用法

爲了使用 scroll ,初始的搜索請求應該在查詢字符串中指定 scroll 參數,這個參數會告訴 Elasticsearch 將 「search context」 保存多久。例如:?scroll=1m

POST /twitter/_search?scroll=1m
{
    "size": 100,
    "query": {
        "match" : {
            "title" : "elasticsearch"
        }
    }
}

 

上面的請求返回的結果裏會包含一個 _scroll_id ,咱們須要把這個值傳遞給 scroll API ,用來取回下一批結果。

  

POST  /_search/scroll 
{
    "scroll" : "1m", 
    "scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ==" 
}

 

(1) GET 或者 POST 均可以

(2) URL 不能包含 index 和 type 名稱,原始請求中已經指定了

(3) scroll 參數告訴 Elasticsearch 把搜索上下文再保持一分鐘

(4) scroll_id 的值就是上一個請求中返回的 _scroll_id 的值

size 參數容許咱們配置每批結果返回的最大命中數。每次調用 scroll API 都會返回下一批結果,直到再也不有能夠返回的結果,即命中數組爲空。

IMPORTANT:初始的搜索請求和每一個 scroll 請求都會返回一個新的 _scroll_id ,只有最近的 _scroll_id 是可用的

NOTE:若是請求指定了過濾,就只有初始搜索的響應中包含聚合結果。

NOTE:Scroll 請求對 _doc 排序作了優化。若是要遍歷全部的文檔,並且不考慮順序,_doc 是最高效的選項。

GET /_search?scroll=1m
{
  "sort": [
    "_doc"
  ]
}

1.Keeping the search context alive

scroll 參數告訴了 Elasticsearch 應當保持搜索上下文多久。它的值不須要長到可以處理完全部的數據,只要足夠處理前一批結果就好了。每一個 scroll 請求都會設置一個新的過時時間。

一般,爲了優化索引,後臺合併進程會把較小的段合併在一塊兒建立出新的更大的段,此時會刪除較小的段。這個過程在 scrolling 期間會繼續進行,可是一個打開狀態的索引上下文能夠防止舊段在仍須要使用時被刪除。這就解釋了 Elasticsearch 爲何可以不考慮對文檔的後續修改,而返回初始搜索請求的結果。

TIP:使舊段保持活動狀態意味着須要更多的文件句柄。請確保你已將節點配置爲擁有足夠的可用的文件句柄。詳情參閱 File Descriptors

你可使用 nodes stats API 查看有多少搜索上下文處於開啓狀態

GET /_nodes/stats/indices/searchGET /_nodes/stats/indices/search

2.Clear scroll API

當超出了 scroll timeout 時,搜索上下文會被自動刪除。可是,保持 scrolls 打開是有成本的,當再也不使用 scroll 時應當使用 clear-scroll API 進行顯式清除。

DELETE /_search/scroll
{
    "scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ=="
}

 

可使用數組傳遞多個 scroll ID

DELETE /_search/scroll
{
    "scroll_id" : [
      "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ==",
      "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAABFmtSWWRRWUJrU2o2ZExpSGJCVmQxYUEAAAAAAAAAAxZrUllkUVlCa1NqNmRMaUhiQlZkMWFBAAAAAAAAAAIWa1JZZFFZQmtTajZkTGlIYkJWZDFhQQAAAAAAAAAFFmtSWWRRWUJrU2o2ZExpSGJCVmQxYUEAAAAAAAAABBZrUllkUVlCa1NqNmRMaUhiQlZkMWFB"
    ]
}

 

使用 _all 參數清除全部的搜索上下文

DELETE /_search/scroll/_allDELETE /_search/scroll/_all

也可使用 query string 參數傳遞 scroll_id ,多個值使用英文逗號分割

DELETE /_search/scroll/DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ==,DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAABFmtSWWRRWUJrU2o2ZExpSGJCVmQxYUEAAAAAAAAAAxZrUllkUVlCa1NqNmRMaUhiQlZkMWFBAAAAAAAAAAIWa1JZZFFZQmtTajZkTGlIYkJWZDFhQQAAAAAAAAAFFmtSWWRRWUJrU2o2ZExpSGJCVmQxYUEAAAAAAAAABBZrUllkUVlCa1NqNmRMaUhiQlZkMWFBDELETE /_search/scroll/DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ==,DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAABFmtSWWRRWUJrU2o2ZExpSGJCVmQxYUEAAAAAAAAAAxZrUllkUVlCa1NqNmRMaUhiQlZkMWFBAAAAAAAAAAIWa1JZZFFZQmtTajZkTGlIYkJWZDFhQQAAAAAAAAAFFmtSWWRRWUJrU2o2ZExpSGJCVmQxYUEAAAAAAAAABBZrUllkUVlCa1NqNmRMaUhiQlZkMWFB

3.Sliced Scroll

若是 scroll 查詢返回的文檔數量過多,能夠把它們拆分紅多個切片以便獨立使用

GET /twitter/_search?scroll=1m
{
    "slice": {
        "id": 0, 
        "max": 2 
    },
    "query": {
        "match" : {
            "title" : "elasticsearch"
        }
    }
}
GET /twitter/_search?scroll=1m
{
    "slice": {
        "id": 1,
        "max": 2
    },
    "query": {
        "match" : {
            "title" : "elasticsearch"
        }
    }
}

 

(1) 切片的 id

(2) 最大切片數量

上面的栗子,第一個請求返回的是第一個切片(id : 0)的文檔,第二個請求返回的是第二個切片的文檔。由於咱們設置了最大切片數量是 2 ,因此兩個請求的結果等價於一次不切片的 scroll 查詢結果。默認狀況下,先在第一個分片(shard)上作切分,而後使用如下公式:slice(doc) = floorMod(hashCode(doc._uid), max) 在每一個 shard 上執行切分。例如,若是 shard 的數量是 2 ,而且用戶請求 4 slices ,那麼 id 爲 0 和 2 的 slice 會被分配給第一個 shard ,id 爲 1 和 3 的 slice 會被分配給第二個 shard 。

每一個 scroll 是獨立的,能夠像任何 scroll 請求同樣進行並行處理。

NOTE:若是 slices 的數量比 shards 的數量大,第一次調用時,slice filter 的速度會很是慢。它的複雜度時 O(n) ,內存開銷等於每一個 slice N 位,其中 N 時 shard 中的文檔總數。通過幾回調用後,篩選器會被緩存,後續的調用會更快。可是仍須要限制並行執行的 sliced 查詢的數量,以避免內存激增。

爲了徹底避免此成本,可使用另外一個字段的 doc_values 來進行切片,但用戶必須確保該字段具備如下屬性:

  • 該字段是數字類型
  • 該字段啓用了 doc_values
  • 每一個文檔應當包含單個值。若是一份文檔有指定字段的多個值,則使用第一個值
  • 每一個文檔的值在建立文檔時設置了以後再也不更新,這能夠確保每一個切片得到肯定的結果
  • 字段的基數應當很高,這能夠確保每一個切片得到的文檔數量大體相同
GET /twitter/_search?scroll=1m
{
    "slice": {
        "field": "date",
        "id": 0,
        "max": 10
    },
    "query": {
        "match" : {
            "title" : "elasticsearch"
        }
    }
}

 

NOTE:默認狀況下,每一個 scroll 容許的最大切片數量時 1024。你能夠更新索引設置中的 index.max_slices_per_scroll 來繞過此限制。

3.實現分頁案例

1.實現分頁,每頁20條數據,第一次請求返回第一頁數據

POST /book1/_search?scroll=10m

{
    "size":20
}
{
    "_scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAL6FlI4dDZSMjBYUXJpdEpCXzVRVlFzdmcAAAAAAAAC_hZSOHQ2UjIwWFFyaXRKQl81UVZRc3ZnAAAAAAAAAvsWUjh0NlIyMFhRcml0SkJfNVFWUXN2ZwAAAAAAAAL8FlI4dDZSMjBYUXJpdEpCXzVRVlFzdmcAAAAAAAAC_RZSOHQ2UjIwWFFyaXRKQl81UVZRc3Zn",
    "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 41,
        "max_score": 1,
        "hits": [
            {
                "_index": "book1",
                "_type": "english",
                "_id": "_update",
                "_score": 1,
                "_source": {
                    "scripted_upsert": true,
                    "script": {
                        "id": "my_web_session_summariser",
                        "params": {
                            "pageViewEvent": {
                                "url": "foo.com/bar",
                                "response": 404,
                                "time": "2014-01-01 12:32"
                            }
                        }
                    },
                    "upsert": {}
                }
            },
            {
                "_index": "book1",
                "_type": "english",
                "_id": "79",
                "_score": 1,
                "_source": {
                    "name": "new_name"
                }
            }
。。。。
}

2.使用scroll_id請求後面的幾頁的數據,每次返回一頁

POST /_search/scroll
{
    "scroll":"10m",
    "scroll_id" : "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAL6FlI4dDZSMjBYUXJpdEpCXzVRVlFzdmcAAAAAAAAC_hZSOHQ2UjIwWFFyaXRKQl81UVZRc3ZnAAAAAAAAAvsWUjh0NlIyMFhRcml0SkJfNVFWUXN2ZwAAAAAAAAL8FlI4dDZSMjBYUXJpdEpCXzVRVlFzdmcAAAAAAAAC_RZSOHQ2UjIwWFFyaXRKQl81UVZRc3Zn" 
}

最後一頁只有一條:

{
    "_scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAL6FlI4dDZSMjBYUXJpdEpCXzVRVlFzdmcAAAAAAAAC_hZSOHQ2UjIwWFFyaXRKQl81UVZRc3ZnAAAAAAAAAvsWUjh0NlIyMFhRcml0SkJfNVFWUXN2ZwAAAAAAAAL8FlI4dDZSMjBYUXJpdEpCXzVRVlFzdmcAAAAAAAAC_RZSOHQ2UjIwWFFyaXRKQl81UVZRc3Zn",
    "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 41,
        "max_score": 1,
        "hits": [
            {
                "_index": "book1",
                "_type": "english",
                "_id": "oAmI_mQBbhSmAk-TGrMQ",
                "_score": 1,
                "_source": {
                    "name": "否sdfdsfds",
                    "age": 13,
                    "class": "dsfdsf",
                    "addr": "中國"
                }
            }
        ]
    }
}

繼續執行返回空:

{
    "_scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAL6FlI4dDZSMjBYUXJpdEpCXzVRVlFzdmcAAAAAAAAC_hZSOHQ2UjIwWFFyaXRKQl81UVZRc3ZnAAAAAAAAAvsWUjh0NlIyMFhRcml0SkJfNVFWUXN2ZwAAAAAAAAL8FlI4dDZSMjBYUXJpdEpCXzVRVlFzdmcAAAAAAAAC_RZSOHQ2UjIwWFFyaXRKQl81UVZRc3Zn",
    "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 41,
        "max_score": 1,
        "hits": []
    }
}

 

3.異常:earchContextMissingException

SearchContextMissingException[No search context found for id [721283]];緣由:scroll設置的時間太短了。

相關文章
相關標籤/搜索