elasticsearch 查询超10000的解决方案

前言

默认情况下,Elasticsearch集群中每个分片的搜索结果数量限制为10000。这是为了避免潜在的性能问题。

但是我们 在实际工作过程中时常会遇到 需要深度分页,以及查询批量数据更新的情况

问题:当请求form + size >10000 时,请求直接报错

在这里插入图片描述

1:修改max_result_window 参数(不推荐)

在此方案中,我们建议仅限于测试用,生产禁用,毕竟当数据量大的时候,过大的数据量可能导致es的内存溢出,直接崩掉,一年绩效白干。

PUT wkl_test/_settings
{"index":{"max_result_window":2147483647}
}

查看索引的 settings
在这里插入图片描述
重新查数据:

在这里插入图片描述

2:使用游标 scroll API

使用scroll API:scroll API可以帮助我们在不加载所有数据的情况下获取所有结果。它会在后台执行查询以获取滚动ID,并将其用于进行后续查询。这样就可以一次性获取所有结果,而不必担心限制

ES语句查询

在游标方案中,我们只需要在第一次拿到游标id,之后通过游标就能唯一确定查询,在这个查询中通过我们指定的 size 移动游标,具体操作看看下面实操。

  • 游标查询,设置游标有效时间,有效时间内,游标都可以使用,过期就不行了
GET wkl_test/_search?scroll=5m
{"query": {"match_all": {}},"sort": [{"seq": {"order": "asc"}}],"size": 200
}
  • 上面操作中通过游标的结果返回
    在这里插入图片描述
  • 之后将_scroll_id 复制到窗口,就可以不端通过这个_scroll_id 进行之前设置的页数不断翻页
    以此类推,后面每次滚屏都把前一个的scroll_id复制过来。注意到,后续请求时没有了index信息,size信息等,这些都在初始请求中,只需要使用scroll_id和scroll两个参数即可。
    在这里插入图片描述
    注意,此时游标移动了,所以我们可以通过游标的方式不断后移,直到移动到我们想要的 from+size 范围内。再次点击
    在这里插入图片描述

java实现


