分层存储概述Compatible with Milvus 2.6.4+
在 Milvus 中,传统的满载模式要求每个查询节点在初始化时加载段的所有数据字段和索引,甚至包括可能永远不会被访问的数据。这可确保数据的即时可用性,但往往会导致资源浪费,包括内存使用率高、磁盘活动频繁和 I/O 开销大,尤其是在处理大规模数据集时。
分层存储通过将数据缓存与分段加载解耦来应对这一挑战。Milvus 引入了一个缓存层,区分热数据(本地缓存)和冷数据(远程存储),而不是一次性加载所有数据。现在,QueryNode 最初只加载轻量级元数据,并根据需求动态拉取或驱逐字段数据。这大大缩短了加载时间,优化了本地资源利用率,并使查询节点能够处理远远超出其物理内存或磁盘容量的数据集。
在以下情况下,请考虑启用分层存储:
超过单个 QueryNode 可用内存或 NVMe 容量的集合
加载速度比首次查询延迟更重要的分析或批处理工作负载
对于访问频率较低的数据,可容忍偶尔缓存缺失的混合工作负载
元数据包括 Schema、索引定义、块映射、行计数和远程对象引用。这类数据较小,始终处于缓存状态,且永不被驱逐。
有关段和块的更多详情,请参阅段。
如何工作
分层存储改变了 QueryNode 管理段数据的方式。QueryNode 现在不再在加载时缓存每个字段和索引,而是只加载元数据,并使用缓存层动态获取和驱逐数据。
满载模式与分层存储模式的比较
虽然满载模式和分层存储模式处理的数据相同,但它们在 QueryNode 缓存这些组件的时间和方式上有所不同。
满载模式:在加载时,QueryNode 从对象存储中缓存完整的 Collections 数据,包括元数据、字段数据和索引。
分层存储模式:加载时,QueryNode 只缓存元数据。字段数据以块为粒度按需提取。索引文件保持远程状态,直到第一次查询需要它们;然后获取并缓存整个分段索引。
下图显示了这些差异。
全加载模式与分层存储模式
查询节点加载工作流
在分层存储模式下,工作流程分为以下几个阶段:
查询节点加载工作流程
阶段 1:懒加载
初始化时,Milvus 执行懒加载,只缓存段级元数据,如 Schema 定义、索引信息和块映射。
在此阶段不会缓存实际字段数据或索引文件。这样,Collections 几乎可以在启动后立即开始查询,同时将内存和磁盘消耗降到最低。
由于字段数据和索引文件在首次访问前一直保存在远程存储中,因此首次查询可能会出现额外的延迟,因为必须按需获取所需数据。为减轻关键字段或索引的这种影响,可以使用预热策略,在段可查询前主动预加载它们。
配置
启用分层存储时自动应用。无需手动设置。
第 2 阶段:预热
为减少懒加载带来的首次命中延迟,Milvus 提供了预热机制。
在段可查询之前,Milvus 可以主动从对象存储中获取并缓存特定字段或索引,确保首次查询直接命中缓存数据,而不是触发按需加载。
预热期间,字段将在块级别预加载,而索引将在段级别预加载。
配置
预热设置在milvus.yaml 的 "分层存储 "部分中定义。可以为每个字段或索引类型启用或禁用预加载,并指定首选策略。有关详细配置,请参阅预热。
第 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
下一步
配置预热- 针对访问模式优化预加载。请参阅 "预热"。
调整驱逐- 针对资源限制设置适当的水印和 TTL。请参阅 "驱逐"。
监控性能- 跟踪缓存命中率、驱逐频率和查询延迟模式。
迭代配置- 根据观察到的工作负载特征调整设置。
常见问题
能否在运行时更改分层存储参数?
所有参数都必须在启动 Milvus 之前在milvus.yaml 中设置。更改需要重新启动才能生效。
分层存储会影响数据持久性吗?
不会。数据持久性仍由远程对象存储处理。分层存储只管理查询节点上的缓存。
使用分层存储后,查询速度是否总是更快?
不一定。分层存储减少了加载时间和资源使用量,但接触未缓存(冷)数据的查询可能会出现更高的延迟。对于对延迟敏感的工作负载,建议使用满载模式。
为什么即使启用了分层存储,查询节点仍然会耗尽资源?
两种常见原因:
查询节点配置的资源太少。水印是相对于可用资源而言的,因此配置不足会扩大误判。
QueryNode 资源与其他工作负载共享,因此分层存储无法正确评估实际可用容量。
要解决这个问题,我们建议您为查询节点分配专用资源。
为什么有些查询在高并发情况下会失败?
如果有太多查询同时访问热数据,查询节点资源限制仍可能会被超出。一些线程可能会因资源预留超时而失败。在负载减少后重试或分配更多资源可以解决这个问题。
启用分层存储后,为什么搜索/查询延迟会增加?
可能的原因包括
频繁查询冷数据,而冷数据必须从存储中获取。
水印设置得太近,导致频繁同步驱逐。