0


ElasticSearch-SpringBoot中三种分页查询总结

一、from+size 浅分页

  1. 浅分页的原理很简单,就是查询前20条数据,然后截断前10条,只返回10-20的数据。这样其实白白浪费了前10条的查询
  2. es默认采用的是from+size形式,在深度分页的情况下,这种效率是非常低的,但是可以随机跳转页面
  3. es为了性能,会限制我们分页的深度,es目前支持最大的max_result_window = 10000,也就是from+size的大小不能超过10000

DSL 查询方式

  1. GET demo_index/_search
  2. {
  3. "query":{
  4. "match_all": {}
  5. },
  6. "from": 0,
  7. "size": 10,
  8. "sort": [
  9. {
  10. "id": {
  11. "order": "asc"
  12. },
  13. "publish_time": {
  14. "order": "asc"
  15. }
  16. }
  17. ]
  18. }

注意:es是基于分片的,假设有3个分片,from=100,size=10。则会根据排序规则从3个分片中各取回100条数据数据,然后汇总成300条数据后选择最前边的10条数据

RestHighLevelClient 查询方式

  1. /**
  2. * @Description from+size浅分页查询
  3. * @create by meng
  4. */
  5. private List<SearchHit> docSearch(Date time, String title) {
  6. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
  7. BoolQueryBuilder builder = QueryBuilders.boolQuery();
  8. builder.must(QueryBuilders.matchAllQuery())
  9. .filter(QueryBuilders.rangeQuery("publish_time").gt(time.getTime()));
  10. try {
  11. searchSourceBuilder.query(builder)
  12. .sort("id", SortOrder.ASC)
  13. .sort("publish_time", SortOrder.ASC)
  14. .from(0)
  15. .size(10);
  16. SearchRequest searchRequest = new SearchRequest("demo_index").source(searchSourceBuilder);
  17. SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
  18. SearchHit[] hits = searchResponse.getHits().getHits();
  19. if (hits.length > 0) {
  20. return Arrays.asList(hits);
  21. } else {
  22. return null;
  23. }
  24. } catch (IOException e) {
  25. log.error("doc分页查询异常:{} ", e);
  26. }
  27. return null;
  28. }

二、scroll 深分页

  1. from+size查询在10000-50000条数据(1000到5000页)以内的时候还是可以的,但是如果数据过多的话,就会出现深分页问题。为了这个问题,es提出了scroll滚动查询方式
  2. scroll滚动搜索,会在第一次搜索的时候,保存一个当下的快照。之后只会基于该快照提供数据搜索。在这个期间数据如果发生变动,是不会让用户看到的。推荐非实时处理大量数据的情况可以使用
  3. 不适用于有跳页的情景

DSL 查询方式

  1. GET demo_index/_search?scroll=3m
  2. {
  3. "query":{
  4. "match_all": {}
  5. },
  6. "from": 0,
  7. "size": 10,
  8. "sort": [
  9. {
  10. "id": {
  11. "order": "asc"
  12. },
  13. "publish_time": {
  14. "order": "asc"
  15. }
  16. }
  17. ]
  18. }
  1. scroll=3m表示设置scroll_id保留3分钟可用
  2. 使用scroll必须要将from设置为0
  3. size决定后面每次调用_search搜索返回的数量

通过数据返回的_scroll_id读取下一页内容,每次请求将会读取下10条数据,直到数据读取完毕或者scroll_id保留时间截止:

  1. GET _search/scroll
  2. {
  3. "scroll_id":"mengliulUaGVuRmV0Y2g7NTsxMDk5NDpkUmpiR2FjOFNhNnlCM1ZDMWpWYnRRO==",
  4. "scroll": "3m"
  5. }

注意:我们需要再次设置游标查询过期时间为3分钟,GET和POST请求均可,scroll是非常消耗资源的,所以当不需要scroll数据的时候,尽可能快的把scroll_id显式删除掉

清除指定的scroll_id:

  1. DELETE _search/scroll/mengliulUaGVuRmV0Y2g7NTsxMDk5NDpkUmpiR2FjOFNhNnlCM1ZDMWpWYnRRO==

清除所有的scroll:

  1. DELETE _search/scroll/_all

