如何优化Elasticsearch大文档查询?

devtools/2025/1/18 4:49:27/

记录一次业务复杂场景下DSL优化的过程

背景

B端商城业务有一个场景就是客户可见的产品列表是需要N多闸口及各种其它逻辑组合过滤的,各种闸口数据及产品数据都是存储在ES的(有的是独立索引,有的是作为产品属性存储在产品文档上)。

在实际使用的过程中,发现接口的毛刺比较严重,而这部分毛刺请求的耗时基本都是花费在从ES中查询产品索引的时候。

开启了一下ES慢DSL的日志

PUT /jiankunking_product_prod/_settings
{"index.search.slowlog.threshold.query.warn": "10s","index.search.slowlog.threshold.query.info": "5s","index.search.slowlog.threshold.fetch.warn": "2s","index.indexing.slowlog.source": true
}

经过分析慢DSL日志发现耗时长的部分都是在fetch阶段。

这里有个地方需要注意

[root@jiankunking-search-01: /data/es/logs]# ls -lrth |grep -v .gz
total 2.2G
-rw-r--r-- 1 es es    0 Sep 30  2019 jiankunking_audit.json
-rw-r--r-- 1 es es    0 Sep 30  2019 jiankunking_index_indexing_slowlog.log
-rw-r--r-- 1 es es    0 Sep 30  2019 jiankunking_index_indexing_slowlog.json
-rw-r--r-- 1 es es  53M Dec 31  2023 jiankunking_deprecation.log
-rw-r--r-- 1 es es 108M Dec 31  2023 jiankunking_deprecation.json
-rw-r--r-- 1 es es  55K Jul 30 10:43 jiankunking_server.json
-rw-r--r-- 1 es es  52K Jul 30 10:43 jiankunking.log
-rw-r--r-- 1 es es  63M Jul 30 11:32 jiankunking_index_search_slowlog.log //这里是完整的DSL
-rw-r--r-- 1 es es 8.9M Jul 30 11:32 jiankunking_index_search_slowlog.json //这里的DSL会被截断

分析

已知问题点

  • 产品文档身上有4个属性会很大
    • 属性A(nested属性):可以到几万个
    • 属性B(nested属性):可以到几百个
    • 属性C(string数组):可以到几万个
    • 属性D(大Object):可以到几万个
  • ES fetch阶段慢,其实就是从相关分片请求文档内容慢(这时候id其实已经知道了)

大体就是下图这么个流程

在这里插入图片描述

下面简化一下请求的DSL,看下移除所有复杂的查询逻辑后,直接按照_id来terms查询效果如何?

DSL

GET /jiankunking_product_prod/_search
{"size": 10000,"_source": {"includes": ["code","group","groupBrand"],"excludes": []},"query": {"terms": {"_id": ["具体文档_id"]}}
}

不同文档大小查询时延

当前分析的DSL原本命中的文档数就是8306
下表中的文档数是直接在terms中查询的id数

文档数文档大小(Bytes)文档大小(KB)响应时延(ms)备注
8306无限制5908
5908<500,000<4882327剔除大的
6929<200,000<1951507剔除大的
5731<100,000<97599剔除大的
4925<50,000<49356剔除大的
4236< 30,000<29214剔除大的(注意这里,当文档大小比较小的时候,4000+的文档查询其实是比较快的)
--------------------
4070>30,000>296261剔除小的
3381>50,000>496050剔除小的
2572>100,000>975388剔除小的
1377>200,000>1954973剔除小的
669>500,000>4883984剔除小的
381>1,000,000>9763169剔除小的
217>2,000,000>19522391剔除小的
88>3,000,000>29281244剔除小的

分析

  • 文档数与文档大小查询分析
    • 剔除大文档之后,查询数据效率提升明显
    • 剔除小文档之后,查询数据效率提升缓慢

到这里我们可以发现当文档size比较小的时候几千个文档的查询RT是很短的,但当随着请求命中的大文档越来越多,RT极速增加。

回看下我们的产品索引数据,可以发现大字段其实都是用来过滤的,并不是返回给页面需要的;那我们是不是可以:将索引拆分为两个或者ES只用来作为二级索引返回ids,然后去MySQL中查询具体的产品信息?

在这里插入图片描述

那我们将慢DSL中中查询的字段修改为只返回_id

POST /jiankunking_product_prod/_search
{"size": 10000,"_source": false,"query": {"terms": {"_id": [""],"boost": 1}}
}

这时候查询耗时只需要203ms,这种情况下还能不能再优化了呢?答案是可以的

索引中文档_id就是产品的code

POST /jiankunking_product_prod/_search
{"size": 10000,"_source": false,"stored_fields": "_none_","docvalue_fields": ["code"],"query": {"terms": {"code": [""],"boost": 1}}
}

这时候查询只需要76ms

结论

到这里这次优化基本结束了,最终的方案就是

  • 通过从jiankunking_product_prod索引中通过列存获取ids
  • 到MySQL或者新的产品主数据索引中查询具体的产品数据

思考