@Testpublic void testScroll(){RestHighLevelClient restHighLevelClient ;BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();boolQueryBuilder.mustNot(QueryBuilders.existsQuery("seq"));try {//滚动查询的Scroll,设置请求滚动时间窗口时间Scroll scroll = new Scroll(TimeValue.timeValueMillis(180000));SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();//加入query语句sourceBuilder.query(boolQueryBuilder);//每次滚动的长度sourceBuilder.size(SIZE);//加入排序字段sourceBuilder.sort("id", SortOrder.DESC);//构建searchRequest//加入scroll和构造器SearchRequest searchRequest = new SearchRequest().indices("wkl_test").source(sourceBuilder).scroll(scroll);//存储scroll的listList<String> scrollIdList = new ArrayList<>();//执行首次检索SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);//首次检索返回scrollId,用于下一次的滚动查询String scrollId = searchResponse.getScrollId();//拿到hits结果SearchHit[] hits = searchResponse.getHits().getHits();long value = searchResponse.getHits().getTotalHits().value;//保存返回结果List大小Long resultSize = 0L;scrollIdList.add(scrollId);try {//滚动查询将SearchHit封装到result中while (ArrayUtils.isNotEmpty(hits) && hits.length > 0) {BulkRequest bulkRequest = new BulkRequest();JSONArray esArray = new JSONArray();for (SearchHit hit : hits) {String sourceAsString = hit.getSourceAsString();String index = hit.getIndex();JSONObject jsonObject = JSONObject.parseObject(sourceAsString);String seq = jsonObject.getString("seq");if(StringUtils.isBlank(seq) ){esArray.add(jsonObject);String uuid = jsonObject.getString("id");jsonObject.put("is_del",1);bulkRequest.add(new UpdateRequest(index, uuid).doc(jsonObject));}}resultSize = resultSize+hits.length;//发送请求//实时更新bulkRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);System.out.println(bulk.getTook()+"-------"+bulk.getItems().length);//说明滚动完了,返回结果即可if (resultSize > 20000) {break;}//继续滚动,根据上一个游标,得到这次开始查询位置SearchScrollRequest searchScrollRequest = new SearchScrollRequest(scrollId);searchScrollRequest.scroll(scroll);//得到结果SearchResponse searchScrollResponse = restHighLevelClient.scroll(searchScrollRequest, RequestOptions.DEFAULT);//定位游标scrollId = searchScrollResponse.getScrollId();hits = searchScrollResponse.getHits().getHits();scrollIdList.add(scrollId);}System.out.println("----彻底结束了-----");} finally {//清理scroll,释放资源ClearScrollRequest clearScrollRequest = new ClearScrollRequest();clearScrollRequest.setScrollIds(scrollIdList);restHighLevelClient.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);}} catch (Exception e) {throw new RuntimeException(e);}}

scroll API 的优缺点和总结

优缺点:

  • scroll查询的相应数据是非实时的,如果遍历过程中插入新的数据,是查询不到的。并且保留上下文需要足够的堆内存空间。
  • 相比于 from/size 和 search_after 返回一页数据,Scroll API 可用于从单个搜索请求中检索大量结果。但是 scroll 滚动遍历查询是非实时的,数据量大的时候,响应时间可能会比较长

适用场景

  • 全量或数据量很大时遍历结果数据,而非分页查询。
  • scroll方案基于快照,不能用在高实时性的场景下,建议用在类似数据导出场景下使用

3: search_after + PIT 深度查询

  • Search_after是 ES 5 新引入的一种分页查询机制,其原理几乎就是和scroll一样,因此代码也几乎是一样的。
  • 官方文档说明不再建议使用scroll滚动分页和from size分页,建议使用search_after
  • search_after 分页的方式和 scroll 搜索有一些显著的区别,首先它是根据上一页的最后一条数据来确定下一页的位置,同时在分页请求的过程中,如果有索引数据的增删改查,这些变更也会实时的反映到游标上。

不带PIT

ES语句实现

检索第一页的查询如下所示:

GET wkl_test/_search
{"query": {"match_all": {}},"sort": [{"seq": {"order": "asc"}}],"size": 200
}

上述请求的结果包括每个文档的 sort 值数组。
在这里插入图片描述

这些 sort 值可以与 search_after 参数一起使用,以开始返回在这个结果列表之后的任何文档。例如,我们可以使用上一个文档的 sort 值并将其传递给 search_after 以检索下一页结果:

在这里插入图片描述

Java 实现

@Testpublic void testSearchAfter() throws IOException {RestHighLevelClient restHighLevelClient = es7UtilApi.getRestHighLevelClient();MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(matchAllQueryBuilder);searchSourceBuilder.from(0);searchSourceBuilder.size(200);searchSourceBuilder.sort("seq", SortOrder.ASC);searchSourceBuilder.trackTotalHits(true);SearchRequest searchRequest = new SearchRequest().indices("wkl_test").source(searchSourceBuilder);SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);SearchHits hits = searchResponse.getHits();long value = hits.getTotalHits().value;System.out.println("查询到记录数=" + value);List<JSONObject> list = new ArrayList<>();SearchHit[] searchHists = hits.getHits();Object[] sortValues = searchHists[searchHists.length - 1].getSortValues();if (searchHists.length > 0) {for (SearchHit hit : searchHists) {String sourceAsString = hit.getSourceAsString();JSONObject jsonObject = JSON.parseObject(sourceAsString);jsonObject.put("_id", hit.getId());list.add(jsonObject);}}//往后的每次请求都携带上一次的sort_id进行访问。while (ArrayUtils.isNotEmpty(searchHists) && searchHists.length > 0){searchSourceBuilder.searchAfter(sortValues);searchRequest.source(searchSourceBuilder);SearchResponse searchResponseAfter = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);hits = searchResponseAfter.getHits();searchHists = hits.getHits();sortValues = searchHists[searchHists.length - 1].getSortValues();if (searchHists.length > 0) {for (SearchHit hit : searchHists) {String sourceAsString = hit.getSourceAsString();JSONObject jsonObject = JSON.parseObject(sourceAsString);jsonObject.put("_id", hit.getId());list.add(jsonObject);}}if(list.size()>20000){break;}System.out.println("-----彻底结束了-------");}}

问题

「优点:」

  • 无状态查询,可以防止在查询过程中,数据的变更无法及时反映到查询中。

  • 不需要维护scroll_id,不需要维护快照,因此可以避免消耗大量的资源。

「缺点:」

  • 由于无状态查询,因此在查询期间的变更可能会导致跨页面的不一值。

  • 排序顺序可能会在执行期间发生变化,具体取决于索引的更新和删除。

  • 至少需要制定一个唯一的不重复字段来排序。

  • 它不适用于大幅度跳页查询,或者全量导出,对第N页的跳转查询相当于对es不断重复的执行N次search after,而全量导出则是在短时间内执行大量的重复查询。

带PIT

关于PIT

  • 在7.*版本中,ES官方不再推荐使用Scroll方法来进行深分页,而是推荐使用带PIT的search_after来进行查询;

  • 从7.*版本开始,您可以使用SEARCH_AFTER参数通过上一页中的一组排序值检索下一页命中。

  • 使用SEARCH_AFTER需要多个具有相同查询和排序值的搜索请求。

  • 如果这些请求之间发生刷新,则结果的顺序可能会更改,从而导致页面之间的结果不一致。
    为防止出现这种情况,您可以创建一个时间点(PIT)来在搜索过程中保留当前索引状态。

ES语句实现

1:生成pit
#keep_alive必须要加上,它表示这个pit能存在多久,这里设置的是1分钟
POST wkl_test/_pit?keep_alive=1m

在这里插入图片描述

2:在搜索请求中指定PIT:

在每个搜索请求中添加 keep_alive 参数来延长 PIT 的保留期,相当于是重置了一下时间


GET _search
{"query": {"match_all": {}},"pit":{"id":"t_yxAwEId2tsX3Rlc3QWU0hzbEJkYWNTVEd0ZGRoN0xsQVVNdwAWUGQtaXJpT0xTa2VUN0RGLXZfTlBvZwAAAAAACHG1fxY1UWNKX1RHOFMybXBaV20zbWx3enp3ARZTSHNsQmRhY1NUR3RkZGg3TGxBVU13AAA=","keep_alive":"5m"},"sort": [{"seq": {"order": "asc"}}],"size": 200
}

在这里插入图片描述

3:删除PIT
DELETE _pit
{"id":"t_yxAwEId2tsX3Rlc3QWU0hzbEJkYWNTVEd0ZGRoN0xsQVVNdwAWUGQtaXJpT0xTa2VUN0RGLXZfTlBvZwAAAAAACHG1fxY1UWNKX1RHOFMybXBaV20zbWx3enp3ARZTSHNsQmRhY1NUR3RkZGg3TGxBVU13AAA="
}

在这里插入图片描述

总结

  • 如果数据量小(from+size在10000条内),或者只关注结果集的TopN数据,可以使用from/size 分页,简单粗暴

  • 数据量大,深度翻页,后台批处理任务(数据迁移)之类的任务,使用 scroll 方式

  • 数据量大,深度翻页,用户实时、高并发查询需求,使用 search after 方式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/377177.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【从0到1进阶Redis】主从复制 — 主从机宕机测试

上一篇&#xff1a;【从0到1进阶Redis】主从复制 测试&#xff1a;主机断开连接&#xff0c;从机依旧连接到主机的&#xff0c;但是没有写操作&#xff0c;这个时候&#xff0c;主机如果回来了&#xff0c;从机依旧可以直接获取到主机写的信息。 如果是使用命令行&#xff0c;来…

制作显卡版docker并配置TensorTR环境

感谢阅读 相关概念docker准备下载一个自己电脑cuda匹配的docker镜像拉取以及启动镜像安装cudaTensorRT部署教程 相关概念 TensorRT是可以在NVIDIA各种GPU硬件平台下运行的一个模型推理框架&#xff0c;支持C和Python推理。即我们利用Pytorch&#xff0c;Tensorflow或者其它框架…

电影购票小程序论文(设计)开题报告

一、课题的背景和意义 随着互联网技术的不断发展&#xff0c;人们对于购票的需求也越来越高。传统的购票方式存在着排队时间长、购票流程繁琐等问题&#xff0c;而网上购票则能够有效地解决这些问题。电影购票小程序是网上购票的一种新型应用&#xff0c;它能够让用户随时随地…

Vulnhub靶场DC-3-2练习

目录 0x00 准备0x01 主机信息收集0x02 站点信息收集0x03 漏洞查找与利用1. joomla漏洞查找2. SQL注入漏洞3. 破解hash4. 上传一句话木马5. 蚁剑连接shell6. 反弹shell7. 提权 0x04 总结 0x00 准备 下载链接&#xff1a;https://download.vulnhub.com/dc/DC-3-2.zip 介绍&#…

自适应大气简约健康实木地板生产企业网站模板源码 带完整的安装代码包以及搭建教程

系统概述 在当今数字化时代&#xff0c;企业的在线形象直接关联到其市场竞争力。对于专注于生产高品质、健康环保实木地板的企业而言&#xff0c;一个专业、大气且能完美展示产品特色的官方网站尤为重要。为了满足这一需求&#xff0c;我们精心打造了一款“自适应大气简约健康…

Chromium CI/CD 之Jenkins实用指南2024-如何创建新节点(三)

1. 前言 在前一篇《Jenkins实用指南2024-系统基本配置&#xff08;二&#xff09;》中&#xff0c;我们详细介绍了如何对Jenkins进行基本配置&#xff0c;包括系统设置、安全配置、插件管理以及创建第一个Job。通过这些配置&#xff0c;您的Jenkins环境已经具备了基本的功能和…

Qt基础 | Qt Creator的基本介绍与使用 | 在Visual Studio中创建Qt项目

文章目录 一、Qt Creator的基本介绍与使用1.新建一个项目2.项目的文件组成3.项目文件介绍3.1 项目管理文件3.2 界面文件3.3 主函数文件3.4 窗体相关的文件 4.项目的编译、调试与运行 二、在Visual Studio中创建Qt项目 Qt C开发环境的安装&#xff0c;请参考https://liujie.blog…

HTTP背后的故事:理解现代网络如何工作的关键(一)

一.HTTP是什么 概念 &#xff1a; 1.HTTP ( 全称为 " 超文本传输协议 ") 是一种应用非常广泛的 应用层协议。 2.HTTP 诞生与1991年. 目前已经发展为最主流使用的一种应用层协议. 3.HTTP 往往是基于传输层的 TCP 协议实现的 . (HTTP1.0, HTTP1.1, HTTP2.0 均为 T…

使用 HttpServlet 接收网页的 post/get 请求

前期工作&#xff1a;部署好 idea 和 一个 web 项目 idea(2021),tomcat(9) ->创建一个空的项目 -> 新建一个空的模块 -> 右键单击模块 选择 Add..Fra.. Sup.. -> 勾选Web App...后点击OK -> 点击 file - Project Struc... -> 选择刚刚的模块 -> 点…

Centos 使用nfs配置共享目录使docker集群所有容器日志统一主机访问

Centos 使用nfs配置共享目录&#xff0c;使docker集群所有容器日志统一存放在主机一个共享目录下&#xff0c;供开发人员访问查看 准备两台或以上Centos服务器 192.168.0.1 nfs服务器 192.168.0.2 nfs客户端 以root用户登录192.168.0.1服务器&#xff0c;执行以下操作 注意先…

vue里完成一个简单的评论功能,博客必备

效果展示 步骤 1-表单收集输入信息 <div class"commentorInfo"><div style"position: relative"><i class"fa fa-user"style"position:absolute;top: 50%;transform: translateY(-50%)translateX(8px)"></i>…

大语言模型诞生过程剖析

过程图如下 &#x1f4da; 第一步&#xff1a;海量文本的无监督学习 得到基座大模型&#x1f389; &#x1f50d; 原料&#xff1a;首先&#xff0c;我们需要海量的文本数据&#xff0c;这些数据可以来自互联网上的各种语料库&#xff0c;包括书籍、新闻、科学论文、社交媒体帖…

Java 客户端操作 Redis 命令(端口号映射方法,命令演示,注意事项)

文章目录 开放端口号问题引入依赖验证连接通用命令使用set 和 get 命令的使用exists 和 del 命令的使用keys 命令的使用expire 和 ttl 命令type 命令的使用 String 类型命令使用mset 和 mget 命令getrange 和 setrange 命令append 命令incr 和 decr 命令 list 类型命令使用lpus…

[极客大挑战 2019]FinalSQL

用异或来注入 注入语句为1是error&#xff0c;为0时为check ?id1^(length(database())5)写盲注脚本 这道题有点坑&#xff0c;首先是表名有两个&#xff0c;一个特别想flag&#xff0c;还有一个就是需要多线程&#xff0c;但是由于是python的&#xff0c;也没有那么必要等一…

CinemachineBrain的属性简介

CinemachineBrain的属性简介 CinemachineBrain是Unity Cinemachine的核心组件&#xff0c;它和Camera组件挂载在一起&#xff0c;监控场景中所有的virtual camera。CinemachineBrain在inspector中暴露的属性如下&#xff1a; Live Camera和Live Blend分别表示当前active的virtu…

51单片机嵌入式开发:9、 STC89C52RC 操作LCD1602技巧

STC89C52RC 操作LCD1602技巧 1 代码工程2 LCD1602使用2.1 LCD1602字库2.2 巧妙使用sprintf2.3 光标显示2.4 写固定长度的字符2.5 所以引入固定长度写入方式&#xff1a; 3 LCD1602操作总结 1 代码工程 承接上文&#xff0c;在原有工程基础上&#xff0c;新建关于lcd1602的c和h…

k8s NetworkPolicy

Namespace 隔离 默认情况下&#xff0c;所有 Pod 之间是全通的。每个 Namespace 可以配置独立的网络策略&#xff0c;来 隔离 Pod 之间的流量。 v1.7 版本通过创建匹配所有 Pod 的 Network Policy 来作为默认的网络策略 默认拒绝所有 Pod 之间 Ingress 通信 apiVersion: …

【线程安全】关于死锁问题

文章目录 死锁的基本概念死锁的四个必要条件避免死锁避免死锁的算法死锁检测算法 死锁的基本概念 死锁是指在一组进程中的各个进程均占有不会释放的资源&#xff0c;但因互相申请被其他进程所站用不会释放的资源而处于的一种永久等待状态。当然&#xff0c;线程之间同样也有死…

OpenCV中使用Canny算法在图像中查找边缘

操作系统&#xff1a;ubuntu22.04OpenCV版本&#xff1a;OpenCV4.9IDE:Visual Studio Code编程语言&#xff1a;C11 算法描述 Canny算法是一种广泛应用于计算机视觉和图像处理领域中的边缘检测算法。它由John F. Canny在1986年提出&#xff0c;旨在寻找给定噪声条件下的最佳边…

部署大语言模型并对话

随着人工智能技术的飞速发展&#xff0c;大语言模型&#xff08;Large Language Models, LLMs&#xff09;因其强大的语言理解和生成能力而备受关注。OpenWebUI &#xff0c;原名 Ollama WebUI &#xff0c;是一款专为大语言模型&#xff08;LLM&#xff09;设计的先进 Web 交互…