AI 时代,如果数据库不蹭点 AI 概念,已经都不好融资了,这使得大家都争先恐后地期望自己能够成为 AI 时代的数据底座。但问题是做 DB 的没搞过 AI,搞 AI 的又不搞 DB,这导致很多 DBer 其实并不感知 AI 到底需要什么样的数据库。
这个问题也不断困扰着我,好在我道听途说,东拼西凑了一下,特此记录。
混合搜索
混搜就是标量 + 向量混合搜索,比如我有一个需求:要在南山区里找评分大于 4.5、且评论语义最相关的火锅店:
SELECT id, name, rating
FROM shop
WHERE rating > 4.5 -- 标量过滤
AND district = '南山区' -- 标量过滤
ORDER BY cosine_distance(comment_vec, '[0.12, 0.98, ...]') -- 向量语义
APPROXIMATE -- 关键:才会走向量索引
LIMIT 10;当然图搜索也可以额外作为混搜的一个能力。
Data Evolution
首先要声明一下 Data Evolution 和 Schema Evolution 的区别如下:
Data Evolution:加列之后,老数据也需要处理。对于 Iceberg、Delta 等表格式,往往需要重写整个数据集才可以实现。
Schema Evolution:加列之后,老数据那行对于新列就是直接填充 NULL。像 Iceberg/Delta 支持的都是 Schema Evolution。
在模型训练的时候,同一份原始数据可以不断提取新的语义、业务特征。例如文本可以生成主题、摘要、embedding、安全标签、语言类型、质量评分;图片可以不断追加 OCR 结果、标签、风险分和业务特征。这时候我们需要轻量化的 Data Evolution,避免每一次加列都要重写整个数据集。
大宽表
数据集会随着业务的变化,不断地加特征列,最后可能加到上万列,数据库要能很好地应对这种情况。
像现在 Parquet 会面临元数据快速膨胀的问题,而 Vortex 和 Nimble 就是能解决这类问题。
当然 Parquet 现在也在努力解决了,等新的 FlatBuffers 标准推出来就行了。
Branch、Tag、Time Machine
总的来说就是让数据库变得和 git 一样,开分支,merge,revert 一气呵成,极大方便 agent 数据加工试错。
比如业务方 fork 一个 Database,秒级复制出一个完整的数据库副本,在分支上随便试各种特征加工口径、筛选条件,搞成了 merge 回主干,搞砸了直接扔掉,代价几乎为零。做训练数据本来就是大量试错,这个能力可以让你大胆放手试。
多租户
多租户必然伴随着高效的资源隔离,当有多个 Agent 同时操作数据库时,如果有多租户能力,可以有效隔离不同 Agent 之间的干扰。
同时 Agent 数量可能会不少,所以要具备支持海量租户的能力。
随机访问
具备对数据集随机访问的能力,总的来说就是取数组下标式的访问(table.take_idx(5))。
为什么需要这种能力?这里我就简单举两个例子:
RAG 回表
ANN 索引返回 doc id,要低延迟按主键取回 chunk 原文、元数据、配图。另外 ANN 粗召回之后常要读全精度原始向量做精排(rerank),这也是按 id 随机读。通常 doc id 就是文本行号,也就是说会随机取这个表的第 n 行。
Shuffle 数据集
在训练模型时,我们需要基于打乱数据后的数据集进行训练。一般做法就是从一个表里面随机取出 n 行,喂给模型进行训练。
训练时为什么要打乱数据?
训练神经网络时用的是 mini-batch 随机梯度下降(SGD),它有一个基本前提:每个 batch 里的样本应该是随机抽取的、彼此独立的。如果你总是按数据在磁盘上的原始顺序一条接一条地喂,会出大问题。
具体到 GitHub 代码数据这个场景,假设不打乱、顺序读:
数据在磁盘上很可能是一个仓库的文件连着放的——比如前 5000 个样本全是 Django 项目的代码,接着 5000 个全是某个数据科学库的代码。如果按顺序喂,模型会在某一段时间里只看到 Django 风格的代码,梯度就朝着「拟合 Django」的方向猛跑;过一会儿又全是另一种风格,梯度又猛地转向。
这会导致几个坏结果:
一是梯度有偏、训练不稳定。每个 batch 内部高度相似,不能代表数据的整体分布,梯度方向来回大幅摆动,loss 曲线剧烈震荡。
二是灾难性遗忘。模型学完这一段风格,进入下一段完全不同的数据时,容易把前面学的忘掉。
三是收敛慢、泛化差。模型看到的「顺序」本身成了一种它不该学的规律。
多模数据
多模数据指图片、音频这种非结构化,还很大的数据。数据库需要在存储和计算上,都进行适配。
我这里随便举两个需要适配的例子:
存储:如果拿 Parquet 硬存大对象,Row Group 会因为 size limit(通常是 128MB),塞不了几行,那就意味着需要更多的 Row Group 才存储这个表。此时因为 Row Group 的增多,元数据会极速膨胀。另外大对象列显然不可能有统计信息,那此时 Parquet 大对象列的统计信息就是完全冗余且毫无意义的。
计算:如果直接把大对象放在内存里面,参与算子的计算,那会非常消耗内存。通常做法是搞一个 Lob 类型,对于小对象直接 inline 在内存里面,对于大对象,那么 Lob 里面只是存放一个指向文件的指针。
实际案例
下面列举一些实际的场景,加深对业务的理解,下面包含 AI 生成内容。
Feature engineering 迭代
ML 团队最常见的循环:跑一个新模型/新算法,给数据集加一列新特征(embedding、分类标签、质量分、caption……),训练,再加下一列。如果用 Iceberg,每加一列都要重写 TB 级数据,代价太高。同时随着业务变化,特征列越加越多,Parquet footer 的元信息也在不断膨胀,从而拖慢查询性能。
能力要求:Data Evolution、大宽表。
图片去重
把数据库当做一个 HashMap。每当图片过来,对其 embedding 后得到向量 vector,然后在数据库里面插入 [picture_vector, picture_binary]。
当后面还有新图片过来时,对图片进行 embedding 得到向量,然后去数据库里面进行近似搜索,来确定是否存在相似图片。
能力要求:混搜、多模数据。
智驾训练
一辆测试车每天就能产出 TB 级数据:多路摄像头视频、激光雷达点云、毫米波雷达、IMU/CAN 总线信号、GPS 轨迹。这些数据跑的是一套「数据闭环」——采集回传、清洗与时间同步、挖掘、标注、训练、上车验证,再触发下一轮采集。真正卡脖子的往往不是算法,而是怎么从海量素材里快速捞出「有价值」的那一小撮场景,尤其是长尾的 corner case(异常路况、极端天气、加塞、碰撞风险),靠人工翻素材根本不现实。
处理链路大致是:视频先抽帧,再做检测 / 分割识别出车辆、行人、车道线等目标,然后用多模态模型(CLIP、BEV-CLIP 这类)把画面编码成特征向量,连同时间、地点、天气、车速、车型、场景标签等结构化元数据一起入库。关键在于——图像 / 文本特征向量要和标量元数据存在同一套系统里,才能在一条查询里同时按向量语义和标量条件筛选。
有了这套底座,场景挖掘就变成了混合检索。业务同学用一段自然语言或一张示例图,向量近似搜索 + 标量过滤一把召回所有相似片段,常见诉求比如:
- 雨天 + 前车切入
- 夜间 + 行人横穿马路
- 匝道汇入 + 大货车遮挡
发现一个新的 corner case 后,还能反过来用它的向量在库里「捞相似」,把同类场景成批补进训练集。
过去这套得靠三件套拼起来:对象存储放视频、关系库放元数据、单独的向量库放特征,几套系统之间来回搬数据、跨库 join,还要自己维护一致性。统一到一套能同时管多模态数据 + 原生向量索引的底座之后,理想情况是一条 SQL 就把「按语义找场景 + 按标签过滤 + 取回原始片段」全干完。
能力要求:混搜、多模数据。
证券行业
券商手里的数据天然分两类:结构化的行情、交易、财务、持仓、客户画像;非结构化的研报、公告、招股书、制度文件、新闻舆情,以及大量电话会议 / 路演的音视频。业务价值往往恰恰来自两类的交叉——比如「筛出 ROE 连续三年 >15%、且最近一个月研报观点明显转多的标的」,前半句是结构化财务过滤,后半句要对研报做语义 + 情绪分析,单靠任何一类数据都答不了。
典型落地场景:
- 研报解析与点评:把 PDF 研报拆解、抽取要点、生成点评,同时保留底稿、可追溯到数据来源,方便合规审查(如海通「e海言道」研报点评大模型)。
- 投研知识检索 / RAG:把海量研报、资讯、会议纪要灌进知识库做检索增强(中信建投用 200 多万条研报与资讯 + 上万小时音视频做二次训练,大模型日处理上千份新研报,把两小时调研会压成十分钟摘要)。
- 合规问答:基于制度文件做 RAG,答案必须能定位到具体条款出处。
头部券商还在做千亿参数级的多模态垂类模型 + 专有知识库(如国泰君安「君弘灵犀」),把这些能力串起来。这些场景的共同点是:既要对非结构化文本 / 音视频做向量与全文检索,又要随时和结构化的行情、财务、持仓做联合过滤。如果结构化数据在数仓、文本在向量库、原文在对象存储,就得反复 ETL、跨系统 join,一致性和权限都难管。而金融行业对私域数据、数据不出域、答案可追溯、按角色控权限的要求又特别高,所以更倾向把结构化 + 非结构化放进同一套带权限和事务的数据服务体系里做混合检索。
能力要求:混搜、多模数据。
保险报销
前面几个案例的重点大多在离线的数据加工与训练,保险报销则更偏实时在线决策:用户拍张发票传上来,恨不得几秒钟就出理赔结果(业界叫「秒赔」),后台的数据库得在一次在线请求里把活全干完。
一次报销进来的东西天然是多模态的:医疗发票、诊断报告、处方笺、费用清单、身份证,基本都是图片或 PDF 扫描件。处理链路大致是:
- 原始单据先入库(多模存储,直接把 blob 存进来,省掉「传对象存储 + 存 URL」那一步,也免了两边一致性的麻烦)。
- 跑 OCR + 多模态模型,抽出结构化字段:金额、就诊日期、医院、科室、诊断 / ICD 编码、药品明细,同时给整张单据算一个 embedding。
- 这些抽取结果是不断追加到同一条记录上的——今天加个金额字段,明天上线了防伪模型再加个「篡改风险分」,后天又加个诊断语义向量。这正是文章前面说的 Data Evolution:加列不该把 TB 级历史单据全部重写一遍,老单据的新列也不能只是简单填 NULL。
真正做理赔决策时,需要的是一次混合检索——结构化过滤 + 向量语义一把查完:
- 结构化那半句:这张保单保不保这个病种?在保障期内吗?扣除免赔额后能赔多少?累计赔付额度还剩多少?
- 语义那半句:诊断描述、处方内容是不是落在保单条款覆盖的范围里?(条款是自然语言,光靠字段匹配不了)
还有一个和前面「图片去重」几乎一模一样的诉求——防重复报销 / 反欺诈。同一张发票不能在多家保险公司、多张保单之间反复报销。每张发票入库时算好向量,新单据进来就先去库里做近似搜索:有没有一张长得几乎一样的发票已经赔过了?以及识别 PS 过的、套模板伪造的单据。
最后,理赔要打款,是笔真金白银的事务:改保单额度、写理赔记录、触发支付,得保证一致性。所以这个场景理想的底座是——能在同一套带事务和权限的系统里,低延迟地把「多模原始单据 + 结构化字段 + 向量索引」一次查全,而不是把图片、元数据、特征向量拆到三套系统里,在线请求还得跨库 join。
能力要求:Data Evolution、混搜、多模数据。
总结
我也是在学习与摸索,如有错误,欢迎指正。