Scroll
search
請求返回一個單一的結果“頁”,而 scroll
API 可以被用來檢索大量的結果(甚至所有的結果),就像在傳統數據庫中使用的游標 cursor。
滾動並不是為了實時的用戶響應,而是為了處理大量的數據,例如,為了使用不同的配置來重新索引一個 index 到另一個 index 中去。
client 支持:Perl 和 Python
注意:從 scroll 請求返回的結果反映了 search
發生時刻的索引狀態,就像一個快照。后續的對文檔的改動(索引、更新或者刪除)都只會影響后面的搜索請求。
為了使用 scroll,初始搜索請求應該在查詢中指定 scroll
參數,這可以告訴 Elasticsearch 需要保持搜索的上下文環境多久(參考Keeping the search context alive),如 ?scroll=1m
。
POST /twitter/tweet/_search?scroll=1m
{
"query": {
"match" : {
"title" : "elasticsearch"
}
}
}
使用上面的請求返回的結果中包含一個 scroll_id
,這個 ID 可以被傳遞給 scroll
API 來檢索下一個批次的結果。
POST /_search/scroll
{
"scroll" : "1m",
"scroll_id" : "c2Nhbjs2OzM0NDg1ODpzRlBLc0FXNlNyNm5JWUc1"
}
GET
或者POST
可以使用- URL不應該包含
index
或者type
名字——這些都指定在了原始的search
請求中。 scroll
參數告訴 Elasticsearch 保持搜索的上下文等待另一個1m
scroll_id
參數
每次對 scroll
API 的調用返回了結果的下一個批次知道沒有更多的結果返回,也就是直到 hits
數組空了。
為了向前兼容,scroll_id
和 scroll
可以放在查詢字符串中傳遞。scroll_id
則可以在請求體中傳遞。
curl -XGET 'localhost:9200/_search/scroll?scroll=1m' -d 'c2Nhbjs2OzM0NDg1ODpzRlBLc0FXNlNyNm5JWUc1'
注意:初始搜索請求和每個后續滾動請求返回一個新的
_scroll_id
,只有最近的_scroll_id
才能被使用。
如果請求指定了聚合(aggregation),僅僅初始搜索響應才會包含聚合結果。
使用 scroll-scan 的高效滾動
使用 from and size
的深度分頁,比如說 ?size=10&from=10000
是非常低效的,因為 100,000
排序的結果必須從每個分片上取出並重新排序最后返回 10
條。這個過程需要對每個請求頁重復。
scroll
API 保持了那些已經返回記錄結果,所以能更加高效地返回排序的結果。但是,按照默認設定排序結果仍然需要代價。
一般來說,你僅僅想要找到結果,不關心順序。你可以通過組合 scroll
和 scan
來關閉任何打分或者排序,以最高效的方式返回結果。你需要做的就是將 search_type=scan
加入到查詢的字符串中:
POST /twitter/tweet/_search?scroll=1m&search_type=scan
{
"query": {
"match" : {
"title" : "elasticsearch"
}
}
}
- 設置
search_type
為scan
可以關閉打分,讓滾動更加高效。
掃描式的滾動請求和標准的滾動請求有四處不同:
- 不算分,關閉排序。結果會按照在索引中出現的順序返回。
- 不支持聚合
- 初始
search
請求的響應不會在hits
數組中包含任何結果。第一批結果就會按照第一個scroll
請求返回。 - 參數
size
控制了每個分片上而非每個請求的結果數目,所以size
為10
的情況下,如果命中了 5 個分片,那么每個 scroll 請求最多會返回 50 個結果。
如果你想支持打分,即使不進行排序,將 track_scores
設置為 true
。
保持搜索上下文存活
參數 scroll
(傳遞給 search
請求還有每個 scroll
請求)告訴 Elasticsearch 應該需要保持搜索上下文多久。這個值(比如說 1m
,詳情請見the section called “Time units)並不需要長到可以處理所有的數據——僅僅需要足夠長來處理前一批次的結果。每個 scroll
請求(包含 scroll
參數)設置了一個新的失效時間。
一般來說,背后的合並過程通過合並更小的分段創建更大的分段來優化索引,同時會刪除更小的分段。這個過程在滾動時進行,但是一個打開狀態的搜索上下文阻止了舊分段在使用的時候不會被刪除。這就是 Elasticsearch 能夠不管后續的文檔的變化,返回初始搜索請求的結果的原因。
保持舊的分段存活意味着會產生更多的文件句柄。確保你配置了節點有空閑的文件句柄。參考File Descriptors
你可以檢查有多少搜索上下文開啟了,
GET /_nodes/stats/indices/search?pretty
清除 scroll API
搜索上下文當 scroll
超時就會自動移除。但是保持 scroll 存活需要代價,如在前一節講的那樣,所以 scrolls 當scroll不再被使用的時候需要被用 clear-scroll
顯式地清除:
DELETE /_search/scroll
{
"scroll_id" : ["c2Nhbjs2OzM0NDg1ODpzRlBLc0FXNlNyNm5JWUc1"]
}
多個 scroll ID 可按照數據傳入:
DELETE /_search/scroll
{
"scroll_id" : ["c2Nhbjs2OzM0NDg1ODpzRlBLc0FXNlNyNm5JWUc1", "aGVuRmV0Y2g7NTsxOnkxaDZ"]
}
所有搜索上下文可以通過 _all
參數而清除:
DELETE /_search/scroll/_all
scroll_id
也可以使用一個查詢字符串的參數或者在請求的body中傳遞。多個scroll ID 可以使用逗號分隔傳入:
DELETE /_search/scroll/DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ==,DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAABFmtSWWRRWUJrU2o2ZExpSGJCVmQxYUEAAAAAAAAAAxZrUllkUVlCa1NqNmRMaUhiQlZkMWFBAAAAAAAAAAIWa1JZZFFZQmtTajZkTGlIYkJWZDFhQQAAAAAAAAAFFmtSWWRRWUJrU2o2ZExpSGJCVmQxYUEAAAAAAAAABBZrUllkUVlCa1NqNmRMaUhiQlZkMWFB
Sliced 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"
}
}
}
每個滾動都是獨立的,可以像任何滾動請求一樣並行處理。第一個請求的結果返回屬於第一個slice(id:0)的文檔,第二個請求的結果返回屬於第二個slice的文檔。由於最大切片數設置為2,因此兩個請求的結果的並集等效於沒有切片的滾動查詢的結果。默認情況下,首先在shard集合上進行分割,然后在每個shard上使用_uid字段在本地進行分割,其公式如下: slice(doc) = floorMod(hashCode(doc._uid), max)
例如,如果shard數等於2且用戶請求4個slice,則分配片0和2到第一個shard,切片1和3分配給第二個shard。
如果
slice
的數量大於shard的數量,則切片過濾器在第一次調用時非常慢,它具有O(N)的復雜度,並且存儲器成本等於每個slice
的N倍,其中N是在shard中的文檔的總數。在幾次調用之后,緩存過濾器在后續調用應該更快,但是您應該限制並行執行的切片查詢的數量以避免內存溢出。
為了完全避免這種成本,可以使用doc_values
另一個字段來進行切片,但用戶必須確保該字段具有以下屬性:
- 該字段是數字。
doc_values
在該字段上啟用- 每個文檔都應包含單個值。如果文檔具有指定字段的多個值,則使用第一個值。
- 創建文檔時永遠不會更新每個文檔的值。這可確保每個切片獲得確定性結果。
- 該領域的基數應該很高。這可確保每個切片獲得大致相同數量的文檔。
GET /twitter/_search?scroll=1m
{
"slice": {
"field": "date",
"id": 0,
"max": 10
},
"query": {
"match" : {
"title" : "elasticsearch"
}
}
}
對於僅附加基於時間的索引,timestamp
可以安全地使用該字段。
默認情況下,每個滾動允許的最大切片數限制為1024.您可以更新
index.max_slices_per_scroll
索引設置以繞過此限制。