es實戰之查詢大量數據
背景
項目中已提供海量日誌數據的多維實時查詢,客戶提出新需求:將數據導出。
將數據導出分兩步:
- 查詢大量數據
- 將數據生成文件並下載
本文主要探討第一步,在es中查詢大量數據或者說查詢大數據集。
es支持的查詢數量
es默認支持的查詢數量或者說查詢深度是10,000。
可以動態修改max_result_window這個參數的設置,默認為10,000。
PUT xz-logs/_settings?preserve_existing=true
{
"index.max_result_window" : "10000000"
}
es search api
from + size
GET /_search { "from" : 0, "size" : 10, "query" : { "term" : { "user" : "kimchy" } } }
當Elasticsearch響應請求時,它必須確定docs的順序,全局排序響應結果。
如果請求的頁數較少時,假設每頁10個docs——即pageSize=10, 此時Elasticsearch不會有什麽問題。
但若取的頁數較大時(深分頁),如請求第20頁,Elasticsearch不得不取出所有分片上的第1頁到第20頁的所有docs,假設你有16個分片,則需要在coordinate node 匯總到 shards* (from+size)條記錄,即需要 16*(20+10)記錄後做一次全局排序,再最終取出 from後的size條結果作為最終的響應。
所以:當索引非常非常大(千萬或億),是無法安裝 from + size 做深分頁的,分頁越深則越容易OOM,即便不OOM,也是很消耗CPU和內存資源的。
scroll
scroll類似於數據庫中的遊標。
遊標查詢允許我們 先做查詢初始化,然後再批量地拉取結果。 這有點兒像傳統數據庫中的 cursor 。
遊標查詢會取某個時間點的快照數據。 查詢初始化之後索引上的任何變化會被它忽略。 它通過保存舊的數據文件來實現這個特性,結果就像保留初始化時的索引 視圖 一樣。
深度分頁的代價根源是結果集全局排序,如果去掉全局排序的特性的話查詢結果的成本就會很低。 遊標查詢用字段 _doc 來排序。 這個指令讓 Elasticsearch 僅僅從還有結果的分片返回下一批結果。
第一次查詢
GET /old_index/_search?scroll=1m { "query": { "match_all": {}}, "sort" : ["_doc"], "size": 1000 }
第二次查詢
GET /_search/scroll
{
"scroll": "1m",
"scroll_id" : "cXVlcnlUaGVuRmV0Y2g7NTsxMDk5NDpkUmpiR2FjOFNhNnlCM1ZDMWpWYnRROzEwOTk1OmRSamJHYWM4U2E2eUIzVkMxalZidFE7MTA5OTM6ZFJqYkdhYzhTYTZ5QjNWQzFqVmJ0UTsxMTE5MDpBVUtwN2lxc1FLZV8yRGVjWlI2QUVBOzEwOTk2OmRSamJHYWM4U2E2eUIzVkMxalZidFE7MDs="
}
Scanning Scroll API
如果只對查詢結果感興趣而不關心結果的順序,可以使用更高效的scanning scroll。使用方法非常簡單,只需在查詢語句後加上“search_type=scan”即可。
search after(5.0新特性)
search_after is not a solution to jump freely to a random page but rather to scroll many queries in parallel. It is very similar to the scroll API but unlike it, the search_after parameter is stateless, it is always resolved against the latest version of the searcher. For this reason the sort order may change during a walk depending on the updates and deletes of your index.
search_after類似於scroll,不同之處是:search_after是無狀態的,它總是針對最新版本的搜索器進行解析。由於更新或者刪除索引,搜索的排序結果可能會發生變化。
bulk
bulk是將多個請求合並成一個請求,如下所示:
POST _bulk
{ "index" : { "_index" : "test", "_type" : "_doc", "_id" : "1" } }
{ "field1" : "value1" }
{ "delete" : { "_index" : "test", "_type" : "_doc", "_id" : "2" } }
{ "create" : { "_index" : "test", "_type" : "_doc", "_id" : "3" } }
{ "field1" : "value3" }
{ "update" : {"_id" : "1", "_type" : "_doc", "_index" : "test"} }
{ "doc" : {"field2" : "value2"} }
其他
插件: elasticsearch-dataformat
實際使用過程中,該插件不好用。如果帶查詢條件,數據無法導出。查看其依賴的jar包,估計其調用poi來生成csv文件, 估計速度快不了。
這種插件做demo可以,實際生成中,不太敢使用,因為不可控因素太多。
總結
綜上所述,最後采用scroll api來解決es查詢大量數據的問題。不過數據量大一點,查詢時間就比較長,在本人的集群中,查詢10w條,需要將近1分鐘的時間。(附本人集群:3個節點。每個節點配置為cpu 8核,heap size 16G,每個索引有5個分片、1個副本。數據量每天4500w)
es實戰之查詢大量數據