当前位置: 首页 > news >正文

如何优化Elasticsearch大文档查询?

记录一次业务复杂场景下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.lryc.cn/news/523687.html

相关文章:

  • Kotlin Bytedeco OpenCV 图像图像54 透视变换 图像矫正
  • Linux中DataX使用第一期
  • [Qt]事件-鼠标事件、键盘事件、定时器事件、窗口改变事件、事件分发器与事件过滤器
  • 关于机器学习的一份总结
  • 推荐一个开源的轻量级任务调度器!TaskScheduler!
  • 【18】Word:明华中学-儿童医保❗
  • 如何用selenium来链接并打开比特浏览器进行自动化操作(1)
  • 基于springboot+thymeleaf+Redis仿知乎网站问答项目源码
  • 读spring官方文档的一些关键知识点介绍
  • 2024年AI与大数据技术趋势洞察:跨领域创新与社会变革
  • ThinkPhp项目解决静态资源请求的跨域问题的解决思路
  • mybatis的多对一、一对多的用法
  • 消息队列实战指南:三大MQ 与 Kafka 适用场景全解析
  • 前端发送Ajax请求的技术Axios
  • 第17章:Python TDD回顾与总结货币类开发
  • opencv_KDTree_搜索介绍及示例
  • Windows 上安装 MongoDB 的 zip 包
  • 先进制造aps专题二十七 西门子opcenter aps架构分析
  • 【数据分享】1929-2024年全球站点的逐年平均气温数据(Shp\Excel\无需转发)
  • 机器学习——什么是代价函数?
  • docker 部署 MantisBT
  • 02内存结构篇(D1_自动内存管理)
  • Centos 8 交换空间管理
  • “深入浅出”系列之数通篇:(5)TCP的三次握手和四次挥手
  • 接口测试及接口测试常用的工具
  • 使用rpc绕过咸鱼sign校验
  • NPC与AI深度融合结合雷鸟X3Pro AR智能眼镜:引领游戏行业沉浸式与增强现实新纪元的畅想
  • 【物联网】ARM核介绍
  • Android系统定制APP开发_如何对应用进行系统签名
  • Tesla Free-Fall Attack:特斯拉汽车网络安全事件纪要