分层存储概述Compatible with Milvus 2.6.4+
在 Milvus 中,传统的满载模式要求每个查询节点在初始化时加载段的所有数据字段和索引,甚至包括可能永远不会被访问的数据。这可确保数据的即时可用性,但往往会导致资源浪费,包括内存使用率高、磁盘活动频繁以及 I/O 开销大,尤其是在处理大规模数据集时。
分层存储通过将数据缓存与分段加载解耦来应对这一挑战。现在,QueryNode 不再一次性加载所有数据,而是最初只加载轻量级元数据,并根据需求动态提取或驱逐 Field 数据。这大大缩短了加载时间,优化了本地资源利用率,并使查询节点能够处理远远超出其物理内存或磁盘容量的数据集。
在以下情况下,请考虑启用分层存储:
超过单个 QueryNode 可用内存或 NVMe 容量的集合
加载速度比首次查询延迟更重要的分析或批处理工作负载
对于访问频率较低的数据,可容忍偶尔缓存缺失的混合工作负载
元数据包括 Schema、索引定义、块映射、行计数和远程对象引用。这类数据较小,始终处于缓存状态,且永不被驱逐。
有关段和块的更多详情,请参阅段。
如何工作
分层存储改变了 QueryNode 管理段数据的方式。QueryNode 现在不再在加载时缓存每个字段和索引,而是只加载元数据,并使用缓存层动态获取和驱逐数据。
满载模式与分层存储模式的比较
虽然满载模式和分层存储模式处理的数据相同,但它们在 QueryNode 缓存这些组件的时间和方式上有所不同。
满载模式:在加载时,QueryNode 从对象存储中缓存完整的 Collections 数据,包括元数据、字段数据和索引。
分层存储模式:加载时,QueryNode 只缓存元数据。字段数据以块为粒度按需提取。索引文件保持远程状态,直到第一次查询需要它们;然后获取并缓存整个分段索引。
下图显示了这些差异。
全加载模式与分层存储模式
查询节点加载工作流程
在分层存储模式下,分层存储的工作流程分为以下几个阶段:
查询节点加载工作流程
阶段 1:懒加载
初始化时,Milvus 执行懒加载,只缓存段级元数据,如 Schema 定义、索引信息和块映射。
在此阶段不会缓存实际字段数据或索引文件。这样,Collections 几乎可以在启动后立即开始查询,同时将内存和磁盘消耗降到最低。
由于字段数据和索引文件在首次访问前一直保存在远程存储中,因此首次查询可能会出现额外的延迟,因为必须按需获取所需的数据。为减轻关键字段或索引的这种影响,可以使用预热策略,在段可查询前主动预加载它们。
配置
启用分层存储时自动应用。无需手动设置。
第 2 阶段:预热
为减少懒加载带来的首次命中延迟,Milvus 提供了预热机制。
在段可查询之前,Milvus 可以主动从对象存储中获取并缓存特定字段或索引,确保首次查询直接命中缓存数据,而不是触发按需加载。
预热期间,字段将在块级别预加载,而索引将在段级别预加载。
配置
预热可在三个级别进行配置:
群集级:在
milvus.yaml中定义适用于所有 Collections 的默认值。Collections 级别:使用 SDK 方法(
create_collection,alter_collection_properties)覆盖特定 Collection 的集群默认值。字段/索引级别:使用 SDK 方法微调单个字段或索引 (
add_field,alter_collection_field,add_index,alter_index_properties)。
高级设置覆盖低级设置(字段/索引 > Collections > 群集)。有关详细配置,请参见预热。
第 3 阶段:部分加载
一旦开始查询或搜索,查询节点就会执行部分加载,只从对象存储中获取所需的数据块或索引文件。
字段:按需加载数据块。只获取符合当前查询条件的数据块,从而最大限度地减少 I/O 和内存使用。
索引:在段级别按需加载。索引文件必须作为完整单元获取,不能分割成块。
配置
启用分层存储时,会自动应用部分加载。无需手动设置。要尽量减少关键数据的首次命中延迟,请与预热结合使用。
第 4 阶段:驱逐
为保持健康的资源使用,当达到特定阈值时,Milvus 会自动释放未使用的缓存数据。
驱逐遵循 "最近最少使用"(LRU)策略,确保不常访问的数据首先被删除,而活动数据仍保留在缓存中。
驱逐受以下可配置项的制约:
水印:定义触发和停止驱逐的内存或磁盘阈值。
缓存 TTL:在规定的不活动时间后删除过时的缓存数据。
配置
在Milvus.yaml 中启用和调整驱逐参数。有关详细配置,请参阅 "驱逐"。
开始使用
先决条件
Milvus 2.6.4 及以上版本
具有专用内存和磁盘资源的查询节点
对象存储后端(S3、MinIO 等)
QueryNode 资源不应与其他工作负载共享。共享资源会导致 Tiered Storage 错误判断可用容量,从而导致崩溃。
基本配置模板
编辑 Milvus 配置文件 (milvus.yaml) 配置群集级 Tiered Storage 设置:
# milvus.yaml
queryNode:
segcore:
tieredStorage:
# Warm Up Configuration
warmup:
scalarField: sync # Preload scalar field data
scalarIndex: sync # Preload scalar indexes
vectorField: disable # Don't preload vector field data (large)
vectorIndex: sync # Preload vector indexes
# Eviction Configuration
evictionEnabled: true
backgroundEvictionEnabled: true
# Memory Watermarks
memoryLowWatermarkRatio: 0.75 # Stop evicting at 75%
memoryHighWatermarkRatio: 0.80 # Start evicting at 80%
# Disk Watermarks
diskLowWatermarkRatio: 0.75
diskHighWatermarkRatio: 0.80
# Cache TTL (7 days)
cacheTtl: 604800
此模板定义群集级默认设置。您可以使用 SDK 覆盖特定 Collections 或单个字段/索引的预热设置。有关详细信息,请参阅预热。
下一步
配置预热 - 针对访问模式优化预加载。请参阅预热。
调整驱逐- 针对资源限制设置适当的水印和 TTL。请参阅 "驱逐"。
监控性能- 跟踪缓存命中率、驱逐频率和查询延迟模式。
迭代配置- 根据观察到的工作负载特征调整设置。
常见问题
能否在运行时更改分层存储参数?
这取决于参数类型:
预热设置:可以在加载 Collections 之前通过 SDK 配置 Collection 级和字段/索引级预热。一旦加载了 Collections,必须先释放它,更改设置,然后重新加载。
驱逐和水印设置:这些必须在启动 Milvus 之前在
milvus.yaml中设置。更改需要重新启动才能生效。
分层存储会影响数据持久性吗?
不会。数据持久性仍由远程对象存储处理。分层存储只管理查询节点上的缓存。
使用分层存储后,查询速度是否总是更快?
不一定。分层存储缩短了加载时间并减少了资源使用量,但接触未缓存(冷)数据的查询可能会出现更高的延迟。对于对延迟敏感的工作负载,建议使用满载模式。
为什么即使启用了分层存储,查询节点仍然会出现资源耗尽的情况?
两种常见原因:
查询节点配置的资源太少。水印是相对于可用资源而言的,因此配置不足会扩大误判。
QueryNode 资源与其他工作负载共享,因此分层存储无法正确评估实际可用容量。
要解决这个问题,我们建议您为查询节点分配专用资源。
为什么有些查询在高并发情况下会失败?
如果有太多查询同时访问热数据,查询节点资源限制仍可能会被超出。一些线程可能会因资源预留超时而失败。在负载减少后重试或分配更多资源可以解决这个问题。
启用分层存储后,为什么搜索/查询延迟会增加?
可能的原因包括
频繁查询冷数据,而冷数据必须从存储中获取。
水印设置得太近,导致频繁同步驱逐。