目录

一、命令方式做分页

1.1 常见的分页方式:from+size

1.2 scroll方式

1.3 search_after 的方式

二、java api做elasticsearch分页

2.1 浅分页from和size方式

2.2 使用scroll深分页


一、命令方式做分页

1.1 常见的分页方式:from+size

elasticsearch默认采用的分页方式是from+size的形式。但是,在深度分页的情况下,这种使用方式的效率是非常低的,比如from=5000,size=10,es需要在各个分片上匹配排序并得到5000*10条有效数据,然后在结果集中取最后10条数据返回。除了会遇到效率上的问题,还有一个无法解决的问题:es目前支持最大的skip值是max_result_window(默认为10000),也就是说,当from+size > max_result_window时,es将返回错误。

问题描述:

比如当客户线上的es数据出现问题,当分页到几百页的时候,es无法返回数据,此时为了恢复正常使用,我们可以采用紧急规避的方式,就是将max_result_window的值调至50000。

解决方案:

curl -XPUT "127.0.0.1:9200/custm/_settings" -d
'{ "index" : { "max_result_window" : 50000 }
}'

上面这种解决方案只是暂时解决问题。当es的使用越来越多时,数据量越来越大,深度分页的场景越来越复杂时,可以使用另一种分页方式scroll。

1.2 scroll方式

为了满足深度分页的场景,es提供了scroll方式进行分页读取。原理为:对某次查询生成一个游标scroll_id,后续的查询只需要根据这个游标去取数据,直到结果集中返回的hits字段为空列表,就表示遍历结束。Scroll的作用不是用于实时查询数据,由于它会对es做多次请求,不可能做到实时查询;它的主要作用是用来查询大量数据或全部数据。

使用scroll,每次只能获取一页的内容,然后会返回一个scroll_id。根据返回的这个scroll_id可以不断地获取下一页的内容,所以scroll并不适用于有跳页的情景

使用curl进行深度分页读取的过程如下:

1、 获取第一个scroll_id,url参数包括/index/type和scroll,scroll字段指定了scroll_id的有效生存时间,过期后会被es自动清理。

curl -H "Content-Type: application/json" -XGET '192.168.200.100:9200/chuyun/_search?pretty&scroll=2m' -d'
{"query":{"match_all":{}}, "sort": ["_doc"]}'

2、在遍历的时候,依据上一次遍历中的_scroll_id,带上scroll参数,重复上一次的遍历步骤,直到返回的数据为空列表,表示遍历完成。
注意:每次都要传参数scroll,刷新搜索结果的缓存时间;不需要指定index和type(不要把缓存的时间设置太长,占用内存)进行后续查询。

curl -H "Content-Type: application/json" -XGET '192.168.200.100:9200/_search/scroll?pretty'  -d'
{"scroll" : "2m", "scroll_id" : "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAABWFm43cDd3eERJVHNHMHJzSlNkajdPUHcAAAAAAAAAVxZuN3A3d3hESVRzRzByc0pTZGo3T1B3AAAAAAAAAFsWazlvUFptQnNTdXlmNmZRTl80cVdCdwAAAAAAAABVFm43cDd3eERJVHNHMHJzSlNkajdPUHcAAAAAAAAAWhZrOW9QWm1Cc1N1eWY2ZlFOXzRxV0J3"
}'

3、scroll的删除

删除所有scroll_id:

curl -XDELETE 192.168.200.100:9200/_search/scroll/_all

删除指定的scroll_id:

curl -XDELETE 192.168.200.100:9200/_search/scroll -d
'{"scroll_id" : ["cXVlcnlBbmRGZXRjaDsxOzg3OTA4NDpTQzRmWWkwQ1Q1bUlwMjc0WmdIX2ZnOzA7"]}'

1.3 search_after 的方式

注意:使用search_after,必须要设置from=0。 获取“上一次查询返回的最后一条数据里sort属性的值”,将其赋值给search_after属性。这里,我使用_id作为唯一值排序。

scroll的方式,官方不建议用于实时的请求(一般用于数据导出)。因为每一个scroll_id不仅会占用大量的资源,而且会生成历史快照,对于数据的变更不会反映到快照上。

search_after分页的方式是根据上一页的最后一条数据来确定下一页的位置,同时在分页请求的过程中,如果有索引数据的增删改查,这些变更也会实时的反映到游标上。但是,需要注意,因为每一页的数据依赖于上一页的最后一条数据,所以没法跳页请求。

