这篇文章主要介绍“如何解决ES深度分页问题”,在日常操作中,相信很多人在如何解决ES深度分页问题问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何解决ES深度分页问题”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
ES深度分页问题:
ES 默认采用的分页方式是 from+ size 的形式,类似于MySQL的分页offset+limit。当请求数据量比较大时,Elasticsearch会对分页做出限制,因为此时性能消耗会很大。例如查询1000条数据,假设我们有5个分片,那么每个shard都需要返回1000条数据给 coordinating node,而 coordinating node 需要接收 5*1000 条数据,进行排序后返回1000条数据给客户端。即使每条数据只有 _doc _id 和 _score,这数据量也很大了,如果请请求量很大的情况下,很容易造成ES的OOM。ES中有个设置
index.max_result_window
,默认是10000条数据,如果分页的数据超过第1万条,就拒绝返回结果了。如果集群配置比较好,查询请求量不是特别大,可以适当的放大这个参数。
解决方案:
1:使用scroll遍历
scroll 分为初始化和遍历两步,初始化时将所有符合搜索条件的搜索结果缓存起来,可以想象成快照,在遍历时,从这个快照里取数据,也就是说,在初始化后对索引插入、删除、更新数据都不会影响遍历结果。因此,scroll 并不适合用来做实时搜索,而更适用于后台批处理任务等
API说明:
1)初始化
POST /book/_search?scroll=1m&size=2 { "query": { "match_all": {}} }
遍历
GET /_search/scroll { "scroll": "1m", "scroll_id" : "步骤1中查询出来的值" }
使用java RestHighLevelClient
代码参考如下:
@Autowired private RestHighLevelClient restHighLevelClient; public Result<GoodsDTo> scrollSearch(...查询参数){ BoolQueryBuilder queryBuilder.te = QueryBuilders.boolQuery(); //添加自己的搜索条件.... queryBuilder.must(QueryBuilders.termQuery("type", "商品所属分类); queryBuilder.must(QueryBuilders.matchQuery("name", "商品名称"); //搜索 SearchSourceBuilder searchSourceBuilder = SearchSourceBuilder.searchSource(); searchSourceBuilder.query(queryBuilder); //排序 searchSourceBuilder.sort(SortBuilders.fieldSort("order").order(SortOrder.DESC)); //搜索结果 SearchResponse searchResponse = null; //根据实际情况,判断是多次调用还是一次while遍历查询全部 if (StringUtils.isBlank("scrollId")) { //首屏 searchSourceBuilder.size("每次查询的条数"); SearchRequest searchRequest = new SearchRequest(); searchRequest.indices("索引名").source(searchSourceBuilder); searchRequest.scroll(new Scroll(TimeValue.timeValueMinutes(scrollKeepAliveTime))); searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); } else { //后续根据上次的id滚动 SearchScrollRequest searchScrollRequest = new SearchScrollRequest("scrollId"); searchScrollRequest.scroll(new Scroll(TimeValue.timeValueMinutes(scrollKeepAliveTime))); searchResponse = restHighLevelClient.scroll(searchScrollRequest, RequestOptions.DEFAULT); } SearchHit[] hits = searchResponse.getHits().getHits(); //根据业务需求,处理搜索结果 GoodsDTO result= handleSearchData(hits); //scrollId,往下滚动需要使用 String scrollId = searchResponse.getScrollId(); return Result.succcess(result); }
2:使用search after
满足实时获取下一页的文档信息,search_after 分页的方式是根据上一页的最后一条数据来确定下一页的位置,同时在分页请求的过程中,如果有索引数据的增删改,这些变更也会实时的反映到游标上,这种方式是在es-5.X之后才提供的。为了找到每一页最后一条数据,每个文档的排序字段必须有一个全局唯一值使用 _id 就可以了。
API说明:
GET /book/_search { "query": {"match_all": {}}, "size": 2, "sort": [{"_id": "desc"}] } GET /book/_search { "query": {"match_all": {}}, "size": 2, "search_after": [3], "sort": [{"_id": "desc"}] }
下一页的数据依赖上一页的最后一条的信息 所以不能跳页
使用java RestHighLevelClient
代码参考如下:
@Autowired private RestHighLevelClient restHighLevelClient; public Result<GoodsDTo> searchAfter(...查询参数){ SearchRequest searchRequest = new SearchRequest(index); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); searchSourceBuilder.query(...搜索条件); searchSourceBuilder.size(1000); searchSourceBuilder.sort("_id", SortOrder.ASC); searchSourceBuilder.searchAfter("上一页最后一条数据的id"); searchRequest.source(searchSourceBuilder); SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); SearchHit[] hits = searchResponse.getHits().getHits(); //根据业务需求,处理搜索结果 GoodsDTO result= handleSearchData(hits); return Result.succcess(result); }
到此,关于“如何解决ES深度分页问题”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。