金仓KES向量数据库实战:一条SQL干掉ETL,机器人不再把停产货当现货卖

一、先说痛点:那套"三件套"架构到底难受在哪

做 RAG 的团队,早期架构大多长一个样,可以叫它三件套。MySQL 这类关系库存业务元数据和文档目录,一个专用向量数据库存 embedding,中间靠 ETL 任务搬砖。向量火起来那几年,这套几乎就是行业标配,照着抄没人会说你错。

跑起来才知道难受。

最要命的一条,同一份业务事实存了两个系统。权限、产品线、有效性这些属性,向量库里放一份当过滤字段,关系库里再放一份当权威版本,靠异步任务保持一致。听着挺合理是吧?问题是 ETL 这东西会安静地失败。同步任务卡住,鉴权失效,队列堆积,很多时候它压根不报错,就是安安静静地不干活。然后呢?产品三个月前就在关系库里标记停售了,向量库里的副本还活得好好的。机器人一检索,相似度命中的全是它的资料,信誓旦旦告诉客户这货有货,客户真去下了单。把停产货当现货卖,这类事故的根儿全在这儿。同源数据双写,靠异步对齐,延迟和漂移就不是会不会发生的问题了,只是什么时候被人撞见的问题。

混合查询也别扭。用户的真实问题从来不是纯相似度,是"某产品线、我有权限、还没过期的资料里搜"这么一串。可向量库的元数据过滤普遍偏弱,咋办?应用层先查关系库拿 id,攥着 id 列表再奔向量库搜呗。权限范围一大,列表好几千个,性能当场趴窝。一次检索拆成两段,中间还得上应用层拿针线缝,代码丑,延迟也高。

还有运维,双份的苦。两套库,两套备份,两套告警,两套权限体系。半夜告警响了,先花半分钟想这回是哪家的。

这些麻烦摊开看,根子其实是同一个问题:向量凭什么非得住在另一个库?它不就是表里的一列吗,凭什么不能跟别的字段住一张表、进同一个事务?

顺着这个问题找方案,就落到金仓 KES 的向量能力上了。思路跟那个问题一模一样,向量作为一种数据类型直接建在关系表里,跟数值、文本、JSON、GIS、时序一块儿活在同一个库里。没有搬运这回事,一条 SQL 里向量检索和普通条件一起下。

起先容易犯嘀咕,专用向量库迭代得跟飞一样,数据库内置的向量能力会不会是个玩具?两件事测完就踏实了。一是人家自研实现了 HNSW 和 IVFFlat 两种近似索引,还带 SIMD 指令级的加速,不是扫个表糊弄事。二是向量数据直接吃数据库的 ACID 事务,这条市面上不少 NoSQL 形态的向量库给不了,而元数据改了向量必须跟着变的场景,恰恰要的就是它。改完即生效,事务提交那一瞬间,检索结果里就再也见不着这条数据了。

目录

二、建表

文档切片表直接带上向量列。embedding 模型输出 768 维,类型声明里就写 768,别的不用多想:

sql 复制代码
CREATE TABLE doc_chunks (
    id          BIGSERIAL PRIMARY KEY,
    doc_id      BIGINT NOT NULL,
    doc_title   TEXT,
    chunk_text  TEXT NOT NULL,
    embedding   VECTOR(768) NOT NULL,
    product_line TEXT,
    is_active   BOOLEAN DEFAULT TRUE,
    dept_perm   TEXT[],
    updated_at  TIMESTAMPTZ DEFAULT now()
);

多看一眼 is_active 和 dept_perm 这俩字段。停产标记、部门权限,原来住在另一个库里,现在跟向量睡同一行。产品停售了咋整?一条 UPDATE 把 is_active 置掉,事务一提交,检索结果里立刻就没有它了。开头说的那类事故,搁这个架构下压根没有发生的土壤。

三、索引选型,加两个血泪坑

两种索引都实测了,代码一起贴给你:

sql 复制代码
-- HNSW:多层图结构,召回高、查询快,就是吃内存、建得慢
CREATE INDEX idx_chunk_hnsw ON doc_chunks
    USING hnsw (embedding vector_cosine_ops);

-- IVFFlat:先聚类再倒排,省内存、建得快,拿 probes 调精度
CREATE INDEX idx_chunk_ivf ON doc_chunks
    USING ivfflat (embedding vector_cosine_ops);

咋选?语料百万级、内存扛得住、场景对召回率敏感的,上 HNSW,漏一条关键维修案例可比慢十毫秒严重多了。向量上亿、内存又紧的,IVFFlat 值得先试。补一句,这儿的索引在数据插入更新时是实时维护的,不存在建完索引就不让改数据那种憋屈事。

