浅析Elasticsearch大数据下深度分页问题
Posted 中兴大数据
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅析Elasticsearch大数据下深度分页问题相关的知识,希望对你有一定的参考价值。
文 | 程丽华
Elasticsearch是一款非常优秀的实时分布式搜索分析引擎,基于它能够达到实时搜索、稳定、可靠、快速、安装方便等优点,目前已经被广泛使用。在使用过程中不可回避的会遇到大数据的遍历、深度分页等问题,本文基于Easticsearch官网和自己的实践测试经验来小结下Elasticsearch深度分页问题。
深度分页存在的问题
深度分页问题之所以存在,是和Elasticsearch搜索内部执行原理分不开的。
如果你想查询第5000-5100数据,查询官网API你很容易就知道,发送如下查询条件就可以做到:
POST auditlog_operation/operlog/_search
{
“from”:5000 //from:定义从哪里开始拿数据
“size”:100 //size:定义一共拿多少条数据
}
查询流程如下:
客户端发送请求到某个node节点。
此node将请求广播到各分片,各分片各自查询前5100条数据。
查询结果返回给node节点,node对结果进行合并整合,取出前5100条数据。
返回给客户端。
相信就算是技术小白也能看出上述深度分页查询的问题,如果你要深度获取1000000到1000100页的数据,性能问题会非常明显的暴露出来:CPU、内存、IO、网络带宽等等,而且Elasticsearch本身就是个Java应用,若并发上去,Elasticsearch会快就会OOM。
解决思路
查询请求:
POST auditlog_operation/operlog/_search
{
“from”:10000
“size”:100
}
如果你尝试发送上述from+size请求来获取10000-10100条数据,对不起会返回错误:
{"error":{"root_cause":[{"type":"query_phase_execution_exception","reason":"Resultwindow is too large, from + size must be less than or equal to: [1000000] butwas [1000100]. See the scroll api for a more efficient way to request largedata sets. This limit can be set by changing the [index.max_result_window]index levelparameter."}],"type":"search_phase_execution_exception","reason":"allshards failed","phase":"query_fetch","grouped":true,"failed_shards":[{"shard":0,"index":"auditlog_operation","node":"iqu-KVKjTRmT3YcT9XAu_w","reason":{"type":"query_phase_execution_exception","reason":"Resultwindow is too large, from + size must be less than or equal to: [1000000] butwas [1000100]. See the scroll api for a more efficient way to request largedata sets. This limit can be set by changing the [index.max_result_window]index level parameter."}}]},"status":500}
迅速查询官网得到更明确的提示:
翻译成中文为:注意 from+size不再适用于查询数据超过index.max_result_window设置值,此默认值为10000。查看 Scroll 或 Search After来获取更高效的深层分页(滚动)。
由此可以得到两个结论:
你可以修改index.max_result_window设置值来继续使用from+size做分页查询。当然效率肯定不高。
如果要找更高效的深度分页方式,请使用Scroll 或者Search After。
Scroll API
Scroll API更适用于检索大量数据(甚至全部数据)。它先做一个初始阶段搜索然后持续批量从Elasticsearch里拉取结果直到返回结果为空。这有点像传统数据库里的cursors(游标)。
Scroll API
Scroll分两步来完成整个遍历过程:
1、初始化
POST index/type/_search?scroll=1m
{
"query": { "match_all": {}}
}
参数解析:
和普通查询一样,可以指定index、type、查询条件等。
Scroll:初始化请求必须指定Scroll参数,此参数告诉Elasticsearch 他要保存此次搜索的上下文多长时间。
2、遍历
POST /_search?scroll=1m
{
"scroll_id":"XXXXXXXXXXXXXXXXXXXXXXX I am scroll id XXXXXXXXXXXXXXX"
}
参数解析:
scroll_id:初始化或上次遍历的返回的scroll_id。
Scroll:必须指定上下文搜索的保持时间,超时scroll_id就过期不能再使用。但是设置时间不可过长,只要能让其保持到下一次遍历(两次遍历时间)完成即可。
index、type不用指定。
默认每次取10条。若需要修改,添加size参数,size的设置也要考虑性能问题,过大一次取数据过多不是自己应用OOM就是es服务OOM。
重复此请求直到返回数据为空,遍历结束。
清除scroll_id
Scroll为了保证遍历不会因为超时而失败,开发人员可能会把超时时间设置稍长,或者遍历被人为中途停止,这个时候可能需要调用清除scroll_id接口。
DELETE /_search/scroll
{
"scroll_id" : ["c2NhbjsxOzY0OmRVRUN4TG5TUXYyaXRIT0Q5SUxiR0E7MTt0b3RhbF9oaXRzOjIwNjgxMjg7"]
}
DELETE /_search/scroll/_all
参数解析:
scroll_id数组:传入要删除的id数组。
若全部清除,可直接传入all。
实际分页应用场景
Scroll API只能遍历全部数据,顶多做到向后翻页,不能向前翻页或者直接跳转到某一页。那在实际使用大数据量场景中,需要实现向前向后翻、直接跳转到某页等功能。就目前我已知的Elasticsearch查询,无法做到真正意义的分页实际应用。我们经过测试和分析,打算结合Scroll API和from+size,融合两者的特点来实现真正意义上的分页查询。
举例:数据库有5649条数据,id依次从1到5649。
1、先使用Scroll API进行遍历全部数据(结果按照id排序)。
2、依次遍历,每次取1000条数据(取多少根据内存考虑),缓存起始id。
上例缓存的id数组为:【1,1001,2001,3001,4001,5001】
3、分页查询时依然使用from+size,只是会根据上述取到的id数据先进行一次过滤,再在1000数据内使用from+size来处理。
例如按照每页100条数据,用户想直接查看第32页的数据的话,那就是发送请求以下请求:
POSTauditlog_operation/operlog/_search
{
“from”:2,
“size”:100, //这1000里取第二页的100条
"query": {
"bool":{
"must":[{
"range":{
"id":{
"gte":"3001",
"lt":"4001" //先将此范围限制在3001-4000条之间
}
}
}],
"must_not":[],
"should":[]
}
},
}
我们在实际开发中使用的Elasticsearch提供的API。代码找对应的API使用就可以,没发现特殊需要注意的坑,此文不再附上。
总结
若有需要查询大量或全部数据却并不关心顺序的应用场景,建议使用scroll-scan,从性能上应该会提高不少。
本文所使用的Elasticsearch版本为2.4.1。
以上是关于浅析Elasticsearch大数据下深度分页问题的主要内容,如果未能解决你的问题,请参考以下文章
干货 | 全方位深度解读 Elasticsearch 分页查询