RestHighLevelClient 查询方式

  1. /**
  2. * @Description scroll 深分页
  3. * @create by meng
  4. */
  5. private void docSearch(Date time, String title) {
  6. List<SearchHit> searchHits = new ArrayList<>();
  7. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
  8. BoolQueryBuilder builder = QueryBuilders.boolQuery();
  9. builder.must(QueryBuilders.matchAllQuery())
  10. .filter(QueryBuilders.rangeQuery("publish_time").gt(time.getTime()));
  11. try {
  12. searchSourceBuilder.query(builder)
  13. .sort("id", SortOrder.ASC)
  14. .sort("publish_time", SortOrder.ASC)
  15. .from(0)
  16. .size(10);
  17. SearchRequest searchRequest = new SearchRequest("demo_index").source(searchSourceBuilder);
  18. //失效时间为3min
  19. Scroll scroll = new Scroll(TimeValue.timeValueMinutes(3));
  20. //封存快照
  21. searchRequest.scroll(scroll);
  22. SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
  23. //计算总页数
  24. long totalCount = searchResponse.getHits().getTotalHits().value;
  25. int pageSize = (int) Math.ceil((float) totalCount / 2);
  26. //多次遍历分页,获取结果
  27. String scrollId = searchResponse.getScrollId();
  28. for (int i = 1; i <= pageSize; i++) {
  29. //获取scrollId
  30. SearchScrollRequest searchScrollRequest = new SearchScrollRequest(scrollId);
  31. searchScrollRequest.scroll(scroll);
  32. SearchResponse response = restHighLevelClient.scroll(searchScrollRequest, RequestOptions.DEFAULT);
  33. SearchHits hits = response.getHits();
  34. scrollId = response.getScrollId();
  35. Iterator<SearchHit> iterator = hits.iterator();
  36. while (iterator.hasNext()) {
  37. SearchHit next = iterator.next();
  38. searchHits.add(next);
  39. }
  40. }
  41. } catch (IOException e) {
  42. log.error("doc分页查询异常:{} ", e);
  43. }
  44. }

三、search_after 深分页

  1. 可以在实时数据的情况下深度分页
  2. 为了找每一页最后一条数据,每个文档必须有一个全局唯一值
  3. 不适用于有跳页的情景

DSL 查询方式

  1. GET demo_index/_search?scroll=3m
  2. {
  3. "query":{
  4. "match_all": {}
  5. },
  6. "from": 0,
  7. "size": 10,
  8. "sort": [
  9. {
  10. "id": {
  11. "order": "asc"
  12. },
  13. "publish_time": {
  14. "order": "asc"
  15. }
  16. }
  17. ]
  18. }
  1. 使用search_after必须要将from设置为0
  2. 上边的DSL查询中id是唯一不重复字段,publish_time可能会重复

注意:查询结果会返回sort字段,我们在返回的结果集中,获取最后一条数据的sort属性值,提供给下次查询中search_after

  1. GET demo_index/_search?scroll=3m
  2. {
  3. "query":{
  4. "match_all": {}
  5. },
  6. "size": 10,
  7. "search_after": [
  8. 1638374400000,
  9. "mengliu20211202"
  10. ],
  11. "sort": [
  12. {
  13. "id": {
  14. "order": "asc"
  15. },
  16. "publish_time": {
  17. "order": "asc"
  18. }
  19. }
  20. ]
  21. }

RestHighLevelClient 查询方式

  1. /**
  2. * @Description search_after 深分页
  3. * @create by meng
  4. */
  5. private void docSearch(Date time, String title) {
  6. List<SearchHit> searchHits = new ArrayList<>();
  7. SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
  8. BoolQueryBuilder builder = QueryBuilders.boolQuery();
  9. builder.must(QueryBuilders.matchAllQuery())
  10. .filter(QueryBuilders.rangeQuery("publish_time").gt(time.getTime()));
  11. try {
  12. searchSourceBuilder.query(builder)
  13. .sort("id", SortOrder.ASC)
  14. .sort("publish_time", SortOrder.ASC)
  15. .from(0)
  16. .size(10);
  17. SearchRequest searchRequest = new SearchRequest("demo_index").source(searchSourceBuilder);
  18. SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
  19. SearchHit[] hits = searchResponse.getHits().getHits();
  20. //查询最后一个数据
  21. SearchHit result = hits[hits.length - 1];
  22. //分页查询下一页数据
  23. SearchSourceBuilder searchSourceBuilder2 = new SearchSourceBuilder();
  24. searchSourceBuilder2.query(builder)
  25. .sort("id", SortOrder.ASC)
  26. .sort("publish_time", SortOrder.ASC)
  27. .size(10);
  28. //存储上一次分页的sort信息
  29. searchSourceBuilder2.searchAfter(result.getSortValues());
  30. SearchRequest searchRequest2 = new SearchRequest("demo_index").source(searchSourceBuilder2);
  31. SearchResponse searchResponse2 = restHighLevelClient.search(searchRequest2, RequestOptions.DEFAULT);
  32. SearchHit[] hits2 = searchResponse2.getHits().getHits();
  33. } catch (IOException e) {
  34. log.error("doc分页查询异常:{} ", e);
  35. }
  36. }

本文转载自: https://blog.csdn.net/W_Meng_H/article/details/123940475
版权归原作者 W_Meng_H 所有, 如有侵权,请联系我们删除。

“ElasticSearch-SpringBoot中三种分页查询总结”的评论:

还没有评论