为啥不直接从jiankunking_product_prod索引中通过列存获取前端需要的数据呢?

因为真实业务场景中需要返回的产品属性虽然每个不大,但总数有20多个,列存在返回字段数多且命中文档大小都不大的场景下,相比原逻辑直接从_source中取会略有下降。

更多原理性解释,可以看下这里:https://jiankunking.com/elasticsearch-source-doc-values-and-store-performance.html

ES适合的场景都有哪些?

目前我这边遇到的场景主要有:

  • 检索加速
    • 数据查询的主存储
      • 当文档大小不是太大的时候,索引检索完直接返回需要的数据
    • 二级索引
      • 针对的就是本文这种场景
  • 日志
    • 应用/容器日志
      • 这里追求的更多是高吞吐的写入
    • 业务日志

具体索引中数据大小是什么情况呢?

分位数大小 (KB)
0.051.16
0.101.39
0.151.61
0.201.69
0.251.77
0.302.14
0.352.97
0.403.50
0.453.90
0.504.24
0.554.92
0.605.73
0.657.15
0.708.82
0.7513.13
0.8032.32
0.8557.52
0.90114.39
0.95262.47
0.99989.75

在这里插入图片描述

拓展阅读

  • https://jiankunking.com/elasticsearch-source-doc-values-and-store-performance.html
  • https://jiankunking.com/elasticsearch-scroll-and-search-after.html
  • https://luis-sena.medium.com/stop-using-the-id-field-in-elasticsearch-6fb650d1fbae
  • https://jiankunking.com/elasticsearch-avoid-the-fetch-phase-when-retrieving-only-id.html
  • https://jiankunking.com/elasticsearch-query-secret.html
  • https://www.elastic.co/guide/en/elasticsearch/reference/current/general-recommendations.html

http://www.ppmy.cn/devtools/151471.html

相关文章

Linux安装docker,安装配置xrdp远程桌面

Linux安装docker&#xff0c;安装配置xrdp远程桌面。 1、卸载旧版本docker 卸载旧版本docker命令 yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine现在就是没有旧版本的d…

LeetCode - #182 Swift 实现找出重复的电子邮件

网罗开发 &#xff08;小红书、快手、视频号同名&#xff09; 大家好&#xff0c;我是 展菲&#xff0c;目前在上市企业从事人工智能项目研发管理工作&#xff0c;平时热衷于分享各种编程领域的软硬技能知识以及前沿技术&#xff0c;包括iOS、前端、Harmony OS、Java、Python等…

GoLang教程004:流程控制和if语句介绍

文章目录 3、流程控制3.1 流程控制的作用3.2 控制语句的分类3.3 if分支3.3.1 单分支3.3.2 多分支3.3.3 双分支 3、流程控制 3.1 流程控制的作用 流程控制的作用&#xff1a;流程控制语句是用来控制程序中各语句执行顺序的语句&#xff0c;可以把语句组合成能完成一定功能的小…

HBase深度历险

作者&#xff1a;京东物流 于建飞 简介 HBase 的全称是 Hadoop Database&#xff0c;是一个分布式的&#xff0c;可扩展&#xff0c;面向列簇的数据库&#xff0c;是一个通过大量廉价的机器解决海量数据的高速存储和读取的分布式数据库解决方案。本文会像剥洋葱一样&#xff0c…

LLama 架构一览

LLama 架构一览 预训练Transformer 架构RMSNorm 归一化&#xff1a;为什么大模型llama选RMSNorm&#xff0c;而不是其他&#xff1f;SwiGLU 激活函数&#xff1a;为什么大模型llama选SwiGLU激活函数&#xff0c;而不是其他&#xff1f;RoPE 旋转位置编码&#xff1a;为什么大模…

Hive迁移,小表(10G以下的),分区快速修复批量脚本

hive迁移要迁移元数据&#xff08;mysql&#xff09;&#xff0c;要迁移实际数据hdfs&#xff0c;迁移完后如果有分区&#xff0c;通常是不能访问的。 这里就要修复分区了&#xff0c;如果是大表&#xff0c;几十T这种&#xff0c;迁移可能花了24小时&#xff0c;那么你修复分…

后端程序打成 JAR 包的详细步骤及解释

在后端开发中&#xff0c;将程序打成 JAR 包是一个常见的操作&#xff0c;它可以方便程序的部署和分发。本文将详细介绍几种将后端程序打成 JAR 包的方法及其相应的解释&#xff0c;帮助你更好地完成项目的打包工作。 一、使用 Maven 构建工具&#xff08;适用于 Maven 项目&a…

Linux初识:【shell命令以及运行原理】【Linux权限的概念与权限管理】

目录 一.shell命令以及运行原理 二.Linux权限的概念与权限管理 2.1Linux权限的概念 sudo普通用户提权 2.2Linux权限管理 2.2.1文件访问者的分类&#xff08;人&#xff09; 2.2.2文件类型和访问权限&#xff08;事物属性&#xff09; 2.2.3文件权限值的表示方法 字符…