为了找到每一页最后一条数据,每个文档必须有一个全局唯一值,官方推荐使用_uuid作为全局唯一值,当然在业务上的id也可以。

例如,在下面实例中,我先根据id做倒序排列:

curl -H "Content-Type: application/json" -XGET '192.168.200.100:9200/chuyun/_search?pretty' -d'
{"size": 2,"from": 0,"sort": [{"_id": {"order": "desc"}}]
}'

查询结果为:

{"took" : 7,"timed_out" : false,"_shards" : {"total" : 5,"successful" : 5,"skipped" : 0,"failed" : 0},"hits" : {"total" : 3,"max_score" : null,"hits" : [{"_index" : "chuyun","_type" : "article","_id" : "3","_score" : null,"_source" : {"id" : 3,"title" : "《青玉案·元夕》","content" : "东风夜放花千树,更吹落,星如雨。宝马雕车香满路。凤箫声动,玉壶光转,一夜鱼龙舞。蛾儿雪柳黄金缕,笑语盈盈暗香去。众里寻他千百度,蓦然回首,那人却在,灯火阑珊处。","viewCount" : 786,"createTime" : 1557471088252,"updateTime" : 1557471088252},"sort" : ["3"]},{"_index" : "chuyun","_type" : "article","_id" : "2","_score" : null,"_source" : {"id" : 2,"title" : "《蝶恋花》","content" : "伫倚危楼风细细,望极春愁,黯黯生天际。草色烟光残照里,无言谁会凭阑意。拟把疏狂图一醉,对酒当歌,强乐还无味。衣带渐宽终不悔,为伊消得人憔悴。","viewCount" : null,"createTime" : 1557471087998,"updateTime" : 1557471087998},"sort" : ["2"]}]}
}

将search_after赋值为“上次查询时sort的返回值”,搜索下一页:

curl -H "Content-Type: application/json" -XGET '192.168.200.100:9200/chuyun/_search?pretty' -d'
{"size": 2,"from": 0,"search_after": [2],"sort": [{"_id": {"order": "desc"}}]
}'

查询结果为:

{"took" : 12,"timed_out" : false,"_shards" : {"total" : 5,"successful" : 5,"skipped" : 0,"failed" : 0},"hits" : {"total" : 3,"max_score" : null,"hits" : [{"_index" : "chuyun","_type" : "article","_id" : "1","_score" : null,"_source" : {"id" : 1,"title" : "《蝶恋花》","content" : "槛菊愁烟兰泣露,罗幕轻寒,燕子双飞去。明月不谙离恨苦,斜光到晓穿朱户。昨夜西风凋碧树,独上高楼,望尽天涯路。欲寄彩笺兼尺素,山长水阔知何处?","viewCount" : 678,"createTime" : 1557471087754,"updateTime" : 1557471087754},"sort" : ["1"]}]}
}

二、java api做elasticsearch分页

按照一般的查询流程,比如我想查找前10条数据:

1、 客户端请求发给某个节点。

2、 该节点转发请求给各个分片,查询每个分片上的前10条数据。

3、 结果返回给节点,整合数据,提取前10条。

4、 返回给请求客户端。

当我想查询第10条到20条的时候,就需要用到分页查询。

工具类:

*** 构建elasticsrarch client*/
public class LowClientUtil {private static TransportClient client;public TransportClient CreateClient() throws Exception {// 先构建clientSystem.out.println("11111111111");Settings settings=Settings.builder().put("cluster.name","elasticsearch1").put("client.transport.ignore_cluster_name", true)  //如果集群名不对,也能连接.build();//创建ClientTransportClient client = new PreBuiltTransportClient(settings).addTransportAddress(new TransportAddress(InetAddress.getByName("192.168.200.100"),9300));return client;}
}

准备数据:

/*** 准备数据* @throws Exception*/
public static void createDocument100() throws Exception {for (int i = 1; i <= 100; i++) {try {HashMap<String, Object> map = new HashMap<>();map.put("title", "第" + i + "本书");map.put("author", "作者" + i);map.put("id", i);map.put("message", i + "是英国物理学家斯蒂芬·霍金创作的科学著作,首次出版于1988年。全书");IndexResponse response = client.prepareIndex("blog2", "article").setSource(map).get();// 索引名称String _index = response.getIndex();// 类型String _type = response.getType();// 文档IDString _id = response.getId();// 版本long _version = response.getVersion();// 返回的操作状态RestStatus status = response.status();System.out.println("索引名称:" + _index +" " + "类型 :" + _type + " 文档ID:" + _id +" 版本 :" + _version + " 返回的操作状态:" + status );} catch (Exception e) {e.printStackTrace();}}
}

2.1 浅分页from和size方式

查询一批数据,从第10条开始逐页返回,每页返回10条。

/*** from-sizesearchRequestBuilder 的 setFrom【从0开始】 和 setSize【查询多少条记录】方法实现* */
public static void sortPages(){// 搜索数据SearchRequestBuilder searchRequestBuilder = client.prepareSearch("blog2").setTypes("article").setQuery(QueryBuilders.matchAllQuery());//默认每页10条记录final long totalHits = searchRequestBuilder.get().getHits().getTotalHits();//总条数final int pageDocument = 10 ;//每页显示多少条final long totalPage = totalHits / pageDocument;//总共分多少页for(int i=1;i<=totalPage;i++){System.out.println("=====================当前打印的是第 :"+i+" 页==============");//setFrom():从第几条开始检索,默认是0。//setSize():查询多少条文档。searchRequestBuilder.setFrom(i*pageDocument).setSize(pageDocument);SearchResponse searchResponse = searchRequestBuilder.get();SearchHits hits = searchResponse.getHits();Iterator<SearchHit> iterator = hits.iterator();while (iterator.hasNext()) {SearchHit searchHit = iterator.next(); // 每个查询对象System.out.println(searchHit.getSourceAsString()); // 获取字符串格式打印}}
}

2.2 使用scroll深分页

对于浅分页(from-size)方式,当Elasticsearch响应请求时,它必须确定docs的顺序,排列响应结果。

如果请求的页数较少(假设每页20个docs), Elasticsearch不会有性能问题。但是,如果页数较大,比如请求第20页,Elasticsearch不得不取出第1页到第20页的所有docs,再去除第1页到第19页的docs,得到第20页的docs,这非常耗费性能。

解决的方式就是使用scroll。scroll维护了当前索引段的一份快照信息(这个快照信息是你执行这个scroll查询时的快照),在这个查询后任何新进来的索引数据,都不会在这个快照中查询到。但是,相对于from和size,它不是“查询所有数据、然后剔除不要的部分”,而是“记录一个读取的位置,保证下一次快速继续读取”。

可以把 scroll 分为初始化和遍历两步:

1、初始化时,将所有符合搜索条件的搜索结果缓存起来,作为快照。

2、遍历时,从这个快照里取数据。也就是说,在初始化后,对索引插入、删除、更新数据,都不会影响遍历结果。

public static void scrollPages(){//获取Client对象,设置索引名称,搜索类型(SearchType.SCAN)[5.4移除,对于java代码,直接返回index顺序,不对结果排序],搜索数量,发送请求SearchResponse searchResponse = client.prepareSearch("blog2").setSearchType(SearchType.DEFAULT)//执行检索的类别.setSize(10).setScroll(new TimeValue(1000)).execute().actionGet();//注意:首次搜索并不包含数据//获取总数量long totalCount=searchResponse.getHits().getTotalHits();int page=(int)totalCount/(10);//计算总页数System.out.println("总页数: ================="+page+"=============");for (int i = 1; i <= page; i++) {System.out.println("=========================页数:"+i+"==================");searchResponse = client.prepareSearchScroll(searchResponse.getScrollId())//再次发送请求,并使用上次搜索结果的ScrollId.setScroll(new TimeValue(1000)).execute().actionGet();SearchHits hits = searchResponse.getHits();for(SearchHit searchHit : hits){System.out.println(searchHit.getSourceAsString());// 获取字符串格式打印}}
}

Elasticsearch分页解决方案研究相关推荐

  1. Elasticsearch分页解决方案

    Elasticsearch分页解决方案 参考文章: (1)Elasticsearch分页解决方案 (2)https://www.cnblogs.com/yfb918/p/11023581.html 备 ...

  2. Java架构直通车——ElasticSearch深度分页解决方案

    文章目录 分页查询 深度分页 深度分页解决方案 分页查询 分页查询是这样的: POST /shop/_doc/_search {"query": {"match_all& ...

  3. 分页解决方案 —— GridView + QuickPager + QuickPager_SQL + DataAccessLibrary + 数据库

    这里要说的不仅仅是一个分页控件,而是一套解决方案,包括如何显示数据.显示分页导航,如何得到分页用的sql语句(等效于存储过程),如何提取数据,如何绑定控件,如何响应事件,添加.修改.删除数据后如何更新 ...

  4. 前端异常监控解决方案研究

    摘要: 异常监控不复杂也不简单啊... 原文:前端异常监控解决方案研究 作者:frustigor 前端监控包括行为监控.异常监控.性能监控等,本文主要讨论异常监控.对于前端而言,和后端处于同一个监控系 ...

  5. 初中python编程教学的困难与解决研究_初中信息技术课程中存在的问题与解决方案研究...

    初中信息技术课程中存在的问题与解决方案研究 初中信息技术课程中存在的问题与解决方案研究 张春秀 (宁夏中卫市海原县三河中学) 摘 要:针对初中信息技术课程的教学特点展开分析,找出初中信息技术课程中存在 ...

  6. elasticsearch 分页查询实现方案——Top K+归并排序

    elasticsearch 分页查询实现方案 1. from+size 实现分页 from表示从第几行开始,size表示查询多少条文档.from默认为0,size默认为10, 注意:size的大小不能 ...

  7. 电商订单ElasticSearch同步解决方案--使用logstash

    电商订单ElasticSearch同步解决方案--使用logstash 参考文章: (1)电商订单ElasticSearch同步解决方案--使用logstash (2)https://www.cnbl ...

  8. 分享一个完整的Mybatis分页解决方案

    分享一个完整的Mybatis分页解决方案 参考文章: (1)分享一个完整的Mybatis分页解决方案 (2)https://www.cnblogs.com/gev-1016/p/6606114.htm ...

  9. java miniui datagrid_miniui datagrid 的客户端分页解决方案

    官方的解决方案 官方在"在线示例"中给了一个简单的 client pagination 解决方案,代码就不贴了,这里说说它的基本思想和处理过程. 首先,是绑定一个 preload ...

最新文章

  1. [svn] linux 下svn服务器的搭建
  2. animate css3 应用的借鉴,一个同事写的JS
  3. 砥志研思SVM(四) 序列最小最优化算法(SMO)论文翻译
  4. jmeter java 关联_使用Jmeter进行数据关联和并发用户
  5. mysql 多版本删除_高性能MySQL(四):多版本并发控制
  6. python使用什么关键字声明匿名函数_Python通过关键字____创造匿名函数。
  7. 免费分享佳能ir c3320 c3330 c3325彩色复印机中文维修手册
  8. 全国智慧园区解决方案TOP50 | 附下载
  9. 域名检测工具图文教程
  10. 比特率(码率) = 采样率 (Sampling rate ) * 位深 (Bit depth)* 声道数目/Opus/AAC/mp3
  11. PHP处理微信昵称特殊符号过滤方法
  12. 1300款主流单机游戏下载 大型PC电脑游戏使命14尼尔5模拟合集
  13. android手机获取系统短信sqlite数据库并查看内容
  14. linux 网桥代码分析之网桥及网桥端口的添加与删除Ⅲ
  15. 超赞的实用前端社区精选?
  16. python调用函数示例_Python使用execjs运行JS函数示例,python,execJS,例子
  17. 为什么梦里常有视觉和听觉,却没有痛觉和嗅觉?
  18. Eclipse Java EE+Tomcat问题和Apache整合Tomcat
  19. 常见的网络攻击方式与防护
  20. 解决ubuntu安装后进入系统时黑屏,左上角闪动光标(ubuntu20亲测有效,应该是解释最详细的一篇了)

热门文章

  1. 关于计算机基础介绍的书,清华大学出版社-图书详情-《大学计算机基础》
  2. 为什么要学Python
  3. 摩托罗拉各地网站被关闭
  4. 《仙剑奇侠传3》流程攻略3
  5. 2014年辽宁省普通高等学校本科大学生计算机设计竞赛,我校学生在辽宁省普通高等学校本科大学生计算机设计大赛中喜获佳绩...
  6. IDEA旗舰版安装与概述
  7. MySQL中的sum函数遇到null的坑点
  8. ING创建有利于华尔街的区块链解决方案
  9. java double类型保留三位小数
  10. 通过编程做特别效果的个性“微信”二维码(思路)