本页是跨章组合视角 · schema 设计焦点
本页讲"一张湖表同时承载 BI 和 AI 数据的 schema 设计"· 涉及机制深挖去:
- 湖表底层机制 → lakehouse/lake-table · lakehouse/multi-modal-lake
- Schema Evolution → lakehouse/schema-evolution
- Embedding 模型选型 → retrieval/embedding · retrieval/multimodal-embedding
- Embedding 生成流水线 → ml-infra/embedding-pipelines
本页专做:多模表 schema 的跨 BI + AI 设计。
多模数据建模¶
一句话理解
一张湖表,同时承载结构化字段 + 非结构化资产(或其指针)+ 多种向量列 + 元数据。三条原则:二进制不直接进表、向量列按用途分列、元数据是一等公民。
为什么单独讨论¶
"把一张图存进表里"听起来简单,实际坑非常多:
- 大二进制放行里 → 列剪裁失效 / 扫描爆内存
- 一张表只存一种向量 → 换模型就得复制全表
- 没有统一元数据 schema → 过滤语义没法跨表复用
一个好的多模表设计决定了未来检索、训练、治理全部的顺畅度。
本节(§推荐表结构模板)· 团队推荐模板 · 非客观唯一答案
以下 7 类字段模板是团队基于多个项目凝练的推荐 · 体现本 wiki 的多模建模主张(字段分类 / 命名 / 数据类型)。具体场景需要增删调整 · 不要机械复制。
客观知识部分在下文 §三条原则(二进制不进表 / 向量列按用途分列 / 元数据一等公民)· 那三条是行业共识 · 不是主张。
推荐表结构模板¶
CREATE TABLE multimodal_assets (
-- 标识
asset_id BIGINT NOT NULL,
asset_version INT NOT NULL DEFAULT 1,
-- 模态标签
kind STRING NOT NULL, -- image / video / audio / text / doc
modality_tags ARRAY<STRING>, -- 'ocr-able', 'face-present', ...
-- 资产指针(二进制不进表)
raw_uri STRING NOT NULL, -- s3://.../xxx.jpg
raw_size_bytes BIGINT,
raw_sha256 STRING,
thumb_uri STRING, -- 缩略图 / 代表帧
-- 可检索的文字侧
caption STRING, -- 人写 / 模型生成
ocr_text STRING,
transcript STRING, -- 语音转写
-- 向量列(按用途 / 模型拆)
clip_vec VECTOR<FLOAT, 512>, -- 多模通用检索
text_vec VECTOR<FLOAT, 1024>, -- 长文精细检索
audio_vec VECTOR<FLOAT, 512>, -- 音频检索(仅音频 / 视频有)
-- 业务 & 治理
owner STRING,
visibility STRING, -- public / internal / restricted
source STRING, -- 来源系统 / 采集 pipeline
tags ARRAY<STRING>,
ts TIMESTAMP,
partition_date DATE
) USING iceberg
PARTITIONED BY (kind, partition_date, bucket(16, asset_id))
TBLPROPERTIES (
'write.parquet.compression-codec' = 'zstd',
'write.distribution-mode' = 'hash',
'history.expire.max-snapshot-age-ms' = '2592000000' -- 30 天
);
本节(§三条原则)· 客观知识 · 行业共识
以下三条原则(二进制不直接进表 / 向量列按用途分列 / 元数据一等公民)在工业界广泛成立 · 不是团队主张。具体实现方式可能因技术栈差异 · 但原则本身通用。
三条原则,拆开来看¶
1. 二进制不直接进表¶
原始图 / 视频 / 音频放对象存储,表里只保留 raw_uri + sha256 + size。理由:
- Parquet 列剪裁只对标量列最高效
- 大二进制让 Row Group 扫描极不可控
- 统一对象存储更适合分层存储、CDN、权限系统
2. 向量列按用途 / 模型分列¶
不要"一列存所有向量"。常见分法:
- 多模通用(CLIP / SigLIP)—— 跨模态检索
- 文本精细(BGE / E5 / Jina)—— 长文场景、高召回
- 稀疏向量(SPLADE)—— Hybrid Search
- 领域专用(人脸 embedding / 指纹 embedding 等)
好处:换模型只重建一列;不同列可独立建索引、独立 rerank。
3. 元数据是一等公民¶
visibility、owner、source、modality_tags 等都是下游过滤 + 权限的锚点。多模检索几乎都要带 WHERE visibility = 'internal' AND kind = 'doc' 这种条件,这些字段必须可索引(min/max + bloom filter)。
分区策略¶
多模表通常按 kind + 时间分区;在 Iceberg 里配合 bucket(N, asset_id) 做 hash 子分区来稳定并行度。
为什么按 kind?
- 不同模态的写入频率差异大(日志级文本 vs 每日上传图片)
- 向量索引按模态构建,物理隔离更利于后续 compaction / reindex
- 绝大多数查询都只查一种模态
演化场景¶
新增一种模态¶
加列:modality_tags 里加一个 tag,新增资产写入即可。历史数据不需要动。
换 embedding 模型¶
增加一列新向量,老列保留一段时间做 A/B。不要覆盖老列,以免新模型效果不如旧模型时回不去。
新增一个治理字段¶
加列 + 批作业回填 + 重建索引(通常只影响 bloom filter)。Schema Evolution 原生支持。
相关¶
延伸阅读¶
- Designing Data-Intensive Applications (Kleppmann) —— 建模基础
- LanceDB 的多模表 tutorial
- Databricks MosaicML 公开的多模数据 blueprint