前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >修改ES返回字段方式提升性能

修改ES返回字段方式提升性能

原创
作者头像
叫我家宝
修改2022-04-13 18:09:30
2.9K0
修改2022-04-13 18:09:30
举报
文章被收录于专栏:搜索引擎技术研讨

背景

最近我们在公司内尝试用ES替换老旧的Solr, 在性能对比测试的环节, 发现ES竟然比Solr慢了非常多, 响应时间是Solr的两三倍, 然后开始各种排查, 最后发现ES的响应时间竟然随着request.size的增加呈线性增加, 这说明大部分时间都耗在了获取返回字段上面. 而我们目前在召回时并未获取很多字段, 只获取了UID(我们自己定义的一个基于docvalues列存的字段)和score. 按照ES的query-then-fetch召回模式来说, score应该是在query阶段生成, 在fetch阶段应该只需要读取UID, 而UID是基于列存的, 没有理由会随着request.size的增加而线性增长.

因此有一个初步的猜想, 就是执行fetch阶段时可能不符合我们的预期.

阅读官方文档

让我们来看看官方文档里提供的获取字段的几种方式.

  1. _source: 需要把doc的整个原始文档解开, 然后取需要的字段.
  2. fields: 类似于_source, 只是取出field后会按照mapping来解析格式化.
  3. docvalue_fields: 可以用来取支持docvalue的字段(不需要显示指定, 支持的数据类型默认会存docvalue), 避免读取整个_source.
    1. 支持keyword, 数字, date, 不支持text.
    2. 不支持嵌套对象.
  4. stored_fields: 可以用来取支持store的字段(需要显示指定store=true), 一般不推荐使用, 比起这个方式更推荐用_source.
    1. 只支持显示指定store=true的字段, 很不方便.
    2. 不支持嵌套对象
    3. 可以完全禁用storefields: `"stored_fields": "_none"`, 禁用的话_source也不能访问了, 因为_source本质也是一个store field.
  5. script_fields: 通过script获取自定义字段. 可以通过读取_source和docvalue两种方式获取字段.
    1. 通过_source获取: params['_source']['my_field'].
    2. 通过docvalue获取: doc['my_field'].value.

测试不同的获取字段方式

测试: 在返回4000条文档的UID,score属性的测试中, 配置不同的返回字段参数的响应时间如下:

代码语言:txt
复制
"_source":{
	"include":["UID"]
},

120ms +

代码语言:txt
复制
"fields":["UID"],
"_source":false,

110ms +

代码语言:txt
复制
"docvalue_fields":["UID"],
"_source":false,

110ms +

代码语言:txt
复制
"docvalue_fields":["UID"],
"stored_fields": "_none_",
"_source":false,

20ms +

结论

很显然, 使用"stored_fields": "_none_"的响应时间相比简单的使用_source要减少100ms, 性能要提升5倍多.

疑问

根据官方文档的说法, "stored_fields": "_none_" 是完全禁掉了包括_source在内的store字段.

目前还有两个疑问:

  1. 为什么当设置了"_source":false的时候性能无明显提升呢? 难道即便这样设置, ES依然会从硬盘上读取_source吗? 这听起来不是很合理啊.
  2. 因为是query_then_fetch的模式, 这样在fetch阶段, 每个shard需要获取字段的文档数应该接近size/shard_size, 假设有20个shard, 那么平均每个shard只需要获取4000/20=200个文档, 并且多个shard是并发执行的, 这个过程会增加100ms那么多时间吗?

阅读源码解释疑问

为什么当设置了"_source":false的时候性能无明显提升呢? 难道即便这样设置, ES依然会从硬盘上读取_source吗? 这听起来不是很合理啊.

通过阅读源码知道, 当设置了"_source":false的时候, ES确实没有读取_source, 但是会默认读取两个字段: _id和_routing, 这两个字段是ES内置的, 正常情况下无法查看其字段类型, 但是我们可以通过Luke工具查看:

luke.png
luke.png

通过查看我们得知, 这两个字段是仅索引的, 既没有存docvalues也没有存stored. 那么ES是如何读取的呢? 答案是通过fielddata cache. 第一次试图召回_id字段的时候, ES会根据其倒排索引结构, 在堆内存中构建fielddata cache并缓存. fielddata cache就是把倒排索引结构反转为正排索引, 这样一来就相当于在内存中构建了_id字段的列存. 缺陷是第一次请求因为要构建fielddata cache会慢.

因此仅仅设置"_source":false是不够的, 如果不需要召回_id和_routing的话, 应该设置"stored_fields": "_none_". 而且官方文档其实也指出了这一点:

doc.png
doc.png

因为是query_then_fetch的模式, 这样在fetch阶段, 每个shard需要获取字段的文档数应该接近size/shard_size, 假设有20个shard, 那么平均每个shard只需要获取4000/20=200个文档, 并且多个shard是并发执行的, 这个过程会增加100ms那么多时间吗?

这个问题暂时还没搞清楚.

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 阅读官方文档
  • 测试不同的获取字段方式
  • 结论
  • 疑问
  • 阅读源码解释疑问
相关产品与服务
Elasticsearch Service
腾讯云 Elasticsearch Service(ES)是云端全托管海量数据检索分析服务,拥有高性能自研内核,集成X-Pack。ES 支持通过自治索引、存算分离、集群巡检等特性轻松管理集群,也支持免运维、自动弹性、按需使用的 Serverless 模式。使用 ES 您可以高效构建信息检索、日志分析、运维监控等服务,它独特的向量检索还可助您构建基于语义、图像的AI深度应用。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档