4. 元数据采集策略:主动采集 vs 被动采集

元数据采集,说白了就是怎么把向量数据的“身世”给抓出来。我做了这么多年数据治理,发现很多团队在这个环节就栽了跟头——要么采集不全,要么把生产系统搞崩了。

今天咱们聊聊四种主流方案。每种都有它的脾气,选对了事半功倍。

4.1 主动采集 vs 被动采集

先分个类。主动采集,就是系统主动去问、去查、去拉数据。被动采集,是系统等着别人把数据送上门。

维度 主动采集 被动采集
触发方式 定时轮询或手动触发 事件驱动,有变化才触发
实时性 取决于轮询间隔,秒级到小时级 毫秒级,几乎实时
对源系统影响 有额外查询压力 影响极小,异步处理
实现复杂度 低,写个定时任务就行 高,需要对接事件系统
数据完整性 可能漏掉短时间内的变更 理论上不丢数据

我的建议: 核心链路用被动采集,辅助链路用主动采集。比如向量数据库的写入操作必须实时追踪,而元数据表的全量快照每天拉一次就够了。

我在项目中遇到过一种尴尬情况:用主动采集每5分钟扫一次,结果刚好在两次扫描之间,某个关键向量被删了又重建。血缘链直接断了。后来改成被动采集+日志兜底,才算稳了。

4.2 日志解析方案

这是我最常用的方案。说白了,就是盯着系统的日志文件,把元数据变化给“抠”出来。

为什么选它?因为几乎所有系统都会写日志,你不需要改一行代码。

核心流程

  1. 日志采集:用 Filebeat、Fluentd 或 Logstash 把日志收上来
  2. 日志解析:写正则或 Grok 模式,把关键字段提取出来
  3. 事件还原:把解析结果拼成标准化的元数据事件
  4. 写入血缘存储:推给 Neo4j 或 JanusGraph
# 一个简单的 Grok 解析模式示例
# 原始日志:2024-01-15 10:30:22 [INSERT] vector_id=v_12345, collection=products, dims=768
%{TIMESTAMP_ISO8601:timestamp} \[%{WORD:operation}\] vector_id=%{DATA:vector_id}, collection=%{DATA:collection}, dims=%{NUMBER:dimensions}

避坑指南: 我曾经被日志格式变更坑过——开发同学随手加了个字段,解析器直接崩了。后来我养成了两个习惯:一是给解析加熔断和告警,二是保留原始日志,方便回溯重解析。

日志解析的痛点是啥?实时性不够。日志是异步写的,从操作发生到日志落盘,再到被采集解析,中间可能有几秒到几分钟的延迟。对于实时性要求高的场景,得配合其他方案。

4.3 API Hook 方案

这个方案更“优雅”。在向量数据库或计算引擎的关键操作上挂钩子,操作一发生,钩子就把元数据事件发出来。

你想想看,这就像在门口装了个传感器——有人进出,它立刻就知道。

实现方式

  • 前置 Hook:操作执行前触发,可以拿到请求参数
  • 后置 Hook:操作执行后触发,能拿到执行结果
  • 异步 Hook:不阻塞主流程,通过消息队列发送事件
# 伪代码:向量数据库的写入 Hook
class VectorWriteHook:
    def after_write(self, request, response):
        event = {
            "type": "vector_insert",
            "vector_id": request.vector_id,
            "collection": request.collection,
            "timestamp": time.now(),
            "source": request.source_system,
            "status": response.status
        }
        # 发送到消息队列
        message_queue.send("metadata_events", event)

注意: Hook 不能影响主流程性能。我见过有人在后置 Hook 里做重计算,结果接口响应时间从 10ms 飙到 500ms。记住,Hook 里只做轻量操作,重活丢给异步队列。

API Hook 的优点是实时性好、数据准确。缺点是需要改代码——你得在业务系统里植入 Hook 逻辑。对于老系统或者第三方系统,这招可能行不通。

4.4 数据库 CDC 方案

CDC(Change Data Capture),变更数据捕获。这是最“硬核”的方案,直接从数据库的变更日志里捞数据。

我最早接触 CDC 是在做 MySQL 的 Binlog 解析。后来发现,几乎所有主流数据库都支持类似的机制。

常见 CDC 工具

数据库 CDC 机制 常用工具
MySQL Binlog Canal, Maxwell, Debezium
PostgreSQL WAL (Write-Ahead Log) Debezium, pgoutput
MongoDB Oplog MongoDB Connector, Debezium
Milvus 消息队列 + 日志 自定义 Consumer
# Debezium 配置示例(MySQL -> Kafka)
{
  "name": "vector-metadata-connector",
  "config": {
    "connector.class": "io.debezium.connector.mysql.MySqlConnector",
    "database.hostname": "192.168.1.100",
    "database.port": "3306",
    "database.user": "cdc_user",
    "database.password": "******",
    "database.server.id": "12345",
    "database.server.name": "vector_meta",
    "table.include.list": "vector_db.vectors, vector_db.collections",
    "database.history.kafka.bootstrap.servers": "kafka:9092",
    "database.history.kafka.topic": "schema-changes.vector_meta"
  }
}

CDC 的核心优势: 对业务系统零侵入,不需要改代码,不需要写 Hook。而且它捕获的是数据库底层的变更,连 DDL 操作都能抓到——这在血缘追踪里太重要了。

但 CDC 也有坑。我记得有一次,Binlog 的格式从 ROW 改成了 STATEMENT,解析器直接废了。还有一次,数据库做了主从切换,CDC 的 offset 没对齐,丢了一大批事件。

嗯,这里要注意:CDC 方案一定要做好 offset 管理和容错。建议用 Kafka 做事件缓冲,配合 Exactly-Once 语义,基本能保证不丢不重。

4.5 方案选型对比

说了这么多,到底选哪个?我一般按这个逻辑判断:

  • 能改代码、实时性要求高 → API Hook + 消息队列
  • 不能改代码、数据量中等 → 日志解析
  • 不能改代码、数据量大、要求零侵入 → 数据库 CDC
  • 辅助链路、非核心数据 → 主动采集(定时任务)

实际项目中,我通常会把几种方案混着用。比如核心的向量写入用 CDC,辅助的配置变更用主动采集,中间再加一层日志解析做兜底。

元数据采集策略总览 向量数据源 主动采集(轮询) 日志解析 API Hook 数据库 CDC 元数据事件(标准化) 血缘存储(图数据库 / 时序库) 💡 选型建议 • 核心链路:CDC + API Hook 双保险,保证实时性和零侵入 • 辅助链路:主动采集兜底,日志解析做历史回溯 • 所有事件统一格式,写入消息队列解耦

我的经验: 别追求单一方案的完美。生产环境里,我通常用 CDC 做主采集通道,日志解析做备份,再配一个主动采集做全量对账。三管齐下,基本没出过问题。