阅读,与值得关注的内容Readance

Milvus 3.0 外表能力解读:如何让数据留在S3与湖上 ,Milvus 以只读模式检索

图片在不少 AI 系统中,embedding 和元数据会直接保存在数据湖中,由上游 Pipeline 负责生成和更新,并在数据平台统一进行版本管理。例如,商品数据处理时,通常会把商品属性和多模态 embedding 写成 Parquet 文件存到 S3;大模型检索或训练的语料,也可能直接维护在 Iceberg 或 Lance 表中。
但传统向量数据库数据,往往需要将数据存储在本地,构建一份专门用于在线业务的副本,才能完成高效检索。
这时候,如果想对已经存在数据湖里的文件做高性能向量检索,通常有两个办法:
  • 建设一个ETLPipeline,把数据复制进向量数据库。这样做的优点是可以使用灵活的向量索引,并保证在线检索的高性能,代价则是数据的多副本存储。以及后续每次商品目录变化、embedding 模型升级,字段补写,都要触发的数据同步工作。
  • 直接查询数据湖。优点是无需复制数据;缺点是直接查询湖中的文件时,通常无法直接利用向量数据库为在线检索构建的 HNSW 等 ANN 索引,因此可能退化为大范围甚至全量扫描,难以满足生产环境的低延迟要求。
Milvus 3.0 的External Collection带来了第三种选择。
借助这一能力,我们可以让源数据继续留在 Parquet、Iceberg、Lance、Vortex 或其他受支持的外部格式中。Milvus 只管理指向外部数据的引用、字段映射,以及为检索构建的索引、manifest 等服务状态。主要包括:
  • external_source,用于标识外部文件或数据表;
  • external_spec,用于描述源数据格式和存储访问方式;
  • external_field映射,用于把 Milvus schema 中的字段对应到外部数据集中的列;
  • Milvus 为检索构建的索引、manifest 和服务状态。
如此一来,使用时,只需要将外部字段映射到 Milvus schema,定义所需索引并执行 Refresh;完成 Load 后,即可继续使用 Milvus 的 search 和 query API。
需要注意的是,并非每次查询都需要从对象存储重新读取数据。索引和高频访问的热数据可以缓存在本地,从而降低远程读取对查询延迟的影响。
这样一来,在线检索不必再创建一份独立的源数据副本,同时仍可以通过 Milvus 的索引和查询引擎提供低延迟检索;Spark、训练流程、评测任务和治理工具仍旧在原本的位置工作,不同工作负载可以建立在同一份数据湖数据之上。

01 

External Collection如何支持在线离线多种负载,减少数据拷贝?

对于一个数据团队来说,External Collection的第一层价值在于存储优化,避免团队将几TB的数据在不同Pipeline中重复搬运与存储。
但更深一层的价值则在于,它以非常低的成本保证了不同ipeline中的数据能够保持一致。
依旧是商品目录这个例子。过去,数据平台会产出 Parquet 原始数据,然后一条 ETL 作业转换后灌进向量库形成副本一。接着,推荐团队做离线召回分析,又导一份进自己的 Spark 集群,形成副本二。然后,模型迭代要换 embedding,于是重算、回灌,每份副本都得跟着更新。
每多一个消费方,就多一份拷贝、多一条同步链路、多一处可能对不上的账。为了让这些数据保持同步和一致,团队需要付出大量的工程、协调和机会成本。
但在 External Collection 上,原始数据始终只有一份。Milvus 可以通过 External Collection 为商品搜索、推荐或 Agent 检索提供在线服务。与此同时,其他系统可以直接处理数据湖中的同一份数据:
Spark 可以识别重复商品;
  • 训练流程可以使用新模型生成 embedding;
  • 数据质量任务可以发现格式错误或异常记录;
  • 评测流程可以比较不同模型版本的检索质量;
  • 批处理任务可以生成摘要、标签或新的元数据。
离线处理可以把改进后的数据或新字段重新写回数据湖。下一次 Refresh 完成后,更新后的数据版本即可对 Milvus 查询可见。不再需要单独维护一套 export / import 流程,专门为了在线服务再重建一份数据副本。
治理边界也仍然清晰。Milvus 继续负责 Collection 级的访问控制,并管理访问外部存储所需的身份与凭证;源数据本身的版本、血缘和所有权则仍由数据湖平台管理。

