作者:来自 Elastic Valentin Crettaz

dense_vector 使用的是你的堆内存图表无法显示的堆外内存。AutoOps 能够在向量内存压力导致 OOM 之前检测到内存压力。
AutoOps 现在会在 dense_vector 堆外内存占用 、堆内存压力 和 运行压力 同时集中在同一个 Elasticsearch 节点时,触发 Vector memory pressure(向量内存压力) 洞察。
我们在一台 4 GiB 节点上进行了持续的 k 最近邻( kNN )写入测试。结果显示,当堆内存使用率约为 75%,并伴随线程池压力时,该洞察就会触发,大约比节点达到饱和提前一个小时发出预警。而在那个时间点,仅查看堆内存图表仍然会认为内存使用处于中等水平。
用于 kNN 的 dense_vector 存储在 Java 堆外,因此堆内存监控和断路器( circuit breaker )都无法反映完整的向量内存使用情况。下面我们将介绍该洞察监测的内容,以及为什么仅依赖堆内存无法发现这一问题。同时,我们还会讨论当该洞察触发时应该采取哪些措施。
为什么 dense_vector 会产生堆外内存压力,而堆内存图表无法发现
语义搜索和 kNN 依赖于 dense_vector 字段。Elasticsearch 将其中很大一部分数据存储在堆外内存中。这与 Java Virtual Machine( JVM )的运行方式有关,但与堆内存使用并不是一回事。
在生产环境中,堆内存与堆外内存的差异通常表现为以下模式:
-
堆内存在数周内一直看起来很正常,而 dense_vector 的堆外内存占用却在悄悄增长。
-
堆内存断路器始终没有触发,或者直到很晚才出现峰值,因为压力发生在 JVM 之外。
-
kNN 查询和批量写入速度开始下降,队列不断堆积,但仪表板上没有任何指标表明问题来自向量内存。
堆内存限制只能保护 Java 内存分配。它无法告诉你,向量堆外内存占用是否仍然能够容纳在部署实际可用的 RAM 范围内。
AutoOps 已经能够广泛监控集群健康状况,而 Vector memory pressure 则在内存和负载信号同时出现时,为向量负载较高的节点提供更有针对性的监测。
AutoOps 如何测量向量内存、堆内存和剩余空间
AutoOps 使用与你在 Stack Monitoring 中相同的节点统计指标。
对于每个节点,它会计算并跟踪以下三个派生指标:
| 符号 | 含义 | 数据来源(典型) | 图表(见下文) |
|---|---|---|---|
| V | 向量堆外内存占用 | indices.dense_vector.off_heap.total_size_bytes |
第一张图中的绿色曲线 |
| A | 产品视图中的可用 RAM | os.mem.total_in_bytes 与 os.mem.used_in_bytes 的差值 |
第二张图中的绿色曲线 |
| H | 剩余空间( Headroom ) | A − V ( headroom_bytes ) |
第一张图中的蓝色曲线 |
当 H > 0 时,表示系统仍有可用余量:向量内存占用仍然能够轻松容纳在当前内存范围内。
当 H ≤ 0 时,表示系统进入了压缩( compression )状态:向量内存占用( V )已经达到或超过了 AutoOps 根据遥测数据计算出的可用 RAM( A )。
在较小规格的节点上,这种情况在高负载下较为常见。
该洞察关注的是:
-
向量堆外内存占用的增长趋势。
-
持续增长的模式。
-
与之相互印证的系统压力信号。
而不是仅依据某一次 H ≤ 0 的快照就发出警报。
AutoOps 还会跟踪一个压缩状态标志( compression regime flag ) ,用于统计最近一段时间内 H ≤ 0 的采样比例,因此短暂出现的波动不会主导整体判断(见下方第三张图)。

