1【理解】
1 请求第20页,假设你有16个分片,则需要在coordinate node 汇总到 shards* (from+size)条记录,即需要 16*(20+10)记录后做一次全局排序,再最终取出 from后的size条结果作为最终的响应。
2 当索引非常非常大(千万或亿),是无法安装 from + size 做深分页的,分页越深则越容易OOM,即便不OOM,也是很消耗CPU和内存资源的。
CPU、内存和IO消耗容易理解,网络带宽问题稍难理解一点。在 query 阶段,每个shards需要返回 1,000,100 条数据给 coordinating node,而 coordinating node 需要接收 10 * 1,000,100 条数据,即使每条数据只有 _doc _id 和 _score,这数据量也很大了,而且,这才一个查询请求,那如果再乘以100呢?
3 为了不合理使用 from + size 造成OOM及最终的集群不稳定,官方在后2.x版本中已增加限定 index.max_result_window:10000作为保护措施 ,即默认 from + size 不能超过1万。当然这个参数可以动态修改,也可以在配置文件配置——但最好不要这么做,除非你知道这意味着什么
4 根据业务字段属性作为游标
数据随时间在变化,数据丢失重复等不可预期的结果 ;不能保证 游标的准确性;
curl -XPUT "http://11.12.84.126:9200/_audit_0102/_settings" -d '{
"index": {
"max_result_window": 100000
}
}'
OOM
???
"reason": {
"type": "query_phase_execution_exception",
"reason": "Result window is too large, from + size must be less than or equal to: [10000] but was [10010]. See the scroll api for a more efficient way to request large data sets. This limit can be set by changing the [index.max_result_window] index level setting."
2【解决方案】于是出现了:
海量导数据第一版:
1 A scrolled search takes a snapshot in time(适时)
2 Normally, the background merge process optimizes the index by merging together smaller segments to create new bigger segments, at which time the smaller segments are deleted(合并小的分段到新的大的分段,同时小的分段被删除)于是更多的file handles 被占用
3【应用】
POST /note_/recommend_note/_search?scroll=1m
{
"size": 2,
"query": {
"match" : {
"operation_tags" : "育儿"
}
}
}
返回:
"_scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAF1RhFl9ISGlIYnNhUzlhVDdLd01wOGc4WXcAAAAAABl0kRZyTmhoMmJNVlJzQ1VzOEROVUhHRlZnAAAAAAAZdJIWck5oaDJiTVZSc0NVczhETlVIR0ZWZwAAAAAAGXSTFnJOaGgyYk1WUnNDVXM4RE5VSEdGVmcAAAAAABdUYhZfSEhpSGJzYVM5YVQ3S3dNcDhnOFl3"
第二步:
POST /_search/scroll
{
"scroll" : "1m",
"scroll_id" : "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAF1RhFl9ISGlIYnNhUzlhVDdLd01wOGc4WXcAAAAAABl0kRZyTmhoMmJNVlJzQ1VzOEROVUhHRlZnAAAAAAAZdJIWck5oaDJiTVZSc0NVczhETlVIR0ZWZwAAAAAAGXSTFnJOaGgyYk1WUnNDVXM4RE5VSEdGVmcAAAAAABdUYhZfSEhpSGJzYVM5YVQ3S3dNcDhnOFl3"
} 一直执行相同的请求直到返回结果为空
第三步:GET /_nodes/stats/indices/search
"indices": {
"search": {
"open_contexts": 3,
"query_total": 1668363,
"query_time_in_millis": 14717409,
"query_current": 0,
"fetch_total": 1570213,
"fetch_time_in_millis": 1323603,
"fetch_current": 0,
"scroll_total": 62483,
"scroll_time_in_millis": 679341,
"scroll_current": 3,
"suggest_total": 0,
"suggest_time_in_millis": 0,
"suggest_current": 0
}
}
第四步清理 单个:
DELETE /_search/scroll/DnF1ZXJ5VGhlbkZldGNoBQAAAAAAFpvrFm5mZ1ZidkgwUW9hOUR4VVkwUnQ2SmcAAAAAABl1KhZyTmhoMmJNVlJzQ1VzOEROVUhHRlZnAAAAAAAWm-oWbmZnVmJ2SDBRb2E5RHhVWTBSdDZKZwAAAAAAF1VFFl9ISGlIYnNhUzlhVDdLd01wOGc4WXcAAAAAABdVRhZfSEhpSGJzYVM5YVQ3S3dNcDhnOFl3
{
"succeeded": true,
"num_freed": 5
}
所有:
DELETE /_search/scroll/_all
{
"succeeded": true,
"num_freed": 3
}
第二种做法
POST ip:port/my_index/my_type/_search?search_type=scan&scroll=1m&size=50
{
"query": { "match_all": {}}
}
java:
第一次查询
SearchResponse response1 = client.prepareSearch("_audit_0221").setTypes("_log_0221")
.setQuery(boolQueryBuilder)
.setSearchType(.setSearchType(SearchType.DEFAULT))
.setSize(10).setScroll(TimeValue.timeValueMinutes(5))
.addSort("logTime", SortOrder.DESC)
.execute().actionGet();//第一次查询
for (SearchHit searchHit : response1.getHits().hits()) {
biz handle....;
}
第二次查询
while (response1.getHits().hits().length>0) {
for (SearchHit searchHit : response1.getHits().hits()) {
System.out.println(searchHit.getSource().toString());
}
response1 = client.prepareSearchScroll(response1.getScrollId()).setScroll(TimeValue.timeValueMinutes(5))
.execute().actionGet();
}
一次性查询清理现场:
ClearScrollRequest request = new ClearScrollRequest();
request.addScrollId(scrollId);
client.clearScroll(request);
4【评价】
这时如果你的产品经理要求你按照常规的做法去分页,你可以很明确的告诉他,你的系统不支持这么深度的分页,翻的越深,性能也就越差。
不过这种深度分页场景在现实中确实存在,有些场景下,我们可以说服产品经理很少有人会翻看很久之前的历史数据,但是有些场景下可能一天都产生几百万。这个时候我们可以根据具体场景具体分析。
参考https://my.oschina.net/u/1787735/blog/3024051
5 【scroll和scroll-scan】区别
1. scroll支持排序,scroll-scan不支持排序,是按照索引顺序返回,可以提高查询效率。
2. scroll-scan第一次查询只支持返回id,没有结果。
总结:
1. es的分页查询不支持深度分页,如果偏要使用要结合具体业务场景进行使用。不能当成关系型数据库中的分页进行使用。
2. 要想提高产品体验和查询效率不能过于依赖技术,要结合需求进行分析以提高体验,因为很多搜索类产品都不支持深度分页。
3. 如果在不涉及排序的情况下尽量使用scroll-scan,它是按照索引顺序返回,提高效率。
PS:elasticSearch各个版本可能都稍有区别,但是原理相同。