02 

External Collection 的数据源支持与安全机制

目前,External Collection已支持 AWS S3、阿里云 OSS、Google Cloud Storage(GCS) 等主流云厂商,以及 MinIO 等 S3 兼容的自建存储。
格式上,External Collection 首批支持以下主流的开放格式与数据源:
实际应用中,你给数据湖选的存储格式,会一路影响线上检索的延迟和成本。通常来说,Lance、Vortex 这类现代列存随机读更快、更省,
考虑到不同Pipeline中数据管理方式的差异,External Collection 还支持字段映射(external field mapping)。例如,源数据中的product_id可以映射为 Milvus 中的id字段,image_vec可以映射为embedding。如果源表字段很多,也不需要把所有列都暴露给 Collection。这意味着数据平台不必为了适配在线服务数据库,就去重命名或重写自己的源数据。
另外,针对 Iceberg、Milvus 快照这类带版本的数据源,External Collection还能指定某个历史快照来建表或刷新。这样你就可以检索「上周二那个版本」的数据,做可复现的评测、回归对比或审计。不必担心做评测时,数据库中的源数据发生更新,影响最终评测效果。与此同时,底层文件也可以在同一时间,也可以继续被数据技术栈中的其他系统使用。
最后,关于数据访问安全问题。External Collection 支持基于 IAM Role 的访问方式以及跨账号 STS AssumeRole,可将身份认证交由云厂商的身份体系管理,避免在配置中长期保存静态 Access Key。(注:这里的存储身份决定的是 Milvus 如何访问源数据。Milvus 内部的授权仍然属于另一个独立的安全边界。)

03 

如何创建、建立索引、Refresh 和查询 External Collection

External Collection 的生命周期主要有四个步骤:
  • 定义外部数据源,并把其中的列映射到 Milvus schema;
  • 定义当前工作负载需要的索引;
  • 执行 Refresh,让 Milvus 发现源数据并准备一个可查询的数据版本;
  • Load Collection,然后照常使用 Milvus 的 search 和 query API。
下面继续以商品目录为例,把它表示成一个 External Collection:
import jsonimport timefrom pymilvus import DataTypeMilvusClientclient = MilvusClient(    uri="http://localhost:19530",    token="root:Milvus",)schema = client.create_schema(    external_source="s3://my-lake/datasets/products/",    external_spec=json.dumps(        {            "format""parquet",            "extfs": {                "cloud_provider""aws",                "region""us-east-1",                "use_iam""true",                "iam_endpoint""https://sts.us-east-1.amazonaws.com",            },        }    ),)schema.add_field(    field_name="id",    datatype=DataType.INT64,    external_field="product_id",)schema.add_field(    field_name="embedding",    datatype=DataType.FLOAT_VECTOR,    dim=768,    external_field="image_vec",)schema.add_field(    field_name="title",    datatype=DataType.VARCHAR,    max_length=256,    external_field="product_name",)schema.add_field(    field_name="stock",    datatype=DataType.INT64,    external_field="stock",)schema.add_field(    field_name="rating",    datatype=DataType.FLOAT,    external_field="rating",)client.create_collection(    collection_name="products_ext",    schema=schema,)
索引使用普通的 Milvus 接口:
index_params = client.prepare_index_params()index_params.add_index(    field_name="embedding",    index_type="HNSW",    metric_type="COSINE",)index_params.add_index(field_name="stock", index_type="AUTOINDEX")index_params.add_index(field_name="rating", index_type="AUTOINDEX")client.create_index(    collection_name="products_ext",    index_params=index_params,)
然后对外部数据源执行 Refresh:
job_id = client.refresh_external_collection(    collection_name="products_ext",)while True:    progress = client.get_refresh_external_collection_progress(job_id=job_id)    if progress.state == "RefreshCompleted":        break    if progress.state == "RefreshFailed":        raise RuntimeError(progress.reason)    time.sleep(2)
Refresh 完成后,就可以像普通 Milvus Collection 一样 Load 并进行检索:
client.load_collection("products_ext")results = client.search(    collection_name="products_ext",    data=[query_vec],    anns_field="embedding",    filter="stock > 0 and rating >= 4.0",    limit=10,    output_fields=["id""title""stock""rating"],)
比普通的Milvus Collection, search 调用本身的变化不大,区别主要在于数据的整个生命周期从哪里开始。由 Milvus 管理的 Collection,从数据写入或导入 Milvus 开始;External Collection 则从引用一份已经存在于外部的数据开始。