向量内存压力检测如何工作:扩张( Expansion )与压缩( Compression )
Vector memory pressure(向量内存压力) 是一个 HIGH 严重级别的 AutoOps 事件,它会根据当前是否处于压缩状态( compression regime )自适应调整检测逻辑:
-
扩张( H > 0 ): 重点关注剩余空间( Headroom )持续减少,即向量堆外内存占用不断增长。
-
压缩( H ≤ 0 ): 重点关注 ΔV(向量内存增量)、堆内存状态、 I/O 和延迟。当剩余空间已经耗尽时,"距离 H 降至零还有多少小时"已经不是主要关注点。
该检测器必须同时满足以下三个层面的条件才会触发:
-
内存信号: 进入压缩状态、剩余空间持续减少,或向量堆外内存占用持续增长。
-
运行信号: 搜索或索引延迟相对于滚动基线增加、文件系统读取压力(需与延迟或堆内存压力同时出现)、索引限流、线程池队列积压或拒绝、 segment 持续增长,或者与其他压力信号同时出现的堆内存断路器。仅凭断路器本身不足以升级为告警,必须有其他压力信号相互印证。
-
堆内存升温: 与该节点过去 24 小时滚动中位数相比,堆内存使用率明显升高。因此,仅仅进入压缩状态,而堆内存仍然平稳时,不会触发该洞察。
这种组合是有意设计的。
只有向量内存压力而没有运行负载,可能只是容量规划问题;只有运行负载而没有向量内存压力,则可能是其他根本原因。
只有当向量内存 、运行压力 和堆内存压力三者同时出现时,系统才会识别出真正的向量 RAM 问题,也就是说,仅在节点真正遇到问题时才会触发洞察,而不是在每一次进入压缩状态但堆内存仍然正常时都发出告警。
验证:4 GiB 节点在 kNN 负载下的内存压力检测
我们在 4 GiB 的 Elastic Cloud Hosted 部署上,对向量内存压力检测进行了压力测试,测试条件包括受限速的 dense_vector 写入(约每分钟 2,000 个文档)以及稳定的 kNN 查询负载(约每秒 8 次查询)。
针对 HNSW 、 BBQ HNSW 和 DiskBBQ 三种映射配置,在持续 24--48 小时的测试中,我们观察到以下结果:
-
在两次 HNSW 测试中,向量堆外内存占用从接近零增长到约 6 GiB(已索引超过 470 万个向量),这是资源最紧张的测试场景。
-
在整个测试过程中,大部分时间节点都处于压缩状态( H ≤ 0 ),这是预期结果,因为在该模型下,向量内存占用超过了节点的总 RAM。
-
即使已经进入压缩状态,并且线程池压力持续增加,只要堆内存保持在约 50%, Vector memory pressure 都不会触发。
-
在 HNSW 和 BBQ HNSW 两种测试中,当堆内存使用率超过约 75%,同时伴随内存压缩和线程池队列压力时,该洞察才会触发,时间大约比堆内存接近饱和提前一个小时。
-
在同一时间窗口内,还出现了节点内存不足( OOM )、断路器触发,以及搜索和索引性能下降等现象。
正如下方仪表板所示,在测试运行接近尾声时,由于多个压力信号相互印证,系统性能出现了明显下降。

- 在 DiskBBQ 测试中,虽然节点长期处于压缩状态,但由于堆内存始终保持正常,因此 Vector memory pressure 并未触发,因为此时真正的限制因素是存储,而不是内存。对于这种配置,应重点关注磁盘和水位线( watermark )相关信号。
正如下方截图所示,即使我们在相同规格的实例上写入了超过 7,000 万个向量并填满了磁盘,所有监测指标依然保持稳定,整个测试过程中性能始终保持恒定。

这样设计的意义就在于:运维人员获得的是基于向量的内存视角,并且与真实的 RAM 压力及各个子系统的运行状态相关联,而不是在每个进入压缩状态的索引上都收到告警,或者等到节点已经全面承压、堆内存图表变红之后才发现问题。
当 AutoOps 检测到向量内存压力时应该怎么做
下面是 AutoOps 在检测到 Vector memory pressure(向量内存压力) 时触发的洞察:

