谈谈 ES 6.8 到 7.10 的功能变迁(3)- 查询方法篇
INFINI Labs 小助手 发表了文章 • 0 个评论 • 1601 次浏览 • 2025-04-28 12:17
上一篇咱们了解了 ES 7.10 相较于 ES 6.8 新增的字段类型,这一篇我们继续了解新增的查询方法。
Interval 间隔查询:
功能介绍
Interval 查询,词项间距查询,可以根据匹配词项的顺序、间距和接近度对文档进行排名。主要解决的查询场景“创建一个多搜索词匹配的查询,同时保留搜索词的顺序”,比 match phrase 更加符合需求场景,查询方法使用比 span 查询更简单。ES 后续版本想用 interval 查询逐步替代 span 查询。
注意事项
规则组合
- 可以使用 prefix、wildcard、fuzzy 等规则
- 通过设置 max_gaps 和 ordered 参数,可以控制词项间的最大间隙和顺序要求。
性能考虑
- 间隔查询比简单的词项匹配更消耗资源
- 嵌套规则越多,性能开销越大
- 建议合理使用 maxGaps 参数限制间距
使用限制
- 只能用于 text 字段
- 不支持跨字段查询
- 不支持对数值类型字段使用
Distance feature 查询
功能说明
时间/地理距离特性查询,该查询用于查找更接近被查询日期和地理位置的结果。 日期和位置分别是声明为 date 和 geo_point 数据类型的字段。返回结果的字段值不需要完全等于被查询值,而是按照给定日期或给定位置的进度算分,越是接近被查询值,在相关性得分中被评为更高。
字段类型要求
- 日期字段必须是 date 类型,地理位置字段必须是 geo_point 类型
- 不支持其他类型的距离计算
评分机制
- 距离越近,得分越高
- 使用 boost 参数调整权重
- 可以与其他查询组合使用
性能考虑
- 地理距离计算较为耗费资源,建议使用合适的索引优化地理查询,比如:考虑使用地理网格索引提升性能
Pinned 查询
功能说明
实现对某些文档的置顶功能,使用存储在_id 字段中的文档 ID 来标识升级或“固定”的文档。
此功能通常用于引导搜索者查找精选的文档,这些文档在搜索的任何 “organic” 匹配项之上被提升。当查询中有排序时,pinned 查询失效。
使用限制
- 不能与自定义排序一起使用
- 置顶文档必须存在于索引中
- 最多支持 100 个置顶文档
排序规则
- 置顶文档按照 ids 数组中的顺序排序
- organic 查询结果按照相关性得分排序
- 置顶文档始终在 organic 结果之前
PIT 查询
功能说明
Point in time 查询是一个轻量级的视图,根据保留周期保留 PIT 查询发生时数据的状态,用于不同条件的深度分页查询。
scroll 滚动搜索及其上下文与查询内容绑定。这意味着编写一个查询,添加一个滚动参数,来自这个查询的响应数据就会保持一致。不同的查询内容则会产生不同的 scroll 上下文,资源使用就会相对紧张。有时想对同一固定数据集适时运行不同的查询,就需要 PIT 查询。
如果对于一个不断变化的索引有着很高的搜索负载,那么为每个请求创建一个新的时间点查询会使用相当多的资源。可以通过使用一个后台进程每隔几分钟创建一个时间点 id 并将其用于所有搜索请求的方式来优化资源使用
更多内容可以[参照这里](https://blog.csdn.net/UbuntuTo ... 925187)
注意事项
资源管理
- PIT 会占用系统资源,需要及时释放
- 建议设置合理的保留时间
- 监控 open context 数量
使用场景
- 适合需要一致性视图的场景
- 适合需要深度分页的场景
- 适合需要在固定数据集上执行多次查询的场景
性能优化
- 避免过长的保留时间,合理设置批次大小
- 建议查询的时候带 sort 参数排序
测试代码
<br /> // 1. 使用 product_test 索引,创建 PIT<br /> POST /product_test/_pit?keep_alive=1m<br />

<br /> GET /_search<br /> {<br /> "size": 1,<br /> "query": {<br /> "match": {<br /> "model": "iphone"<br /> }<br /> },<br /> "pit": {<br /> "id": "i6-xAwEMcHJvZHVjdF90ZXN0FmRObXltV3ZDU1VTTnllYjNoR0ZtamcAFk1GQklTWXBaUkllb2h1cGl1VVFsdUEAAAAAAAABZk8WOFVoUHBhN3BSVVN5TWVmeTh4d3JpdwEWZE5teW1XdkNTVVNOeWViM2hHRm1qZwAA",<br /> "keep_alive": "1m"<br /> },<br /> "sort": [<br /> {"_score": "desc"},<br /> {"_id": "asc"}<br /> ]<br /> }<br /> <br />

<br /> DELETE /_pit<br /> {"id":"i6-xAwEMcHJvZHVjdF90ZXN0FmRObXltV3ZDU1VTTnllYjNoR0ZtamcAFk1GQklTWXBaUkllb2h1cGl1VVFsdUEAAAAAAAABZk8WOFVoUHBhN3BSVVN5TWVmeTh4d3JpdwEWZE5teW1XdkNTVVNOeWViM2hHRm1qZwAA"}<br />

小结
作为查询方法,Elasticsearch 新增的几个功能面对的场景更加具象且方便,大家可以根据业务的场景特性可以优化使用。其中深度分页查询是 ES 使用者几乎避免不了的场景,PIT 查询也是提供了一个更为优质的方法。
推荐阅读
- [谈谈 ES 6.8 到 7.10 的功能变迁(1)- 性能优化篇](https://infinilabs.cn/blog/202 ... part-1)
- [谈谈 ES 6.8 到 7.10 的功能变迁(2)- 字段类型篇](https://infinilabs.cn/blog/202 ... part-2)
- [谈谈 ES 6.8 到 7.10 的功能变迁(4)- 聚合功能篇](https://infinilabs.cn/blog/202 ... part-4)
- [谈谈 ES 6.8 到 7.10 的功能变迁(5)- 任务和集群管理](https://infinilabs.cn/blog/202 ... part-5)
- [谈谈 ES 6.8 到 7.10 的功能变迁(6)- 其他](https://infinilabs.cn/blog/202 ... part-6)
关于极限科技(INFINI Labs)

极限科技,全称极限数据(北京)科技有限公司,是一家专注于实时搜索与数据分析的软件公司。旗下品牌极限实验室(INFINI Labs)致力于打造极致易用的数据探索与分析体验。
极限科技是一支年轻的团队,采用天然分布式的方式来进行远程协作,员工分布在全球各地,希望通过努力成为中国乃至全球企业大数据实时搜索分析产品的首选,为中国技术品牌输出添砖加瓦。
官网:<https://infinilabs.cn>
作者:金多安,极限科技(INFINI Labs)搜索运维专家,Elastic 认证专家,搜索客社区日报责任编辑。一直从事与搜索运维相关的工作,日常会去挖掘 ES / Lucene 方向的搜索技术原理,保持搜索相关技术发展的关注。
原文:https://infinilabs.cn/blog/202 ... rt-3/
谈谈 ES 6.8 到 7.10 的功能变迁(2)- 字段类型篇
INFINI Labs 小助手 发表了文章 • 0 个评论 • 2086 次浏览 • 2025-04-25 19:41
我们继续来了解一下从 ES 6.8 到 ES 7.10 新增的功能。本篇主要介绍新增的字段类型,会简要概述一下新增字段类型的使用场景和限制,提供简单的测试代码。
Flattened 扁平化对象字段
功能说明
解决场景
该功能主要用于处理具有大量不确定键的 JSON 对象,避免字段映射爆炸问题,特别适用于不需要对对象内部字段进行单独分析和聚合的场景,以及当对象结构不固定,字段名称动态变化时。
使用注意点
- 整个对象被视为单个字段,无法对内部单个字段进行分析或聚合
- 只支持 keyword 类型的操作,如 term、prefix 查询等。因为它的每个解析出的字段都值为 keyword 字段
- 默认最大字段深度为 20,可以通过 depth_limit 来设置。
- 不支持数字范围查询,高亮显示
- 查询时,无法使用通配符引用字段键,比如 { "term": {"labels.time*": 1541457010}}
- split_queries_on_whitespace 为 true 时,这个字段的全文查询(match,query_string,simple_query_string)等于是用了空格分词器。
支持的查询方法
- term 查询:精确匹配某个字段的值
- terms/terms_set 查询:匹配多个值中的任意一个或者多个
- prefix 查询:前缀匹配
- exists 查询:检查字段是否存在
- match 查询:分词后的全文检索(但因为是 keyword,所以实际上是精确匹配)
- query_string 和 simple_query_string
Shape 字段
功能说明
该功能主要用于存储和查询任意几何图形数据,支持点、线、圆、矩形、多边形等几何形状,特别适用于地理空间分析和基于形状的搜索场景,以及相比于 geo_shape 专门用于地理空间数据(坐标系统固定为 WGS84 经纬度),shape 字段可以用于任意坐标系统的几何形状,比如虚拟世界或者保密空间。
使用要点
- 形状数据使用 [GeoJSON](https://geojson.org/) 或[Well-Known Text (WKT)](https://docs.ogc.org/is/12-063r5/12-063r5.html)格式表示
- 支持的空间关系操作包括:INTERSECTS(相交)、DISJOINT(不相交)、WITHIN(内部)和 CONTAINS(包含)
- 由于 shape 字段的复杂输入结构和索引表示,目前无法对 shape 字段进行排序,也无法直接检索其字段值(只能通过 _source 字段获取)
- orientation 参数用于定义多边形顶点读取的顺序:默认逆时针(counterclockwise),可选顺时针(clockwise)
Wildcard 字段
功能说明
解决的场景
用于优化通配符和正则表达式查询性能。特别适用于需要进行暴力模糊匹配的文本字段。
wildcard 字段在 keyword 字段和 ngrams 分词器之间找到了性能和存储成本的均衡。
原理说明
wildcard 字段采用了一种独特的索引策略,使用这种数据类型自动加速通配符和正则表达式搜索:
- n-gram 索引:存储字符串中所有 3 个字符的序列。可以理解为 ngram 分词器取长度为 3 的子串.
- 二进制 doc value:存储完整的原始文档值
这种方式与传统的 keyword 字段(完全不分词)和 text 字段(基于分词器分词)有很大不同。在查询时,系统首先使用这些 ngrams 构建的索引进行初步筛选,快速定位可能匹配的文档。这个过程类似于数据库中的索引过滤,可以大大减少需要详细检查的文档数量。然后,系统会从二进制 doc value 中取出这些候选文档的完整字段值,进行精确的模式匹配,确保最终结果的准确性。
这种策略在性能和存储空间上取得了很好的平衡:
- n-gram 索引:存储字符串中所有 3 个字符的序列。可以理解为 ngram 分词器取长度为 3 的子串.
- 相比 keyword 字段,在精确匹配查询时性能稍差
- 相比 ngrams 分词,它避免了过度的 token 生成,既节省存储空间,又保持了不错的查询性能
- 特别适合对日志数据进行类似 grep 的模式匹配查询,如通配符查询和正则表达式查询
使用注意点
- 比 keyword 类型更适合做通配符搜索,但会占用更多磁盘空间
- 建议仅在确实需要频繁进行通配符查询的字段上使用
- 与 keyword 字段一样是非分词(untokenized)的,所以不支持依赖词项位置的查询(如短语查询)
更多原理说明可以[参照这里](https://blog.csdn.net/UbuntuTo ... 120879)
Version 字段类型
功能说明
该功能主要用于存储和比较软件版本号,提供版本号的自然排序和比较功能,支持标准的版本号格式(如 1.0.0, 2.1.3-alpha 等)。
使用说明
- 版本号必须符合标准格式:主版本号、次版本号、修订号
- 支持带有预发布标识符的版本号(如 alpha, beta, rc 等)
- 可以进行版本号的大小比较和范围查询,比如可以在 1.0.0 和 2.0.0 之间查找所有版本
- 底层存储的是 keyword 类型,虽然可以正则匹配或者模糊查询,但是在大数据量下注意性能问题。
Histogram 直方图字段
功能说明
解决的场景
Histogram 字段专门用于存储预先计算好的直方图数据,这种数据结构在需要频繁进行统计分析的场景下特别有用。通过预先聚合数据并以直方图形式存储,可以显著减少查询时的实时计算开销,提高查询性能。对大规模数据集的统计分析很有利,比如系统监控指标、用户行为分析等需要快速获取数据分布情况的场景。
直方图的数据格式
直方图字段需要包含两个必需的参数来表示直方图数据:
values
:数值数组,表示每个聚合分桶的取值点,即区间的起点。数组中的值必须是严格递增双精度浮点数counts
:整数数组,表示落在当前区间内的元素数量。数组中的值必须是非负整数。具体来说,对于位置 i,counts[i] 表示数值大于等于 values[i] 且小于 values[i+1] 的元素个数(最后一个区间则包含等于 values[last] 的元素)
这两个数组必须具有相同的长度。
例如,一个表示年龄分布的直方图数据可能如下:
json<br /> {<br /> "age_histogram": {<br /> "values": [20.0, 30.0, 40.0, 50.0], // 年龄区间的起点:[20-30)、[30-40)、[40-50)、[50 及以上]<br /> "counts": [100, 150, 75, 25] // 表示:20-29 岁有 100 人,30-39 岁有 150 人,40-49 岁有 75 人,50 岁及以上有 25 人<br /> }<br /> }<br />
存储说明
直方图字段主要用于聚合分析。为了优化聚合操作的性能,数据以二进制 doc values 形式存储,而不是创建索引。每个直方图字段的存储大小最多为 13 * numValues 字节,其中 numValues 是数组的长度。
使用注意点
- value 数组必须按升序排列
- count 数组的长度必须与 value 数组相同
- 由于数据被索引,直方图字段仅支持以下操作:
- 聚合操作:min(最小值)、max(最大值)、sum(求和)、value_count(计数)、avg(平均值)、percentiles(百分位数)、percentile_ranks(百分位等级)、boxplot(箱线图)、histogram(直方图)
- 查询操作:仅支持 exists(存在性)查询
Search-as-you-type 字段
功能说明
解决的场景
该功能主要用于实现自动补全的即时搜索体验,比如可以在当用户输入搜索关键词的时候,还没输完就可以提示用户数据库里最相关的内容。
它是通过支持前缀匹配和部分词语匹配的方式,使用户在输入过程中就能获得搜索结果。
字段结构说明
search_as_you_type 字段会自动创建以下子字段:
- 聚合操作:min(最小值)、max(最大值)、sum(求和)、value_count(计数)、avg(平均值)、percentiles(百分位数)、percentile_ranks(百分位等级)、boxplot(箱线图)、histogram(直方图)
{field_name}
: 基础字段,用于完整词语匹配,使用字段设置的分词器。{field_name}._2gram
: 用大小为 2 的 shingle token filter 分词器对 ny_field 进行分词{field_name}._3gram
: 用大小为 3 的 shingle token filter 分词器对 ny_field 进行分词{field_name}._index_prefix
: 用 edge ngram token filter 包装 my_field._3gram 的分词器
使用注意
- 可以通过设置 max_shingle_size 参数(默认为 3)来控制生成的子字段数量。max_shingle_size 参数越大,子字段越多,默认 3 个字段能覆盖大部分的场景。
- search-as-you-type 字段索引空间占用较大,因为需要存储多个分词器的结果。
前缀查询(prefix)优化机制
search_as_you_type 字段在处理前缀查询时有特殊的优化。当对根字段或其子字段进行前缀查询时,查询会被重写为针对 ._index_prefix 子字段的 term 查询,这比传统的文本字段前缀查询更高效
._index_prefix 子字段的分析器会对分词结果进行特殊处理:
- 不仅索引常规的 n-gram 分词结果
- 还会索引字段值词条的前缀,即使这些词条不会出现在 n-gram 子字段中
例如,对于文本 "quick brown fox",在 max_shingle_size 为 3 的情况下:不仅会索引会索引"quick" "brown" "fox",还会索引这些此项的前缀,这确保了字段中所有词条都能支持自动完成功能,即使这些词条组合并不存在于 ._3gram 子字段中
小结
这里我们只是简要的介绍了一下新增字段类型的设计背景和原理,并没有深入展开挖掘新增字段的性能场景或者奇思妙用,比如 wildcard 与 ngram 颗粒度性能对比,直方图数据和 pipeline 进行日志分析的组合等等。大家可以根据实际的需求场景进行深度挖掘。
推荐阅读
- [谈谈 ES 6.8 到 7.10 的功能变迁(1)- 性能优化篇](https://infinilabs.cn/blog/202 ... part-1)
- [谈谈 ES 6.8 到 7.10 的功能变迁(3)- 查询方法篇](https://infinilabs.cn/blog/202 ... part-3)
- [谈谈 ES 6.8 到 7.10 的功能变迁(4)- 聚合功能篇](https://infinilabs.cn/blog/202 ... part-4)
- [谈谈 ES 6.8 到 7.10 的功能变迁(5)- 任务和集群管理](https://infinilabs.cn/blog/202 ... part-5)
- [谈谈 ES 6.8 到 7.10 的功能变迁(6)- 其他](https://infinilabs.cn/blog/202 ... part-6)
关于极限科技(INFINI Labs)

极限科技,全称极限数据(北京)科技有限公司,是一家专注于实时搜索与数据分析的软件公司。旗下品牌极限实验室(INFINI Labs)致力于打造极致易用的数据探索与分析体验。
极限科技是一支年轻的团队,采用天然分布式的方式来进行远程协作,员工分布在全球各地,希望通过努力成为中国乃至全球企业大数据实时搜索分析产品的首选,为中国技术品牌输出添砖加瓦。
官网:<https://infinilabs.cn>
作者:金多安,极限科技(INFINI Labs)搜索运维专家,Elastic 认证专家,搜索客社区日报责任编辑。一直从事与搜索运维相关的工作,日常会去挖掘 ES / Lucene 方向的搜索技术原理,保持搜索相关技术发展的关注。
谈谈 ES 6.8 到 7.10 的功能变迁(1)- 性能优化篇
INFINI Labs 小助手 发表了文章 • 0 个评论 • 2666 次浏览 • 2025-04-15 11:21
前言
ES 7.10 可能是现在比较常见的 ES 版本。但是对于一些相迭代比较慢的早期业务系统来说,ES 6.8 是一个名副其实的“钉子户”。
借着工作内升级调研的任务东风,我整理从 ES 6.8 到 ES 7.10 ELastic 重点列出的新增功能和优化内容。将分为 6 个篇幅给大家详细阐述。
本系列文章主要针对 Elasticsearch 传统的使用功能和基础的模块,像是集群任务的管理、搜索、聚合还有字段类型这样的功能。对于付费功能或者全新的模块,比如:CCR、机器学习和数据流,这里不去深入探讨。
内容的主要来源于 Elastic [各个版本的发布信息](https://www.elastic.co/cn/blog/category/releases),这里主要比对 ES 6.8 版本到 7.10 版本的差异,并不一一枚举各个新的功能点出现的时间版本。
下面是第一篇:关于 ES 性能的优化
ES 7.10 的性能优化
集群协调算法升级
基于 Elastic 博客提供的资料,Elasticsearch 7.0 的核心改进在于集群协调层的彻底重构,取代了旧版 Zen Discovery 的局限性,引入更健壮、自动化的分布式共识机制。从理论上来说这次优化有着不少的进步,可以显著提升了高可用性与运维效率
主要的优化点有下面三点:
- 消除分裂脑(Split Brain)风险:通过自动化计算,确保集群状态更新的安全性。旧版
minimum_master_nodes
的手动配置被移除,避免人为误操作。
- 提升集群稳定性与恢复速度:节点故障时,集群更快达成一致,减少服务中断窗口。
- 简化运维复杂度:可以动态扩缩容无需手动调整配置,系统自动管理选举配置。同时提供更清晰的日志和错误提示,加速故障诊断。
| 旧版配置 | ES 7.0 配置 | 作用 |
| ------------------------------------ | ---------------------- | ----------------------------------------- |
|discovery.zen.ping.unicast.hosts
|discovery.seed_hosts
| 定义初始发现的种子节点列表(IP 或主机名) |
|discovery.zen.minimum_master_nodes
| 已移除 | 由系统自动管理法定人数 |
而在优化的原则里,Elastic 更强调安全第一。比如,在半数以上主节点永久丢失的风险场景下,ES 7.0 之前的集群会静默等待恢复,允许通过启动新空节点强制恢复,这样可能会导致数据不一致或丢失。在 Elasticsearch 7.0 以及更高版本中,这种不安全活动受到了更多限制。集群宁愿保持不可用状态,也不会冒这种风险(除非使用 elasticsearch-node 恢复工具)。
这次优化显著降低了人为错误的风险:移除脆弱的手动配置,减少运维使用的理解成本。同时提升关键业务连续性:快速故障恢复与明确的容错机制,能适合更多场景需求。
当然也并不是尽善尽美的,也会存在大集群下投票节点过多导致竞争激烈而[无法选主的问题](https://mp.weixin.qq.com/s/jU8HCEf2E6hkz_1ZVH_GaQ),这种情况下,建议部署独立的主节点,并且可以考虑适当增大 cluster.election.duration 的配置。
Top K 对检索的加速
这里的 Top K 主要是指在普通检索时展示前列的数据 Top K。也就是说 Elasticsearch 7.0 对检索数据的查询性能做了明显的改善。那是做了所有查询场景的提升么?
ELastic 做了这么一个场景假设:如果用户通常只关注搜索结果的第一页,且并不关心具体匹配的文档总数,对于超出一定数量的数据搜索引擎可以展示“超过 10,000 条结果”并提供分页浏览来优化搜索效率。但是在实际过程中用户常在查询中使用高频词(如“the”或“a”),这迫使 Elasticsearch 为大量文档计算评分,明显占用了查询资源的使用,即使这些常见词对相关性排序贡献甚微。

而现在,Elasticsearch 现在可以跳过那些在早期阶段就被判定为不会进入结果集顶部的低排名记录的评分计算,从而显著提升查询速度。这里主要涉及了 block-max WAND 算法的实现。这是一个复杂且漫长的优化过程,有兴趣的同学可以阅读一下这段[Magic WAND: Faster Retrieval of Top Hits in Elasticsearch](https://www.elastic.co/blog/fa ... x-wand)。

从 Elastic 的测试结果来看,新算法的优化让 term 查询加速了 3-7 倍。当然从场景背景可以看出,这个优化主要在大数据量下有明显效果(小数据量也不会有太多的日常高频词)。
默认开启 soft-delete 减少 translog
从 Elasticsearch 7.4 开始,副本的数据恢复,不再完全依赖 translog 了,而是通过索引的 soft-delete 特性(Elasticsearch 7.0 起所有新索引默认启用软删除 soft-deletes)。这样就可以缩小 translog 的使用场景,从而 translog 的保留大小也可以减少了。
那原来使用 translog 是什么样的呢?
translog 是 ES 用于保证数据安全性的重要工具。同时副分片进行恢复时,它也起着重要作用,只要副分片待获取的差异数据是在 translog 所保留的数据范围内,就可以只从 trasnlog 复制差异的部分数据,而不用拖取整个分片。在之前的版本中,Elasticsearch 默认会保留 512M 或 12 小时的 translog 用于副本恢复。
那现在使用的 soft-delete 是什么呢?
soft-deletes 是 Lucene 中实现的特性。这个软删除有时候会和 lucene 本身的标记删除概念发生混淆。为了方便理解,我们在这里归纳一下,lucene 实现删除的方式是一种标记删除的方式,而这种标记删除可以分为硬删除和软删除。软删除和硬删除有一个明显的区分点是:硬删除,被删除的文档对应的文档号用索引文件 .liv 来描述。软删除 soft-delete,被标记为删除的文档不使用索引文件.liv 来描述,而是通过索引文件 .dvd .dvm 来描述。
这里再扩展一下,.liv 文件主要实现 fixedbitset 数据结构。而 .dvd .dvm 则组合实现了 docvalue 这种正排数据结构。
正排索引的数据结构助力了 translog 的‘减负’,副本可以相对简便的通过软删除中的数据标记来实现数据恢复的处理。

相比较简洁高效的位图索引,docvalue 虽然实现了更多的功能,满足更多的场景,也会带来更多的问题。最明显的就是对于 update 操作,会导致 refresh 变得慢,有些压力场景下 refresh 会达到 10s 以上。
数值/日期排序查询加速
Elasticsearch 7.6 版本提升了按日期或数值(即任何存储为有符号 64 位整数(long 类型)的字段)进行排序的查询性能。
这背后的优化原理和之前 top K 使用的 Block-Max WAND 算法有点相似,都是利用算法跳过非竞争性文档来实现加速。
实际效果可能因环境而异,受多种参数影响。在 Elastic 进行的测试场景下,可以达到 35 倍的速度优化。
FST 内存使用迁移到堆外
Elastic 7.3 版本实现了这个优化,是藏在 release note 里的彩蛋。
Also mmap terms index (.tip) files for hybridfs #43150 (issue: #42838)
看似不经意的一行,但是带来效果却不小。FST 从堆内转移到堆外后,JVM 的空间可以空余出很客观的一部分。

一直以来,ES 堆中常驻内存中占据比重最大是 FST,即 tip(terms index) 文件占据的空间,1TB 索引大约占用 2GB 或者更多的内存,因此为了节点稳定运行,业界通常认为一个节点 open 的索引不超过 5TB。现在,从 ES 7.3 版本开始,将 tip 文件修改为通过 mmap 的方式加载,这使 FST 占据的内存从堆内转移到了堆外由操作系统的 pagecache 管理。
存储字段压缩优化
Elasticsearch 7.10 基于 Apache Lucene 8.7 引入了对存储字段(stored fields)的更高压缩率优化。不管是对于基于 DEFLATE 的index.codec: best_compression
还是基于 LZ4 的index.codec: default
都有不错的表现,在 Elastic 的测试场景下,最大可达到 10%的存储空间减少。
对于数据压缩 lucene 这次主要做了两个优化。
- Elastic 研究发现在存储数据的时候,底层的 block 越大,压缩效果越好,因为中间被压缩的重复数据可能越多。但是大块的 block 也可能因为解码重复数据降低查询速度。
- block 间通过共享字典来维持检索效率和数据压缩之间的平衡。
2.1. 首先为压缩算法提供一个数据字典,它也可以用于字符串重复数据删除。如果在要压缩的数据流和字典之间有许多重复的字符串,那么最终可以得到更好的压缩比。在解压缩时也通过字典来快速补足。

2.2. 同时,ES 使用更大的数据块,这些数据块本身被分成一个字典和 10 个子块,这些子块使用这个字典进行压缩。

而对于实际业务场景中,日志和监控数据的重复率往往会很好,因此在这两个场景中的压缩效果也是最明显的。
小结
当然,除了这几项外,ES 在各个版本中也做了不少优化,比如:调整 search.max_buckets 增加到 65534;Date histogram 聚合性能优化等等。有兴趣的同学可以参照各个版本的 [release highlight](https://www.elastic.co/guide/e ... s.html)
参考资料:
- [Save space and money with improved storage efficiency in Elasticsearch 7.10](https://www.elastic.co/blog/sa ... h-7-10)
- [Elasticsearch 7.3 的 offheap 原理](https://mp.weixin.qq.com/s/QviC_9ElknSS9kxXSMjjbg)
- [Elasticsearch 7.4 的 soft-deletes 是个什么鬼](https://mp.weixin.qq.com/s/_l8JAtqK_NOSP8b7OqSVDg)
推荐阅读
- [谈谈 ES 6.8 到 7.10 的功能变迁(2)- 字段类型篇](https://infinilabs.cn/blog/202 ... part-2)
- [谈谈 ES 6.8 到 7.10 的功能变迁(3)- 查询方法篇](https://infinilabs.cn/blog/202 ... part-3)
- [谈谈 ES 6.8 到 7.10 的功能变迁(4)- 聚合功能篇](https://infinilabs.cn/blog/202 ... part-4)
- [谈谈 ES 6.8 到 7.10 的功能变迁(5)- 任务和集群管理](https://infinilabs.cn/blog/202 ... part-5)
- [谈谈 ES 6.8 到 7.10 的功能变迁(6)- 其他](https://infinilabs.cn/blog/202 ... part-6)
作者:金多安,极限科技(INFINI Labs)搜索运维专家,Elastic 认证专家,搜索客社区日报责任编辑。一直从事与搜索运维相关的工作,日常会去挖掘 ES / Lucene 方向的搜索技术原理,保持搜索相关技术发展的关注。
原文:https://infinilabs.cn/blog/202 ... rt-1/
- [谈谈 ES 6.8 到 7.10 的功能变迁(2)- 字段类型篇](https://infinilabs.cn/blog/202 ... part-2)
代理 Elasticsearch 服务:INFINI Gateway VS Nginx
INFINI Labs 小助手 发表了文章 • 0 个评论 • 2780 次浏览 • 2025-04-11 15:38

INFINI Gateway 简介
[INFINI Gateway](https://infinilabs.cn/products/gateway/) 是一款面向 Elasticsearch 的高性能应用网关,专为提升 Elasticsearch 集群的性能、安全性和可管理性而设计。它作为 Elasticsearch 的前置网关,能够处理所有客户端请求,并将其转发到后端的 Elasticsearch 集群,同时提供丰富的功能来优化请求处理和管理。此外还支持代理 Opensearch、[Easysearch](https://infinilabs.cn/products/easysearch/) 服务。
Nginx 简介
Nginx 是一个高性能的 HTTP 和反向代理服务器,以其高并发处理能力、低内存消耗和稳定性著称,广泛应用于 Web 服务器、负载均衡和反向代理等场景。在 Elasticsearch 的使用场景里,也有小伙伴使用 Nginx 来代理 Elasticsearch 的服务,利用 Nginx 的负载均衡能力,将请求转发到多个 Elasticsearch 节点。
这两个软件都能代理 Elasticsearch 服务,但是他们有什么区别?我们来一起分析分析。
负载均衡
Elasticsearch 是分布式系统,提倡使用 round-robin 方式将请求发送到多个节点。不管是 Nginx 还是 INFINI Gateway 都默认使用 round-boin 方式转发请求,也都支持 weighted round-robin(加权轮询)方式进行请求转发,这点两者相当。
节点自动更新
Elasticsearch 集群可能会遇到添加、删除节点的情况,代理程序能否感知 Elasticsearch 节点的变化将变得非常关键。
在 Nginx 中,所有转发节点的 IP 地址都必须写入到配置文件中。如果 Elasticsearch 集群加入了新的节点进行请求处理,则需要 Nginx 编辑配置文件把新节点的 IP 地址加入其中,然后重启或重载 Nginx 服务,才能将请求分发到新的节点。反之如果有节点下线,也要编辑 Nginx 配置文件并重载服务。
INFINI Gateway 是面向 Elasticsearch 设计的应用网关,具有后端节点发现和更新的功能,能够感知 Elasticsearch 集群节点加入、离开的情况。开启节点发现和更新功能后,Gateway 会定期自动更新节点列表,将请求均匀转发到列表中的节点。可参考之前的[博客](https://infinilabs.cn/blog/202 ... teway/)开启节点自动更新。
⚠️ 注意:INFINI Gateway 默认后端节点发现和更新的功能为关闭状态。
定向转发请求
使用 Elasticsearch 集群的场景多种多样,如果想对转发的节点做进一步控制,可能需要根据不同条件进行节点筛选:
- IP 地址
- 节点角色
- 节点标签
Nginx
Nginx 支持根据 IP 地址进行转发的,将需要转发的节点 IP 地址写入配置文件即可。
plain<br /> upstream es-cluster {<br /> server 192.168.56.102:9200;<br /> server 192.168.56.102:9201;<br /> server 192.168.56.102:9202;<br /> }<br />
但不支持按节点角色、节点标签进行筛选,因为 Nginx 中并没有这种概念。
INFINI Gateway
INFINI Gateway 支持按 IP 地址进行筛选:
- 不开节点发现:只转发到配置文件指定的节点(IP 地址)
- 开启节点发现:转发到所有发现的节点
```plain
flow:
- name: cache_first
filter:
- elasticsearch:
elasticsearch: prod
refresh:
enabled: true
interval: 30s
filter:
hosts:
exclude:
- 192.168.3.201:9200
include: - 192.168.3.202:9200
- 192.168.3.203:9200
<br /> <br /> 此外 Gateway 还支持通过节点角色、节点标签筛选转发节点。<br /> <br />
plain
flow:
- 192.168.3.201:9200
- elasticsearch:
- name: cache_first
filter:
- elasticsearch:
elasticsearch: prod
refresh:
enabled: true
interval: 30s
filter:
tags:
exclude:
- temp: cold
include: - disk: ssd
roles:
exclude: - master
include: - data
- ingest
```
多种筛选条件可以同时使用,详细信息请查看官方[文档](https://docs.infinilabs.com/ga ... earch/)。
作者:杨帆,极限科技(INFINI Labs)高级解决方案架构师、《老杨玩搜索》栏目 B 站 UP 主,拥有十余年金融行业服务工作经验,熟悉 Linux、数据库、网络等领域。目前主要从事 Easysearch、Elasticsearch 等搜索引擎的技术支持工作,服务国内私有化部署的客户。
原文:https://infinilabs.cn/blog/202 ... ginx/
- temp: cold
- elasticsearch:
- name: cache_first
是否可以为es的slowlog传入多个id值
kin122 回复了问题 • 2 人关注 • 1 个回复 • 2179 次浏览 • 2025-03-12 08:57
有什么 es 开源工具可以推荐使用
INFINI Labs 小助手 回复了问题 • 2 人关注 • 1 个回复 • 4196 次浏览 • 2025-01-15 14:48
是否可以在forcemerge时指定只清理一个段里的deleted文档,不合并2个段
Fred2000 回复了问题 • 2 人关注 • 1 个回复 • 2968 次浏览 • 2025-01-14 10:14
Elasticseach Ingest 模块&&漏洞分析
Radiancebobo 发表了文章 • 0 个评论 • 2910 次浏览 • 2024-12-23 12:58
本文基于Elasticsearch7.10.2分析
0.Ingest 节点 概述
在实际进行文档index之前,使用采集节点(默认情况下,每个es节点都是ingest)对文档进行预处理。采集节点会拦截bulk和index请求,进行转换,然后将文档传回index或bulk API。
每个索引都有index.default_pipeline 和 index.final_pipeline 两个配置。
他们都是用于指定 Elasticsearch index 或者bulk 文档时要执行的预处理逻辑。
- index.default_pipeline 定义了默认管道,它会在索引文档时首先执行。但如果索引请求中指定了 pipeline 参数,则该参数指定的管道会覆盖默认管道。如果设置了 index.default_pipeline 但对应的管道不存在,索引请求会失败。特殊值 _none 表示不运行任何摄取管道。
- index.final_pipeline 定义了最终管道,它总是在请求管道(如果指定)和默认管道(如果存在)之后执行。如果设置了 index.final_pipeline 但对应的管道不存在,索引请求会失败。特殊值 _none 表示不运行最终管道。
简而言之,default_pipeline 先执行,可被覆盖;final_pipeline 后执行,不可被覆盖。两者都可设为 _none 以禁用。
一个Pipeline可以有多个Processor组成,每个Processor有着各自的不同功能,官方支持的Processor可参考:https://www.elastic.co/guide/e ... .html
一个简单的例子,利用Set Processor 对新增的文档中加入新的字段和值:
java<br /> PUT _ingest/pipeline/set_os<br /> {<br /> "description": "sets the value of host.os.name from the field os",<br /> "processors": [<br /> {<br /> "set": {<br /> "field": "host.os.name", // 增加的属性<br /> "value": "{{os}}" // 这里引用了文档原先的os属性, 这里可以直接填写其他值<br /> }<br /> }<br /> ]<br /> }<br /> <br /> POST _ingest/pipeline/set_os/_simulate<br /> {<br /> "docs": [<br /> {<br /> "_source": {<br /> "os": "Ubuntu"<br /> }<br /> }<br /> ]<br /> }<br />
这样转换之后,文档内容就变成了:
java<br /> {<br /> "host" : {<br /> "os" : {<br /> "name" : "Ubuntu"<br /> }<br /> },<br /> "os" : "Ubuntu"<br /> }<br />
写单个文档的流程概述
当请求,或者索引本身配置有pipline的时候,协调节点就会转发到ingest节点
java<br /> PSOT source_index/_doc?pipeline=set_os<br /> {<br /> "os": "xxxx"<br /> }<br />
【注】 并不是一定会发生内部rpc的请求转发,如果本地节点能接受当前的请求则不会转发到其他节点。
1. 模块总体概述
本小节关注IngestService中重要的相关类,对这些类有一个整体的了解有助于理解该模块。
- ClusterService
- IngestService 实现了 ClusterStateApplier 接口, 这样就能监听和响应集群的状态变化,当集群状态更新时,IngestService可以调整其内部 pipelines完成CRUD。
- 另外IngestService还有List<Consumer
>用来对提供给对集群状态变更之后需要最新状态的插件。
- IngestService 实现了 ClusterStateApplier 接口, 这样就能监听和响应集群的状态变化,当集群状态更新时,IngestService可以调整其内部 pipelines完成CRUD。
- ScriptService
- 某些Processor需要其用于管理和执行脚本,比如Script Processor。
- 某些Processor需要其用于管理和执行脚本,比如Script Processor。
- AnalysisRegistry
- 某些需要对文档内容进行分词处理的Processor。
- 某些需要对文档内容进行分词处理的Processor。
- ThreadPool
- Processor都是异步执行的,实际执行线程池取决于调用上下文(如 write 或 management)
- bulk API 时发生的pipeline 处理使用的是write线程池。
- pipeline/_simulate API 使用的是management线程池,模拟执行通常是短时间的、低频的任务,不需要高并发支持而且为了不影响实际的文档处理或其他重要任务。
- bulk API 时发生的pipeline 处理使用的是write线程池。
- 另外为了避免Grok Processor运行时间过长,使用了Generic线程做定时调度检查执行时间
- Processor都是异步执行的,实际执行线程池取决于调用上下文(如 write 或 management)
- IngestMetric
- 通过实现ReportingService接口来做到展示ingest内部的执行情况。
- GET _nodes/stats?filter_path=nodes.*.ingest 可以查看到ingest 中的每个Pipeline中的执行的次数、失败次数以及总耗时
- 通过实现ReportingService接口来做到展示ingest内部的执行情况。
- IngestPlugin
- Ingest支持加载自定义的Processor插件,系统内置的所有Processor以及自定义的都通过IngestService 中的Map<String, Processor.Factory> processorFactories来进行管理。
- Ingest支持加载自定义的Processor插件,系统内置的所有Processor以及自定义的都通过IngestService 中的Map<String, Processor.Factory> processorFactories来进行管理。
- IngestDocument
- 其包含文档的源数据,提供了修改和查询文档的字段的能力,为Processor灵活操作文档数据提供基础,在后续pipeline的执行中,也是由其的executePipeline方法驱动的。
2. Processor 实现机制
抽象工厂设计模式的应用
Processor接口设计
每个Processor都有核心方法execute,使得处理器能够以统一的方式操作 IngestDocument,并通过多态实现不同处理逻辑。
Processor.Factory的设计
Processor.Factory 是 Processor 的抽象工厂,负责动态创建处理器实例。其主要职责包括:
- 其包含文档的源数据,提供了修改和查询文档的字段的能力,为Processor灵活操作文档数据提供基础,在后续pipeline的执行中,也是由其的executePipeline方法驱动的。
- 动态实例化:
- Processor create() 方法接收处理器配置并创建具体的处理器。
- 支持递归创建,例如ConditionalProcessor可以嵌套其他处理器。
- Processor create() 方法接收处理器配置并创建具体的处理器。
- 依赖注入:
- Processor.Parameters 提供了一组服务和工具(如 ScriptService),工厂可以利用这些依赖创建复杂的Processor。
Processor.Factory的集中管理
在 IngestService 中,通过 Map<String, Processor.Factory> processorFactories 集中管理所有处理器工厂。这种管理方式提供了以下优势:
动态扩展:
- Processor.Parameters 提供了一组服务和工具(如 ScriptService),工厂可以利用这些依赖创建复杂的Processor。
- 插件可以注册自定义Processor工厂。
- 新处理器类型的注册仅需添加到 processorFactories。
组合以及装饰器设计模式的应用
Processor经典的几个类的关系如下:
CompoundProcessor是经典的组合设计模式,Pipeline这个类可以像使用单个 Processor 一样调用 CompoundProcessor,无需关注其内部具体细节。
而ConditionalProcessor 以及TrackingResultProcessor则体现了装饰器模式,在不改变原有对象的情况下扩展功能:
- ConditionalProcessor 在执行Processor前会调用evaluate方法判断是否需要执行。
- TrackingResultProcessor中decorate是为 CompoundProcessor 及其内部的 Processor 添加跟踪功能。
如何自定义Processor 插件
自定义 Processor 插件的注册方式为实现 IngestPlugin (Elasticsearch 提供不同的插件接口用来扩展不同类型的功能)的 getProcessors 方法,该方法返回一个工厂列表,IngestService 会将这些工厂注入到 processorFactories 中。
分析完代码之后,我们回到实战中来,简单起见,我们实现类似Append的Processor,但是我们这个更简单,输入的是字符串,然后我们用,分割一下将其作为数组设为值。
java<br /> import org.elasticsearch.ingest.Processor;<br /> import org.elasticsearch.plugins.IngestPlugin;<br /> import org.elasticsearch.plugins.Plugin;<br /> import java.util.HashMap;<br /> import java.util.Map;<br /> public class AddArrayProcessorPlugin extends Plugin implements IngestPlugin {<br /> @Override<br /> public Map<String, Processor.Factory> getProcessors(Processor.Parameters parameters) {<br /> Map<String, Processor.Factory> processors = new HashMap<>();<br /> processors.put(AddArrayProcessor.TYPE, new AddArrayProcessor.Factory());<br /> return processors;<br /> }<br /> }<br />
java<br /> import org.elasticsearch.ingest.AbstractProcessor;<br /> import org.elasticsearch.ingest.ConfigurationUtils;<br /> import org.elasticsearch.ingest.IngestDocument;<br /> import org.elasticsearch.ingest.Processor;<br /> import java.util.*;<br /> <br /> public class AddArrayProcessor extends AbstractProcessor {<br /> public static final String TYPE = "add_array";<br /> public String field;<br /> public String value;<br /> protected AddArrayProcessor(String tag, String description, String field, String value) {<br /> super(tag, description);<br /> this.field = field;<br /> this.value = value;<br /> }<br /> @Override<br /> public IngestDocument execute(IngestDocument ingestDocument) throws Exception {<br /> List valueList = new ArrayList<>(Arrays.asList(value.split(",")));<br /> ingestDocument.setFieldValue(field, valueList);<br /> return ingestDocument;<br /> }<br /> @Override<br /> public String getType() {<br /> return TYPE;<br /> }<br /> public static final class Factory implements Processor.Factory {<br /> @Override<br /> public Processor create(Map<String, Processor.Factory> processorFactories, String tag,<br /> String description, Map<String, Object> config) throws Exception {<br /> String field = ConfigurationUtils.readStringProperty(TYPE, tag, config, "field");<br /> String value = ConfigurationUtils.readStringProperty(TYPE, tag, config, "value");<br /> return new AddArrayProcessor(tag, description, field, value);<br /> }<br /> }<br /> }<br />
打包之后,我们去install我们的Processor插件:
java<br /> bin/elasticsearch-plugin install file:///home/hcb/data/data/es/data_standalone/elasticsearch-7.10.2-SNAPSHOT/AddArrayProcessor-1.0-SNAPSHOT.zip <br /> warning: no-jdk distributions that do not bundle a JDK are deprecated and will be removed in a future release<br /> -> Installing file:///home/hcb/data/data/es/data_standalone/elasticsearch-7.10.2-SNAPSHOT/AddArrayProcessor-1.0-SNAPSHOT.zip<br /> -> Downloading file:///home/hcb/data/data/es/data_standalone/elasticsearch-7.10.2-SNAPSHOT/AddArrayProcessor-1.0-SNAPSHOT.zip<br /> [=================================================] 100% <br /> -> Installed AddArrayProcess<br />
测试一下:
java<br /> POST /_ingest/pipeline/_simulate<br /> {<br /> "pipeline": {<br /> "processors": [<br /> {<br /> "add_array": {<br /> "field": "test_arr",<br /> "value": "a,b,c,d,e,f"<br /> }<br /> }<br /> ]<br /> },<br /> "docs": [<br /> {<br /> "_index": "test",<br /> "_id": "1",<br /> "_source": {<br /> "field": "value"<br /> }<br /> }<br /> ]<br /> }<br /> docs结果:<br /> "_source" : {<br /> "field" : "value",<br /> "test_arr" : [<br /> "a",<br /> "b",<br /> "c",<br /> "d",<br /> "e",<br /> "f"<br /> ]<br /> }<br />
3. Pipeline设计
如何管理Pipeline
在IngestServcie中有一个Map存储所有的Pipeline实例,private volatile Map<String, PipelineHolder> pipelines = Collections.emptyMap();
这里并没有将Pipeline实例存储在IngestMetadata,这样做的原因有2个:
- 在 Elasticsearch 的启动过程中,插件和节点服务的初始化发生在 ClusterState 加载之后, 只有等所有插件完成加载后,所有的Processor工厂才会被注册到系统中,这意味着在集群状态初始化之前,Processor工厂并不可用。
- ClusterState 中的元数据结构是静态注册的,即在类加载时已经确定,如果要将运行时示例存储进去,必须改变 ClusterState 的元数据结构存储格式,这个很不方便维护,而且这在集群状态同步的时候也会带来不必要的序列化以及反序列化。
所以Pipeline的管理逻辑是:
- 在 Elasticsearch 的启动过程中,插件和节点服务的初始化发生在 ClusterState 加载之后, 只有等所有插件完成加载后,所有的Processor工厂才会被注册到系统中,这意味着在集群状态初始化之前,Processor工厂并不可用。
- ClusterState 中的IngestMetadata 只存储 JSON 格式的管道定义,描述 Pipeline 的配置(例如,包含哪些 Processor,它们的参数等)。
- IngestService 中维护的运行时实例,将 JSON 配置解析为完整的 Pipeline 对象,包括处理器链
在集群变更时, pipeline的CRUD的实现在innerUpdatePipelines方法中 。
责任链设计模式的应用
管道的执行通过IngestDocument的org.elasticsearch.ingest.IngestDocument#executePipeline 去驱动,每个文档的每个Pipeline都会进入到这个函数, 而每个Pipeline有组装好的CompoundProcessor,实际的链式调用是在CompoundProcessor中。
简图大致如下:
这里拿其和Zookeeper(3.6.3)中RequestProcessor 做一些对比:
| 特点 | ES中的CompoundProcessor | Zookeeper中RequestProcessor |
| --- | --- | --- |
| 链的存储定义 | 由两条列表组成,分别保存正常流程以及失败流程的Processor列表。 | 固定的链式结构。 |
| 异步执行 | 支持异步回调,可以异步执行链中的处理器。 | 同步执行 。 |
| 失败处理 | 提供专门的 onFailureProcessors 作为失败处理链。如果不忽略异常并且onFailureProcessors 不为空则会执行失败处理链逻辑。 | 没有专门的失败处理链,异常直接交由上层捕获。 |
| 可配置性 | 可动态调整处理器链和配置(如 ignoreFailure)。 | 代码中写死,无法配置 |
这里并没有说Zookeeper的设计就差于Elasticsearch, 只是设计目标有所不同,RequestProcessor就是适合集中式的强一致、其中Processor并不需要灵活变化,而CompoundProcessor就是适合高并发而Procesor灵活变化场景。
4. Ingest实战建议
回到实战中来,这里结合目前所分析的内容给出相应的实战建议。
- 建立监控
- 对于关键的Piepline,我们需要通过GET _nodes/stats?filter_path=nodes.*.ingest 获取其运行状况,识别延迟或失败的 Processor。
- 文档写入的时候使用的是write线程池,我们也需要监控GET _nodes/stats?filter_path=nodes.*.thread_pool.write 的queue和write_rejections 判断需要扩展线程池。
- 对于关键的Piepline,我们需要通过GET _nodes/stats?filter_path=nodes.*.ingest 获取其运行状况,识别延迟或失败的 Processor。
- 优化Processor
- 减少高开销操作,优先使用内置 Processor,比如script Processor 可适时替换set, append,避免过度复杂的正则表达式或嵌套逻辑。
- 自定义的Processor尽量优化,比如如果涉及查询外部系统可考虑引入缓存。
- 减少高开销操作,优先使用内置 Processor,比如script Processor 可适时替换set, append,避免过度复杂的正则表达式或嵌套逻辑。
- 建立单独的Ingest节点
- 如果有大量的Pipeline需要执行,则可以考虑增加专用 Ingest 节点,避免与数据节点争夺资源。
5. 漏洞&&修复分析
这里分析7.10.2版本Ingest模块存在的漏洞以及官方是如何修复的。
CVE-2021-22144
https://discuss.elastic.co/t/e ... 78100
Elasticsearch Grok 解析器中发现了一个不受控制的递归漏洞,该漏洞可能导致拒绝服务攻击。能够向 Elasticsearch 提交任意查询的用户可能会创建恶意 Grok 查询,从而导致 Elasticsearch 节点崩溃。
漏洞复现
发起这个请求:
- 如果有大量的Pipeline需要执行,则可以考虑增加专用 Ingest 节点,避免与数据节点争夺资源。
- patterns: 处理字段时使用的 Grok 模式,这里设置为 %{INT}。
- pattern_definitions: 定义自定义 Grok 模式,这里故意让 INT 模式递归引用自身,导致循环引用问题。
go<br /> POST /_ingest/pipeline/_simulate<br /> {<br /> "pipeline": {<br /> "processors": [<br /> {<br /> "grok": {<br /> "field": "message",<br /> "patterns": [<br /> "%{INT}"<br /> ],<br /> "pattern_definitions": {<br /> "INT": "%{INT}"<br /> }<br /> }<br /> }<br /> ]<br /> },<br /> "docs": [<br /> {<br /> "_source": {<br /> "message": "test"<br /> }<br /> }<br /> ]<br /> }<br /> <br />
当执行之后会使得节点直接StackOverflow中止进程。
修复逻辑
这个问题的关键在于原先的逻辑中,只会对间接的递归引用(pattern1 => pattern2 => pattern3 => pattern1)做了检测,但是没有对直接的自引用(pattern1 => pattern1 )做检测。
java<br /> private void forbidCircularReferences() {<br /> // 这个是增加的逻辑,检测直接的自引用<br /> for (Map.Entry<String, String> entry : patternBank.entrySet()) {<br /> if (patternReferencesItself(entry.getValue(), entry.getKey())) {<br /> throw new IllegalArgumentException("circular reference in pattern [" + entry.getKey() + "][" + entry.getValue() + "]");<br /> }<br /> }<br /> <br /> // 间接递归引用检测(这个是原先的逻辑)<br /> for (Map.Entry<String, String> entry : patternBank.entrySet()) {<br /> String name = entry.getKey();<br /> String pattern = entry.getValue();<br /> innerForbidCircularReferences(name, new ArrayList<>(), pattern);<br /> }<br /> }<br />
CVE-2023-46673
https://discuss.elastic.co/t/e ... 47708
漏洞复现
尝试了很多已有的Processor都没有复现,我们这使用自定义的Processor来复现,将之前的自定义AddArrayProcessor加一行代码:
java<br /> @Override<br /> public IngestDocument execute(IngestDocument ingestDocument) throws Exception {<br /> List valueList = new ArrayList<>(Arrays.asList(value.split(",")));<br /> valueList.add(valueList); // 增加的代码<br /> ingestDocument.setFieldValue(field, valueList);<br /> return ingestDocument;<br /> }<br />
重新编译再安装插件之后,执行改Processor 将会StackOverflow。
修复逻辑
这个问题的关键在于在IngestDocument的deepCopyMap的方法之前没有判断这样的无限引用的情况:
那么在此之前做一个检测就好了,这个方法在原本的ES代码中就存在:org.elasticsearch.common.util.CollectionUtils#ensureNoSelfReferences(java.lang.Object, java.lang.String) ,其利用 IdentityHashMap 记录已访问对象的引用,检测并防止对象间的循环引用。
CVE-2024-23450
https://discuss.elastic.co/t/e ... 56314
漏洞复现
虽然我们的索引只有2个Pipeline的配置,但是由于Pipeline Processor的存在,所以实际上一个文档其实能被很多Pipeline处理,当需要执行足够多个的pipline个数时,则会发生StackOverflow。
修复逻辑
这个问题的关键在于对Pipeline的个数并没有限制,添加一个配置项,当超出该个数则直接抛出异常。
java<br /> public static final int MAX_PIPELINES = Integer.parseInt(System.getProperty("es.ingest.max_pipelines", "100"));<br />
IngestDocument的org.elasticsearch.ingest.IngestDocument#executePipeline 添加逻辑:
java<br /> public void executePipeline(Pipeline pipeline, BiConsumer<IngestDocument, Exception> handler) {<br /> if (executedPipelines.size() >= MAX_PIPELINES) {<br /> handler.accept(<br /> null,<br /> new IllegalStateException(PIPELINE_TOO_MANY_ERROR_MESSAGE + MAX_PIPELINES + " nested pipelines")<br /> );<br /> } <br />
思考: 这里判断pipeline是否超出100个限制是用已经执行的pipeline个数来计算的。 假设已经超出100个pipeline,那这100个pipeline是会白跑的, 如果能在真正执行之前分析需要执行的Pipeline个数会更好。
6. 总结
Ingest 模块作为 Elasticsearch 数据处理流程的重要组成部分,提供了灵活的管道化能力,使得用户能够在数据写入前进行丰富的预处理操作。然而,在实际场景中,Ingest 模块也面临性能瓶颈、资源竞争等挑战,需要结合业务需求和系统现状进行精细化调优,通过优化 Processor 执行效率、合理规划集群架构以及增强监控与诊断手段,我们可以充分释放 Ingest 模块的能力,提升 Elasticsearch 的整体数据处理能力。
ES官方版本为什么到现在为止没有提供限流的功能?
Fred2000 回复了问题 • 2 人关注 • 1 个回复 • 2591 次浏览 • 2024-12-19 19:18
es filter script获取nested结构数据如何实现?
duanxiaobiao 回复了问题 • 2 人关注 • 1 个回复 • 4038 次浏览 • 2024-11-16 22:04
目前市面上常用的ES压测工具是啥
wu370324 回复了问题 • 3 人关注 • 2 个回复 • 4059 次浏览 • 2024-10-31 11:51
ES的_sql查询不返回长度超过ingore_above记录
kin122 回复了问题 • 2 人关注 • 1 个回复 • 4608 次浏览 • 2024-12-07 23:08
es 中keyword查询构建bitSet成本
Fred2000 回复了问题 • 2 人关注 • 1 个回复 • 5651 次浏览 • 2024-09-25 18:30
_reindex时怎样判断当目标记录中某个字段值大于原记录字段时跳过不更新?
kin122 回复了问题 • 3 人关注 • 2 个回复 • 4732 次浏览 • 2024-12-07 23:13
Elastic 宣布修改开源协议为 AGPL:Elasticsearch 再次成为开源软件
searchkit 发表了文章 • 0 个评论 • 6248 次浏览 • 2024-08-30 11:26
今日快讯!就在刚刚,开源搜索领域行业巨头 Elastic 官方博客发表了一篇最新公告《Elasticsearch is Open Source, Again》,Elastic 创始人& CTO Shay Banon 宣布 Elasticsearch 和 Kibana 许可证协议修改为 AGPL。
以下为搜索客社区从 Elastic 官方博客翻译的原文内容:
---
Elasticsearch 再次成为开源软件
[D.N.A] Elasticsearch 和 Kibana 现在可以再次被称为开源软件了。这句话让我感到无比的兴奋。真的忍不住跳起来庆祝!我们所有在 Elastic 的人都很高兴。开源精神是我的DNA,也是 Elastic 的DNA。能够再次称 Elasticsearch 为开源软件,真的是一种纯粹的快乐。
[LOVE.] 简而言之,我们将在接下来的几周内,除了 ELv2 和 SSPL 之外,增加 AGPL 作为另一个许可选项。在更改许可后,我们从未停止过像一个开源社区一样相信和行动。但通过使用 AGPL 这一获得开放源码促进会(OSI)批准的许可,消除了人们可能存在的任何疑问或混淆。
[Not Like Us] 我们在 Elastic 从未停止过对开源的信仰。我个人对开源的信仰也从未动摇,至今已有 25 年了。那么为什么三年前我们要做出改变呢?我们遇到了与 AWS 相关的问题,以及他们的服务引发的市场混乱。在尝试了所有能想到的其他选项后,我们更改了许可协议,明知道这会导致 Elasticsearch 被分叉成另一个名称并走上不同的发展轨迹。这是一个漫长的故事。
[Like That] 好消息是,虽然过程痛苦,但结果奏效了。三年后,亚马逊已经完全投入到了他们的分叉项目中,市场的混乱(大部分)得到了缓解,我们与 AWS 的合作伙伴关系比以往更强。我们甚至被评为 AWS 的年度合作伙伴。我一直希望时间能过去得足够久,以至于我们可以安全地回到开源项目的状态——现在终于到了。
[All The Stars] 我们希望尽可能简化用户的使用体验。我们有用户非常喜欢 ELv2(一个受 BSD 启发的许可)。我们也有用户已经批准使用 SSPL(通过 MongoDB 使用)。这就是为什么我们只是增加了一个选项,而不是移除任何东西。如果你已经在使用并喜欢 Elasticsearch,请继续使用,没有任何变化。对于其他人,现在你也可以选择使用 AGPL。
[LOYALTY.] 我们选择 AGPL 而不是其他许可,是因为我们希望通过与 OSI 的合作,能在开源许可领域创造更多的选项。随着我们更改许可以来的发展(例如 Grafana 从 Apache2 转移到 AGPL),也许 AGPL 已经足够适用于像我们这样的基础设施软件了。我们致力于找到最合适的解决方案。
[euphoria] 我非常高兴能够再次称 Elasticsearch 为开源软件。
[Alright] 任何改变都可能引发混淆,当然也可能引来一些网络喷子。(网络喷子总是存在的,对吧?)让我们愉快地回答一些可能出现的问题吧。我可以想象到的一些问题如下,但我们会继续补充。
“更改许可是个错误,Elastic 现在在回撤”:我们在三年前更改许可时消除了很多市场混乱。由于我们的行动,很多事情已经改变。现在是一个完全不同的市场环境。我们不生活在过去。我们希望为用户打造一个更好的未来。正是因为我们当时采取了行动,现在我们才有能力采取新的行动。
“AGPL 不是真正的开源许可,X 才是”:AGPL 是获得 OSI 批准的许可,并且被广泛采用。例如,MongoDB 曾经使用 AGPL,Grafana 现在也是 AGPL。这表明 AGPL 并不影响使用或流行度。我们选择 AGPL 是因为我们认为这是与 OSI 一起为世界开辟更多开源道路的最佳方式,而不是减少开源。
“Elastic 更改许可是因为他们表现不好”:我要说的是,我今天对 Elastic 的未来依然充满期待。我为我们的产品和团队的执行力感到无比自豪。我们发布了无状态的 Elasticsearch ES|QL 和大量用于 GenAI 用例的向量数据库/混合搜索改进。我们在日志记录和可观察性方面大力投入 OTel。我们的安全产品 SIEM 不断添加令人惊叹的功能,并且是市场上增长最快的产品之一。用户的反应让我们感到非常谦卑。股市总有起伏,但我可以向你保证,我们始终着眼于长远发展,而这次的变更就是其中的一部分。
如果我们看到更多问题,会在上面继续添加,以期减少混淆。
[HUMBLE.] 为未来而构建真是令人兴奋。Elasticsearch 回归开源。万岁!这真是一件美妙的事情。今天真是美好的一天。
Forever :elasticheart: Open Source
Shay 2024-08-30
---
原文地址:https://www.elastic.co/blog/el ... gain/
社区热评
Elasticsearch 再次回归开源的消息迅速引发了技术社区的广泛关注。这不仅是 Elastic 对自身开源信念的重申,也是其在激烈市场竞争中精心策划的一次战略调整。
三年前,Elastic 因与 AWS 的市场竞争而选择更改许可协议,这一决定在当时引发了不小的争议。尽管如此,事实证明,这一策略有效地减少了市场上的混淆,也为 Elastic 与 AWS 的合作奠定了更坚实的基础。如今,Elastic 再度选择开源,并新增 AGPL 作为许可选项,这一举措无疑展现了 Elastic 在市场中更加成熟的定位和对未来发展的自信。
这一变化不仅仅是一个公司的商业决策,更是开源生态系统的一次重要信号。Elastic 的回归开源,传递出一个明确的信息:即使在商业竞争中,开源仍然是企业实现长远发展的重要路径。随着这一决定的落地,其他软件公司可能也会重新审视自身的许可策略,推动更多开源项目的发展与创新。
此外,Elastic 选择 AGPL 作为新许可选项,也显示出其对开源生态未来走向的深刻洞察。AGPL 的引入,表明 Elastic 希望在开源社区中保持灵活性和多样性,同时推动整个行业向更加开放和透明的方向迈进。
总体而言,Elastic 重返开源的举动,不仅意在巩固其在开源社区中的地位,也为行业树立了一个新的标杆。这一事件无疑将成为开源软件发展史上的重要篇章,未来可能会激发更多企业重新考虑其开源战略,进而推动整个技术行业的进一步繁荣与进步。
让我们拭目以待!