索引:共4T,5个索引,共100亿条数据
查询语句:
{
"query":{
"term":{
"app_servername":"set-app-heatontime01"
}
},
"size":10000,
"sort": [
{"es_timestamp": "asc"},
]
}
符合条件结果总共5亿条数据。
拉取结果耗时:
拉取条数 |
10万 |
20万 |
50万 |
100万 |
200万 |
300万 |
500万 |
耗时 |
13.5s |
30s |
76s |
158s |
313s |
560s |
787s |
es的并发scroll不适合深度翻页,只适合拉取所有数据。
es search_after也不适合做深度分页,分页多了,内存不够,将查询失败。
我们在分页的时候如果用from+size的话,from + size 默认不能超过1万条数据。
使用from + size方式是将请求达到分片节点上,如果有n个副分片,则查询数据是 n * (from+size) 如果from很大的话,会造成oom或者网络资源的浪费。
若使用scroll的话,尽管能读取许多数据,但是查询出来的结果都是无序的。
对于深度分页,到底有没有比较理想的解决方案,既能拉取比较多的数据,拉取的数据也都是有序的?那么你可以了解下search_after。
第一步
代码块
GET twitter/tweet/_search
{
"size": 10,
"query": {
"match" : {
"title" : "elasticsearch"
}
},
"sort": [
{"es_timestamp": "asc"},
{"_uid": "desc"}
]
}
和我们正常查询没有什么区别,这里必须要使用sort进行排序。
同时sort中必须至少包含一个唯一的字段来进行排序,推荐使用_uid(必须有)。
同时日志中心的数据,也尽量将es_timestamp加上去一起排序。
若这里没有唯一的字段,那么每次获取结果将不够精确,影响使用。
返回的response如下:
代码块
{
"_index": "log.xxx.20181120_2",
"_type": "logs",
"_id": "AWcvRPOwqf9vJFGaVemX",
"_score": null,
"_source": {
"mt_level": "WARN",
"traceId__": "5919513527137007410",
"mt_appkey": "xxxx",
"mt_thread": "xxxx0",
"es_timestamp": "2018/11/20 11:58:23 +0800",
"mt_logger_name": "xxxx",
"mt_datetime": "2018-11-20 11:58:23+0800",
"message": " select scope cost:0\n",
"mt_servername": "xxxxx"
},
"sort": [
1542686303000,
"logs#AWcvRPOwqf9vJFGaVemX"
]
}
注意这里返回的sort结构。返回了每条数据的es_timestamp和_uid。
第二次分页
代码块
GET twitter/tweet/_search
{
"size": 10,
"query": {
"match" : {
"title" : "elasticsearch"
}
},
"search_after": [1463538857, "tweet#654323"],
"sort": [
{"es_timestamp": "asc"},
{"_uid": "desc"}
]
}
若我们想接着上次读取的结果进行读取下一页数据,第二次查询在第一次查询时的语句基础上添加search_after,并指明从哪个数据后开始读取。
search_after原理比较简单:
因为我们在sort中指明了唯一字段_uid,所以查询的数据整体肯定是有序的,在第二次查询时,同时将search_after指定的值作为查询条件(类似游标),指定从整个有序数据哪个位置继续查询。
缺陷:
search_after并不能代替from+ size和scroll,它每第二次读取时,必须要指定查询地址(游标search_after),实际使用时,只能一页一页的向下翻,而不能跳页查询。