产品中的建议对应以下具体操作:
-
在允许的精度范围内减少向量占用: 减少向量维度、使用量化( quantized )映射、归档或拆分索引,或使用更精简的映射重新索引。
-
优化 kNN 负载: 降低
num_candidates、减少并发查询速率,并尽可能缩小带过滤条件的 kNN 查询范围。 -
考虑使用 DiskBBQ: 当内存中的 HNSW 成为瓶颈时,可考虑使用 DiskBBQ(需根据你的使用场景评估召回率和延迟之间的权衡)。如果你已经在使用 DiskBBQ,并且堆内存保持稳定,那么应将磁盘和水位线( watermark )相关洞察作为主要监控信号。请注意,DiskBBQ 需要 Enterprise 许可证。
-
合理增加 RAM 容量: 当向量堆外内存占用( V )持续增长,而剩余空间( Headroom )始终不足时,应考虑扩充 RAM。
AutoOps 会链接到受影响的节点,并以通俗易懂的语言总结当前所处的状态( regime )及相关压力信号。你可以将其理解为:"现在就采取行动,而不是等到所有图表都变红以后再处理。"
哪些环境支持 AutoOps 向量内存压力监控
只要 AutoOps 支持 Elasticsearch 9.2 及以上版本, Vector memory pressure(向量内存压力) 功能即可使用,包括:
-
Elastic Cloud Hosted( ECH )。
-
Elastic Cloud Serverless(即将推出)。
-
通过 Cloud Connect 实现的自托管部署。
AutoOps 已包含在所有支持的部署类型和订阅级别中。在 ECH 上使用该功能不会消耗 ECU。
常见问题
为什么我的堆内存图表看起来仍然正常,而 AutoOps 却报告了向量内存压力?
该洞察是在堆内存相对于该节点过去 24 小时自身基线变热时触发,而不仅仅是在堆内存达到 99% 时才触发。
用于 kNN 的 dense_vector 使用的是堆外内存,因此 JVM 堆内存图表和堆内存断路器都无法反映完整的向量内存占用。
AutoOps 会将向量堆外内存占用与可用 RAM 进行比较,并要求同时存在运行压力(如延迟、队列积压和限流),同时等待堆内存升温后才触发。因此,当 RAM 压力逐渐增大时,你会获得一个针对向量的专用信号,而不会因为仅仅进入压缩状态就产生堆内存正常情况下的误报。
在小规格节点上,负剩余空间( H ≤ 0 )意味着什么?
这表示在 AutoOps 的计算中,向量内存占用已经达到或超过了可用 RAM。
对于较小规格的节点,在持续写入过程中出现这种情况并不少见。
仅仅进入压缩状态并不会触发该洞察。AutoOps 更关注以下几个方面:
-
当前情况是否持续恶化。
-
堆内存是否高于历史基线。
-
搜索、索引或线程池是否已经出现压力。
它与断路器( circuit breaker )触发有什么区别?
断路器用于保护与堆内存相关的操作。
而 Vector memory pressure 关注的是堆外向量数据,并结合内存紧张程度以及其他遥测信号(如延迟、队列积压、限流、 I/O 和堆内存压力)进行综合判断。
断路器可能会在较晚阶段才触发,也可能与向量内存压力同时出现。而该洞察则能够在同一危机窗口中揭示向量 RAM 问题,并且通常会早于 OOM 和线程池故障级联发生。
哪些工作负载最容易触发该洞察?
以下场景最容易触发:
-
大量
dense_vector写入。 -
较大的
num_candidateskNN 查询。 -
在内存受限节点上同时发生以上两种情况。
验证测试采用的是持续批量写入结合稳定的 kNN 查询,而实际生产环境中的负载组合可能有所不同。
对于更大规格的节点,在进入压缩状态之前,可能会先经历一段扩张状态( Expansion )下的剩余空间缓冲期。
对于主要使用 DiskBBQ 且堆内存保持正常的节点,则较少触发该洞察。
当该洞察触发时,我首先应该做什么?
首先关注洞察中关于状态( regime )和子系统( subsystem )的说明。
如果向量内存占用持续增长,同时线程池或延迟已经升高,应降低 kNN 或批量写入负载,并规划减少向量占用或扩充 RAM。
如果堆内存也已经升高,则应同时处理向量容量和 JVM 容量问题,而不要认为只查看某一张图表就能反映全部情况。
原文:Vector memory pressure: AutoOps off-heap detection | Elasticsearch Labs