ES如何查询索引的全量数据

简介: ES如何查询索引的全量数据

问题描述


查询全表数据也是日常工作中常见的一种查询场景。

在ES如果我们使用match_all查询索引的全量数据时,默认只会返回10条数据。

那么在ES如何查询索引的全量数据呢?


小实验


1、索引和数据准备

PUT book
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text", "analyzer": "ik_smart"
      },
      "price": {
        "type": "double"
      }
    }
  }
}
PUT /book/_bulk
{ "create": { } }
{"name": "java编程思想","price": 100}
{ "create": { } }
{"name": "ES实战","price": 120}
{ "create": { } }
{"name": "ES从入门到精通","price": 60}
{ "create": { } }
{"name": "微服务架构 设计模式","price": 160}
{ "create": { } }
{"name": "架构真经","price": 90}
{ "create": { } }
{"name": "spring boot实战","price": 50}
{ "create": { } }
{"name": "高性能mysql","price": 80}
{ "create": { } }
{"name": "java进阶1","price": 10}
{ "create": { } }
{"name": "java进阶2","price": 20}
{ "create": { } }
{"name": "java进阶3","price": 30}
{ "create": { } }
{"name": "java进阶4","price": 40}
{ "create": { } }
{"name": "java进阶5","price": 50}


2、match_all全匹配查询

GET book/_search


等同于

GET book/_search
{
  "query": {
    "match_all": {}
  }
}


发现只返回了10条记录。

这样因为_search查询默认采用的是分页查询,每页记录数size的默认值为10.


3、添加size参数

GET book/_search
{
  "query": {
    "match_all": {}
  },
  "size": 100
}

将size值设置为100,而我们只添加了13条记录,所以成功返回索引的全量记录。


4、size大于10000

GET book/_search
{
  "query": {
    "match_all": {}
  },
  "size": 20000
}


返回结果:

{
  "error" : {
    "root_cause" : [
      {
        "type" : "illegal_argument_exception",
        "reason" : "Result window is too large, from + size must be less than or equal to: [10000] but was [20000]. See the scroll api for a more efficient way to request large data sets. This limit can be set by changing the [index.max_result_window] index level setting."
      }
    ],


异常说明:

1、查询结果的窗口太大,from + size的结果必须小于或等于10000,而当前查询结果的窗口为20000。

2、可以采用scroll api更高效的请求大量数据集。

3、查询结果的窗口的限制可以通过参数index.max_result_window进行设置。


index.max_result_window


The maximum value of from + size for searches to this index. Defaults to 10000. Search requests take heap memory and time proportional to from + size and this limits that memory. See Scroll or Search After for a more efficient alternative to raising this.

说明:

参数index.max_result_window主要用来限制单次查询满足查询条件的结果窗口的大小,窗口大小由from + size共同决定。不能简单理解成查询返回给调用方的数据量。

这样做主要是为了限制内存的消耗。

请求大数据集推荐采用Scroll or Search After 。

比如:from为1000000,size为10,逻辑意义是从满足条件的数据中取1000000到(1000000 + 10)的记录。这时ES一定要先将(1000000 + 10)的记录(即result_window)加载到内存中,再进行分页取值的操作。

尽管最后我们只取了10条数据返回给客户端,但ES进程执行查询操作的过程中确需要将(1000000 + 10)的记录都加载到内存中,可想而知对内存的消耗有多大。

这也是ES中不推荐采用(from + size)方式进行深度分页的原因。


同理,from为0,size为1000000时,ES进程执行查询操作的过程中确需要将1000000 条记录都加载到内存中再返回给调用方,也会对ES内存造成很大压力。


1.参数设置

PUT book/_settings
{ 
  "index.max_result_window" :"5"
}


注意:

1、此方法是设置单索引,如果需要更改索引需要将book换成_all

2、即使换成_all,对于新增的索引,还是默认的10000


2.查看参数

查看所有索引中的index.max_result_window值:


GET _all/_settings/index.max_result_window


查看book索引的_settings配置:


GET book/_settings

查看_settings配置中的参数index.max_result_window的值:


GET book/_settings/index.max_result_window


Scroll api实践


改动index.max_result_window参数值的大小,只能解决一时的问题,当索引的数据量持续增长时,在查询全量数据时还是会出现问题。而且会增加ES服务器内存大结果集消耗完的风险。

最佳实践还是根据异常提示中的采用scroll api更高效的请求大量数据集。


1.DSL命令查询

1、查询命令中新增scroll=1m,说明采用游标查询,保持游标查询窗口一分钟。

2、这里由于测试数据量不够,所以size值设置为2。实际使用中为了减少游标查询的次数,可以将值适当增大,比如设置为1000。


GET /book/_search?scroll=1m 
{
    "query": { "match_all": {}},
    "size":  2
}


查询结果:

除了返回前2条记录,还返回了一个游标ID值_scroll_id。

{
  "_scroll_id" : "FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFm1ab2pDVEpiVGh5ZWthYnRQanB5YlEAAAAAABRP-xZHYWJiZzJGNFJYQ1RPS0dZb1VwejRR",
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  ……
}


采用游标id查询:

GET /_search/scroll
{
    "scroll": "1m", 
    "scroll_id" : "FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFm1ab2pDVEpiVGh5ZWthYnRQanB5YlEAAAAAABRP5BZHYWJiZzJGNFJYQ1RPS0dZb1VwejRR"
}


说明:

多次根据scroll_id游标查询,直到没有数据返回则结果查询。。

采用游标查询索引全量数据,更安全高效,限制了单次对内存的消耗。


2.java高级API实现

/**
 * 通过游标查询所有数据
 * @return
 */
public  <T> List<T> searchAllData(SearchRequest searchRequest, Class<T> clazz)  {
    List<T> tList = new ArrayList<>();
    final Scroll scroll = new Scroll(TimeValue.timeValueMinutes(1L));
    searchRequest.scroll(scroll);
    try{
        SearchResponse searchResponse = highLevelClient.search(searchRequest);
        String scrollId = searchResponse.getScrollId();
        log.info("ES查询DSL语句:\nGET  {}\n{}", String.format("/%s/_search", searchRequest.indices()[0]), searchRequest.source());
        //打印命中数量
        log.info("命中总数量:{}", searchResponse.getHits().getTotalHits());
        SearchHit[] searchHits = searchResponse.getHits().getHits();
        while (searchHits != null && searchHits.length > 0) {
            SearchHits hits = searchResponse.getHits();
            List<T> resultList = Arrays.stream(hits.getHits()).map(e -> {
                String jsonStr = e.getSourceAsString();
                return JSON.parseObject(jsonStr, clazz);
            }).collect(Collectors.toList());
            if(!CollectionUtils.isEmpty(resultList)){
                tList.addAll(resultList);
            }
            SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);
            scrollRequest.scroll(scroll);
            searchResponse = highLevelClient.searchScroll(scrollRequest);
            searchHits = searchResponse.getHits().getHits();
        }
        ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
        clearScrollRequest.addScrollId(scrollId);
        ClearScrollResponse clearScrollResponse = highLevelClient.clearScroll(clearScrollRequest);
        boolean succeeded = clearScrollResponse.isSucceeded();
    }catch (Exception e){
        log.error("执行EsService的searchAllData方法出现异常", e);
    }
    return  tList;
}


注意:

1、尽管通过采用Scroll API提高的查询全量数据的性能,减少了ES服务器的内存消耗。但是当返回结果集过大的时候,也会出现将调用方应用的内存撑爆的风险。

2、推荐的做法是:在调用searchAllData方法查询索引全量数据时,在方法内部添加返回记录条数的限制。避免出现返回几百万、甚至上千万的结果集,导致调用方程序由于内存吃完而宕机。


总结


1、本文主要介绍了如何通过Scroll API查询索引的全量数据。

2、介绍了index.max_result_window参数和相关配置方法。

3、尽管通过采用Scroll API查询索引全量数据提高了查询效率并减少了ES服务器的内存消耗。当同时要注意不要返回太大的结果集撑爆调用方应用的内存。

4、针对ES大结果集的查询,要同时考虑ES服务提供方和请求调用方的内存消耗。

目录
相关文章
|
存储 安全 Go
|
监控 网络安全 索引
Elasticsearch-通过Kibana查看索引数据
引言   当数据存储到Elasticsearch后,我们希望能方便的通过界面进行查询,有两个工具能够满足我们的需要,一个是Elasticsearch-head插件,另一个是Kibana,笔者认为两个工具各有千秋,大家可以自行体会,不过就安装步骤来说,Elasticsearch-head真心麻烦,本文主要介绍如何部署Kibana,并使用Kibana来查看Elasticsearch中的索引数据。
10715 1
|
11月前
|
存储 Kubernetes 调度
【赵渝强老师】K8s的有状态控制器StatefulSet
在Kubernetes中,StatefulSets用于部署有状态应用程序,提供持久存储和唯一标识符。与Deployment不同,StatefulSets确保Pod的标识符在重新调度后保持不变,适用于需要稳定网络标识符和持久存储的场景。本文介绍了StatefulSets的创建、扩容与缩容、更新与回滚等操作,并提供了具体示例和视频讲解。
386 0
|
Linux 数据处理
探索Linux下的readlink命令:解析符号链接的利器
`readlink`命令在Linux中用于揭示符号链接的指向,显示它们所链接的实际文件或目录的路径。它可以显示简洁的绝对路径(-f),处理循环链接(-e),或不加换行符输出(-n)。例如,查看`link.txt`指向:`readlink link.txt`;获取绝对路径:`readlink -f link.txt`。使用时要注意链接是否存在、权限问题和可能的循环链接。
|
存储 数据采集 JSON
彻底搞懂监控系统,使用Prometheus +Grafana搭建完整的应用监控系统
监控是运维系统的基础,我们衡量一个公司/部门的运维水平,看他们的监控系统就可以了。一个完善的监控系统可以提高应用的可用性和可靠性,在提供更优质服务的前提下,降低运维的投入和工作量,为用户带来更多的商业利益和客户体验。下面就带大家彻底搞懂监控系统,使用Prometheus +Grafana搭建完整的应用监控系统。
15793 1
彻底搞懂监控系统,使用Prometheus +Grafana搭建完整的应用监控系统
Java 实现 Elasticsearch 查询全部数据
【7月更文挑战第7天】Java 实现 Elasticsearch 查询全部数据
|
XML 缓存 Java
BeanUtils、BeanCopier、Dozer、Orika 哪家强?
背景 在分层的代码架构中,层与层之间的对象避免不了要做很多转换、赋值等操作,这些操作重复且繁琐,于是乎催生出很多工具来优雅,高效地完成这个操作,有BeanUtils、BeanCopier、Dozer、Orika等等,本文将讲述上面几个工具的使用、性能对比及原理分析。
1143 0
BeanUtils、BeanCopier、Dozer、Orika 哪家强?
|
SQL 数据可视化 前端开发
简单好用的ElasticSearch可视化工具:es-client和Head
使用 ElasticSearch(简称 es) 的过程中,经常有一些临时查询(如 排查问题、验证效果),一个趁手的可视化工具 可以提高工作效率。个人倾向于 免费(最好开源)、易于安装(如 浏览器插件),`es-client` 就是 比较简单好用的一个,尤其是 查询
11553 0
|
机器学习/深度学习 自然语言处理 算法
盘点当下大热的 7 大 Github 机器学习『创新』项目
盘点当下大热的 7 大 Github 机器学习『创新』项目
1094 1
盘点当下大热的 7 大 Github 机器学习『创新』项目
超实用!50+个Goland快捷键整理,开发必备!
超实用!50+个Goland快捷键整理,开发必备!
1221 0