04 

Refresh是如何获取外部数据变化的?

从 Milvus 这一侧看,External Collection 是只读的,但底层的数据湖数据集并不需要一直保持不变。
假设商品数据处理流程新增了一批数据、更新了元数据,或者写入了由新模型生成的 embedding。Milvus 不会持续跟踪源路径中出现的每个对象。这些变化需要通过Refresh才会变得可见。
Refresh 会读取外部元数据,解析源数据 fragment,更新把这些 fragment 关联到 Milvus Collection 的 manifest,并准备相应的索引状态。
整个过程可以增量完成:Milvus 会识别没有发生变化的源 fragment,并复用它们已有的 segment 和索引结果。只有新增或发生变化的 fragment 才需要重新处理。因此,即使是一份数 TB 的数据集,只改动其中一小部分,也不需要重新做一次完整的数据导入和完整的索引重建。
Refresh 还为在线服务提供了一个明确的数据版本边界。在新版本准备期间,查询仍然使用上一个已经发布的版本。等 Refresh 完成后,新版本才会作为一个完整版本对外可见,而不会让查询看到旧数据和部分准备完成的新数据混在一起。
这种模式很适合按小时构建的商品目录、每天夜间更新的知识库、周期性的 embedding 更新、模型生成特征的处理流程,以及类似的批处理工作负载。

05 

Lazy Loading :如何在内存中只定向加载需要字段的数据

如果在线服务层在回答查询前仍然需要把所有数据加载到本地,那么把源数据记录留在对象存储中也起不到多少作用。
但启用 Milvus Tiered Storage 后,Collection Load 时,QueryNode 一开始可以只保留一些轻量级元数据,例如 schema 信息、索引定义、chunk map,以及指向远程对象的引用。字段数据只有在查询需要时,才会按 chunk 从远端读取;索引也可以一直留在远端,第一次使用时再加载并缓存到本地。经常使用的数据会保持热状态,不常使用的数据则可以被淘汰。
这一点对于字段很多的 AI 数据集来说非常有价值。一条商品记录可能包含多个 embedding、长文本描述、原始 JSON、图片元数据、自动生成的摘要、库存、价格、评分,以及许多其他属性。一次典型的相似度搜索,可能只会用到一个向量字段,再加上库存、价格和评分。没有理由仅仅因为其他字段也属于同一条记录,就让它们长期占用在线服务的内存。
总的来说,External Collection 可以从两个层面缩小在线服务侧的数据占用:
第一层是 schema 级投影通过external_field,External Collection 可以只暴露应用需要的源数据列。其他列继续留在数据湖中,不进入当前的服务 schema。
第二层是运行时投影。启用 Tiered Storage 后,QueryNode 可以只读取并缓存实际工作负载需要的字段和索引,而不是在 Load 阶段一次性加载所有已映射数据。
这里有一个明确的取舍。如果查询访问了尚未缓存的冷字段或冷索引,第一次访问时可能会产生远程读取的额外开销。可以通过 warm-up 策略预先加载对延迟敏感的字段或索引,同时利用缓存和淘汰策略,避免低频使用的数据长期占用本地资源。
当然,我们的重点并不是要让对象存储表现得像内存一样,而是让内存和本地磁盘围绕检索工作负载真正访问的工作集(working set)来配置,而不是按照源数据集的总规模和总宽度来配置。
而针对不同源数据格式,在按需访问时可能表现出的不同 I/O 特征。External Collection 并不会消除这些存储层面的差异;它只是让 Milvus 可以在这些数据格式之上建立检索层。

06 

External Collection 支持哪些搜索和索引能力