坑,踩了俩。

先说 IVFFlat,千万别在空表上建。它先对全量数据聚类再分桶,空表建出来的分桶全是错的,召回惨到没法看。这个坑阴就阴在它不报错,只会让你怀疑版本有 bug,排一晚上,最后发现是自己的用法不对。记住顺序,先灌数,后建索引。

另一个更隐蔽。用余弦距离的话,向量入库前要归一化,不然模长会掺进距离里捣乱,排序结果莫名其妙地不对。症状是检索质量忽好忽坏,特别难定位,查到这儿的时候才知道说起来都是泪。你的检索要是时灵时不灵,先去查归一化。

四、检索就一条 SQL

改造后最爽的部分。用户提问的完整检索,就这一条:

sql 复制代码
-- q_vec 是应用侧把用户问题向量化后传进来的参数
SELECT doc_title, chunk_text,
       embedding <=> q_vec AS distance
FROM doc_chunks
WHERE is_active = TRUE
  AND product_line = '工业控制器'
  AND dept_perm @> ARRAY['after_sales']
ORDER BY embedding <=> q_vec
LIMIT 5;

看看 WHERE 里混了些什么。有效性,产品线,数组权限判断,再搭上 <=> 算的余弦距离排序。原来那个先查 id 列表再传给向量库的两段式,整个被压进数据库一次执行。权限列表几千条也不虚了,过滤条件下推,扫描范围先砍一大截。

我个人觉得这是融合架构最被低估的地方。大家盯着向量检索性能比来比去,很少有人算省掉的那条同步链路值多少钱。ETL 没了,监控对象少一截,数据新鲜度从"同步任务上次跑成功是啥时候"变成事务级。

五、顺手的惊喜

向量类型还支持加减、数乘、拼接,带 AVG、SUM 这类聚合。别小看这个。拿它算全部语料向量的均值,专门捞离群的切片:

sql 复制代码
-- 找跟语料中心离得最远的切片,多半是切坏了的或者内容异常的
WITH center AS (
    SELECT AVG(embedding) AS c FROM doc_chunks
)
SELECT id, doc_title, embedding <=> c AS distance
FROM doc_chunks, center
ORDER BY distance DESC
LIMIT 20;

跑出来一瞧,排前面的一堆切稀碎的表格和乱码 PDF。以前这种活儿得把数据导出去写 Python 脚本,现在 SQL 一条。语料清洗这个谁都不爱干的环节,忽然就轻了不少。

六、几句实话

这回最大的感触是,AI 应用的架构难题好多时候不在模型身上,在数据怎么组织。向量数据库这个词听着像要开新世界,扒开看,向量就是一种数据形态嘛,它在关系库里完全能活得好好的,还顺手把事务、权限、混合查询这些老手艺全带过来了。

代价也得老实交代。单一库资源共享,高峰期向量检索和普通业务查询会抢 CPU,得靠资源隔离配置压一压,不算完美,但跟伺候同步链路比,省心多了。

一套数据库解决所有问题,这话说得太满我不学。对中等规模的 AI 应用,建议就一句,先别急着上第三套存储,看看手头的库能不能把向量装下,多半能省出一条同步链路,外加好几个不眠之夜。

相关推荐
Y0010 小时前
PostgreSQL 的锁:为什么你的 ALTER TABLE 会卡住,以及怎么查
数据库·postgresql
灰原喜欢柯南10 小时前
OceanBase 运维管理知识点——OCP 监控、诊断与变更控制
数据库·oceanbase
码农-00410 小时前
Springboot 集成 Ehcache操作数据库显示SQL语句设置
数据库·spring boot·sql
仍然.10 小时前
Redis---事务
数据库·redis
oradh10 小时前
Oracle 执行计划的阅读方法(二叉树结构来理解和阅读)
数据库·oracle·执行计划的阅读方法
SelectDB10 小时前
Doris 向量检索上线踩坑记录:七个排查现场与可直接复制的命令
大数据·数据库·数据分析
数据库小学妹10 小时前
MySQL深分页优化:LIMIT大偏移的根因分析与五种解法对比
数据库·mysql·性能优化
SelectDB10 小时前
一条大查询把集群打挂之后:Apache Doris 稳定性处置与资源隔离速查
大数据·数据库·数据分析
钱栈up10 小时前
全量 21 个失败、单跑全绿:泄漏进连接池的 SQL 变量
sql·单元测试·测试