External Collection 并不是简单地让 Milvus 指向一个 embedding 文件目录,然后扫描其中的数据。
生产环境中的搜索结果很少只取决于向量相似度。它还可能取决于精确关键词、访问权限、库存、时间戳、类别、价格、来源质量或业务排序信号。
因此,Milvus 会在外部数据上构建检索结构,并通过标准的检索引擎执行查询。根据字段和工作负载,Milvus 可以构建:
  • 用于 ANN 搜索的向量索引;
  • 用于元数据过滤的标量索引;
  • 用于半结构化属性的 JSON 索引;
  • 用于词法检索的 BM25 和全文索引;
  • Milvus 数据模型支持的 Function 生成字段。
最终支持包括向量检索、关键词、全文检索、标量过滤、混合检索与排序在内的完整生产级检索能力。
更广义地说,External Collection 为存放在数据湖中的数据提供了一条数据库级检索路径,而不只是提供了一种从文件中读取向量的方法。

07 

External Collection 适合什么场景,又不适合什么场景

External Collection并不能替代流式写入路径。它更适合下面这些情况:
  • 权威数据已经存放在 Parquet、Vortex、Lance、Iceberg 或其他受支持的外部数据源中;
  • 数据集主要通过批处理生成,而不是高频事务写入;
  • 维护第二份服务副本会带来明显的 ETL、数据新鲜度或治理成本;
  • 多个系统需要使用同一份开放数据;
  • 可以接受以明确的 Refresh 边界来控制在线数据的新鲜度;
  • 希望使用 Milvus 的生产级检索能力,但不希望让 Milvus 接管源数据记录。
下面这些情况,普通 Milvus Collection 仍然更合适:
应用会持续 insert 或 upsert 数据;
delete 需要通过在线写入路径及时生效;
工作负载依赖 External schema 尚不支持的 Collection 能力;
在线服务设计本来就希望把所有需要的数据常驻内存,以避免远程 cache miss。
普通 Milvus Collection 与 External Collection 的主要区别如下:
另外,还有几个边界需要注意:
  • External Collection 是只读的。源数据的修改发生在 Milvus 之外。
  • 零复制指的是源数据记录。索引、manifest、缓存和计算资源仍然需要成本。
  • Refresh 需要显式执行。它不是流式同步机制。
  • 外部数据源必须保持可访问。Search、索引构建和 Refresh 仍然依赖存储访问权限和凭证。
  • 需要 Storage V3。在开源 Milvus 3.0 中,使用 External Collection 前必须先启用 Storage V3。
  • External Collection 不会替代上游处理。Embedding 生成、聚类、去重和数据清洗,仍然由相应的上游系统完成。
总的来说,一个系统可以对快速变化的在线数据使用普通 Milvus Collection,同时对规模大、批量生成、天然存放在数据湖中的数据使用 External Collection。

作者介绍

图片

刘伟

Staff Software Engineer at Zilliz

阅读推荐
官宣开源|Milvus 3.0 正式发布
Milvus 3.0 开源解读之backfill|亿级 AI 数据,如何做高效特征回填
Milvus 3.0 开源解读|从Kafka、Pulsar到Woodpecker,数据库如何低延迟、低成本的写入
Milvus 3.0 开源解读之Manifest|AI 数据管理,应该彻底放弃文件中心架构
Milvus 3.0 开源解读之,数据库原生聚合排序如何取代应用侧Pandas胶水代码
Milvus 3.0开源解读之Regex |从=~到NGRAM,如何选择最优性价比的正则过滤
Milvus 3.0 开源解读之Snapshot|无需复制embedding数据的Milvus Collection视图
Milvus 3.0 开源解读|自定义词典如何优化BM25 与Text Match 的专业词理解能力
Milvus 3.0 开源解读|如何借助原生 TEXT 类型和 LOB 高效管理原始文本
Milvus 3.0 开源解读|词法+语义高亮,如何解决Agent的搜索噪音?
Milvus 3.0开源解读|从Entity到Element,StructArray 如何重构多向量检索
图片
图片

前往微信阅读全文

内容来自公众号,可前往微信查看原文。

查看作者的更多文章 →