接到一个企业知识库的选型评审时,我习惯先问三个问题:知识从哪来、用户怎么找、存量业务系统在哪个数据库上。如果答案里有大量非结构化文档,以及问答、推荐类场景,那基本绕不开向量检索。最近一段时间,DynamoDB 原生向量搜索在企业知识库选型里的话题度越来越高,核心原因很简单:它把原来需要单独部署、单独运维、单独同步的向量检索能力,塞进了你已经用得很熟的 NoSQL 数据库里。这篇文章不打算做功能罗列,而是从影响面出发,聊聊它对企业知识库 AI 选型带来的 3 个直接影响,以及我在 PoC 和落地过程中踩过的坑、验证过的心得。适合正在做技术选型的架构师、后端开发,以及被老板问“能不能别再加一套数据库”的技术负责人看。
1. 先搞清背景:企业知识库为什么需要向量搜索
1.1 知识库的演进:从关键词到语义检索
企业知识库的常见形态,无非是内部 Wiki、FAQ、客服话术、产品文档、制度文件、项目复盘。以前大家主要靠 Elasticsearch 或关系型数据库做关键词匹配,问题恰恰在于“用户问的是意思,文档写的是概述”。比如用户搜“发票报销要几天”,文档标题写的是“费用报销周期及流程”,关键词匹配不到,这条知识就被埋没了。
向量检索解决的是语义近似问题。它把文本映射成一组 100 到 1024 维的稠密向量,用最近邻算法算距离。用户的问题成了一个 query 向量,知识库里的每一段文本都有自己的向量,系统按距离从近到远返回最相似的片段。这就是 RAG(检索增强生成)类应用最基础的底座。
这里有一个经常被忽略的事实:企业知识库不只有“文本检索”这一个需求。它通常还伴随权限、分类、时效、数据来源等结构化元数据。你希望搜到的结果不仅语义正确,还要满足“只能看本部门文档”“已归档的不要出现”“管理员撤回的立即失效”。这套逻辑落到存储层,就比单纯一个向量数据库复杂得多。
1.2 “原生”意味着什么
很多团队发展到一定阶段,业务数据已经长在 DynamoDB 上了,比如用户行为、订单、内容元数据、配置表。RAG 方案走的是另一条分支:把文档拆成 chunk,调 embedding 模型生成向量,写入一个独立的向量数据库,再在向量库里建索引、执行相似度查询。于是生产环境里就有了“业务库”和“向量库”两套数据底座。
DynamoDB 原生向量搜索的卖点,在于你不需要把向量放进另一个系统。它允许你在现有的 DynamoDB 表上增加一个向量索引,然后直接对这张表里的 item 执行 KNN(K 最近邻)检索。向量字段和业务字段躺在同一条记录里,索引由 DynamoDB 自己维护。
这个“原生”二字带来的不只是少装一个软件,它直接改变了你处理数据同步、权限、备份、容灾的方式。后面我会展开讲这三个影响。
1.3 不是所有知识库都要用它
先把边界说清楚,避免出现“用了原生功能就万事大吉”的错觉。我个人的判断标准是:
- 数据规模在几十万到几百万向量之间,原生方案很合适;
- 千万级以上、高并发、复杂多模检索,专用向量引擎仍然有优势;
- 如果团队已经有一套 OpenSearch 集群,且只缺向量检索能力,那给 OpenSearch 开 k-NN 插件可能是更省事的路径;
- 如果业务还没上 DynamoDB,纯粹为向量功能迁移过去,那就要重新计算迁移成本。
DynamoDB 原生向量搜索是“够用就好”的典型代表。它不是要取代所有专用向量数据库,而是给大量中等规模、业务耦合度高的知识库场景提供一条低摩擦的路径。
2. 影响一:架构从“数据库 + 向量库”变成“一张表”
2.1 旧架构的数据管道有多繁琐
我去年参与一个中型制造企业的知识库项目,最初方案就是标准的“双存储”架构:DynamoDB 存文档元数据,独立向量库存向量。当时画出来的数据链路是:
- 业务系统在 DynamoDB 写入或更新一条文档记录;
- DynamoDB Streams 捕获变更事件;
- 一个 Lambda 消费事件,取出文本内容;
- 调用 embedding 接口生成向量;
- 把向量写入独立向量库;
- 业务查询时先查向量库拿 TopK ID,再回 DynamoDB 查元数据。
这个链路看着清晰,跑起来全是坑。Lambda 偶发超时、向量库写入失败后没有可靠的失败重试机制、幂等逻辑要自己写。最头疼的是数据同步延迟:文档在业务库已经更新了,向量库里却还是旧向量,新内容搜不到、旧内容又撤不掉。
运维层面同样分裂。权限体系两套,监控告警两套,备份恢复各管各的。出了问题排查链路长,先判断是 Streams 没触发,还是 Lambda 报错,还是向量库写入被限流。团队只有两三个人负责基础设施的时候,这套东西在消耗大量隐性人力。
2.2 原生方案下链路缩短成什么样
改用 DynamoDB 原生向量搜索之后,架构变成这样:
- 业务系统在 DynamoDB 写入一条 item,里面既包含文本和元数据,也包含通过 embedding 模型生成的向量字段;
- DynamoDB 在建好的向量索引上自动维护索引数据;
- 应用端直接发起 KNN 查询,在一条记录里同时拿到相似度和业务元数据。
第 3 步是关键差异。过去查询向量库拿到 ID 以后,还得回业务库再查一遍详情。现在一次查询就能带出文档标题、内容摘要、权限标签、部门信息等所有字段。查询代码少了,出错的环节也就少了。
我在另一家客户现场做 PoC 时,这个差异体现得很直接。他们原来用独立向量库做客服知识库,查询链路平均耗时 80ms 左右,其中向量库只占 15ms,剩下的大头是“查 ID—查详情—拼装结果”的多次网络往返。原生方案一次 KNN 查询直接返回全部字段,整体耗时降到 40ms 上下。
2.3 架构影响背后的取舍
链路变短不等于没有成本。DynamoDB 的索引不是“加一个字段就自动能用”的,你要提前规划好分区键、向量维度、索引覆盖字段。向量字段和业务字段耦合在同一张表里,也意味着表结构设计不能像过去那样随意——你不能为了图省事把 10MB 的原始文本塞进 item,DynamoDB 的 item 大小限制是 400KB,长文本得拆开存放,向量字段只起到“同一条记录里的检索入口”的作用。
还有一点必须在选型阶段想清楚:查询模型是受限的。DynamoDB 擅长的是“在某一分区键范围内执行 TopK 向量检索”,如果你有“全局范围内按复杂业务规则重排向量”的需求,它就比较吃力。架构变简单了,但简单的前提是你把业务场景收敛到了它擅长的范围里。这个判断,做架构的人一定要亲力亲为,不能只凭“新功能很香”就拍板。
3. 影响二:事务写入带来的一致性红利
3.1 一致性问题被忽略的后果
RAG 类应用最烦的一个 bug,就是知识更新了,向量库里搜到的还是旧内容。我见过一个生产事故:某公司的内部制度文档做了更新,新版本要求审批流程多一道环节,但向量库里还是旧文本。员工在智能问答里提问“报销审批流程”,AI 老实地按旧流程回答,等到财务打回单子才发现出了问题。
问题根源就在双写。业务库先更新,异步同步任务还没跑到向量库,或者跑到一半失败了,就出现了时间窗口。这个窗口可能是几百毫秒,也可能是几小时,取决于同步管道的忙碌程度和重试策略。
还有一类问题更隐蔽:撤回。业务文档被标记为“已删除”或“已失效”,业务库里已经查不到了,但向量库里的向量还孤独地存在。应用层如果没有额外过滤,用户依然能搜到已经撤回的内容。
3.2 利用事务能力把写入做成原子操作
DynamoDB 的 TransactWriteItems 支持在同一事务里更新多条 item。因为向量字段就存业务记录这条 item 里,你就可以做到“元数据 + 文本 + 向量”的原子写入。
这里有一个技术理解上的关键点:DynamoDB 的向量索引是跟随表中 item 数据自动更新的,不是走独立管道。事务提交成功,就意味着这条记录带着最新的向量进去了。更新和删除同理。不像过去的架构,要么用二阶段提交硬撑着一致性,要么靠补偿任务处理对账。
当然,向量本身还是需要外部模型生成的。你的流程依然是两步:先调用 embedding 模型拿到向量,再把“业务字段 + 向量”作为一条 item 事务写入 DynamoDB。但至少从“写入存储”这个环节开始,业务数据和向量是原子的。不再存在“业务库已经更新而向量库还没更新”的中间态。
3.3 一致性给 AI 应用带来的三个价值
第一,知识时效性。文档流转状态和向量同步变更,新版本发布后用户立刻能搜到,旧版本立刻从结果里消失。对于知识管理这类“内容经常修订”的场景,这是刚需。
第二,权限安全。很多企业知识库要求按部门、岗位过滤可见内容。过去向量库和业务库权限分家,最怕出现“向量库搜到了但元数据没过滤”的越权风险。现在把权限标签和向量放在同一条 item 里,查询时用 FilterExpression 一起过滤,越权路径被直接封死。
第三,审计与合规。DynamoDB 的 PITR(时间点恢复)可以同时恢复到业务数据和向量数据。过去两套系统要做灾难恢复演练,还要保证恢复时间点一致,麻烦得要命。现在一个备份策略就覆盖了全部检索数据,恢复出来的表可以直接提供查询服务。
3.4 读取一致性与应用层兜底
有一点要说清楚,不要产生“写入后立即强一致”的误解。DynamoDB 的读取分为强一致和最终一致两种,向量索引提供一个异步更新后的最终一致视图。权限变更这种要求立即生效的场景,我建议业务层做双保险:向量查询负责召回,再对召回的 item 做一次基于主键的强一致读取,确认当前状态没有变化再返回给上层。这比在存储层钻牛角尖更实际。
4. 影响三:成本与运维开销的重新计算
4.1 独立向量库的隐藏成本清单
很多团队在选型时只对比“引擎节点多少钱”,忽略了全链路成本。独立向量库方案的真实开销包括但不限于:
| 成本项 | 独立向量库方案 | DynamoDB 原生方案 |
|---|---|---|
| 存储 | 向量数据独立存储,与业务库各存一份 | 向量作为字段存在同一 item,存储叠加但无独立副本 |
| 计算节点 | 至少 1-2 台实例做引擎节点,按内存/CPU 预留 | 使用 DynamoDB 容量单位,无需管理服务器 |
| 同步管道 | Lambda、消息队列、重试任务等额外计算成本 | 省去 Streams 到向量库的中间链路 |
| 备份 | 向量库备份独立管理 | 复用 DynamoDB PITR,自动覆盖向量 |
| 监控运维 | 额外告警、日志、权限、版本升级等工作 | 复用 IAM、CloudWatch、现有告警体系 |
| 团队学习成本 | 团队成员需理解向量库的部署、分片、容量规划 | 复用已有 DynamoDB 知识,学习曲线短 |
这还不算冷启动阶段:向量库建集群、调参数、压测所需要的时间。时间在技术选型中的成本,经常被低估。
4.2 原生方案的计费模型
DynamoDB 中向量索引会占用额外存储空间和容量单位。KNN 查询本质上是扫描索引并计算距离,这部分计算会计入读取容量消耗。维度越高,索引越大,查询消耗也越高。
一个粗略的数量级估算:假设你有 10 万条知识,每条向量 512 维,存储开销大约在 GB 级;按照订单的业务量,如果用按需容量模式,月成本也能控制在“一顿团队聚餐”的范围内。如果你想精确估算,建议直接拿真实数据做一次压测,不要靠拍脑袋。我只能说,在百万级向量以内,原生方案的总拥有成本通常比独立向量库低一个量级。
容量模式的选择也有讲究。知识库场景通常是“读多写少”,写入集中在内容发布时,查询分散在整天。如果你的查询量稳定,预置模式更容易控预算;如果业务波动大,比如每月月末集中提问,按需模式能避免突刺把业务打挂。选预置模式时记得把告警阈值配好,我见过不止一个团队因为忘了配置容量告警,在发布新知识库时被 WCU 限流打了个措手不及。
4.3 边际成本曲线与分水岭
任何架构选型都要看规模拐点。我自己的经验分水岭大致是:
- 100 万向量以内:原生方案综合性价比明显占优;
- 100 万到 1000 万向量:开始需要认真压测,关注查询并发和过滤条件复杂度;
- 1000 万以上向量、纯向量检索场景:专用引擎的分片、并行、混合检索能力更强,性价比反超。
这不是说原生方案“不能上量”,而是你要意识到:DynamoDB 的吞吐上限不变,区域分片自动管理,但你的查询模式如果触发热分区,性能就会受到明显影响。关于热分区的问题,我在第 5 部分详细展开。
4.4 运维复用的隐性收益
运维层面的隐性收益,往往比账单数字更有价值。团队已经熟悉 DynamoDB 的 IAM 权限模型、CloudWatch 指标、S3 集成备份,就不需要再学一套新的向量数据库控制台、新的查询语法、新的告警配置方式。
特别是在安全合规要求高的企业里,DynamoDB 原生方案能直接复用现有的审计体系。数据加密、细粒度权限控制、VPC 终端节点配置都沿用一套标准。过去你给独立向量库单独开安全组、单独做网络隔离、单独写审计策略,现在这部分工作直接归零。
5. 选型判断:三个影响之外,必须考虑这些边界
5.1 它不是一个通用向量数据库
DynamoDB 原生向量搜索的最大优势也对应着它的最大限制:它是一个高度集成的功能,不是一个全功能向量数据库。
KNN 查询语义很强,但如果你需要以下能力,就要打问号:
- 多路召回后做 RRF(倒数排名融合)重排;
- 对向量结果做复杂的深度过滤,例如“标签 A 或(标签 B 且标签 C)再按向量排序”;
- 支持多模态向量(图片、音频)在同一索引内混合检索;
- 自定义距离函数或特定索引算法调参。
这些场景里,独立向量数据库或 OpenSearch 仍然是更好的选择。选型时别拿着功能清单逐项对比,先把你的查询模式写清楚,再决定用哪个底座。
5.2 维度上限与索引配额
技术限制是绕不开的。DynamoDB 对向量维度有上限,你必须把你的 embedding 模型控制在支持范围内。业内常见的 384、512 维向量基本没问题,但 1024 维以上就要格外小心,先去官网文档查清楚再动手。
索引数量、单表规模、分区键设计都有约束。设计表结构时,一个常见做法是:把知识库按业务领域拆成多张表,每张表的分区键是业务域 ID,排序键是文档 ID。查询时先落在业务域分区,再做向量检索,既能缩小范围,又避免把大量向量堆在同一个分区造成热分区。
5.3 团队现有技术栈的适配性
做选型不能只看功能强弱,还得看团队能不能接住。如果你团队里已经有人熟练使用 OpenSearch,那扩一个 k-NN 插件可能半天就搞定了。如果从零起步,团队更熟悉 Python 和 NoSQL,DynamoDB 原生方案的学习成本明显更低。
还有一类情况:你的知识库需要和 PostgreSQL 里的业务强绑定。那与其硬迁到 DynamoDB,不如先用 pgvector 做一轮验证,可能更快出结果。技术选型不存在绝对最优,只有适合当前业务、当前团队、当前预算的最优。
5.4 三个“防坑”建议
我整理几次项目中的教训,给你们三个保命建议。
第一,先做 PoC,再用真实业务文本验证召回效果。不要拿公开的新闻语料或法律数据集跑完,就宣布方案可行。企业知识库的文本风格、术语密度、查询表达方式与公开数据集差别很大,真实环境里的召回率可能差 20 个点。
第二,embedding 模型一旦确定并写入了数据,就不要随意更换。换模型意味着所有向量的分布空间变了,旧向量和新向量之间的距离计算失去意义。必须重建整个向量索引,做全量数据回填。
第三,注意 item 大小限制。DynamoDB 单条 item 最大 400KB,大段文档正文不能直接塞进和向量同一个 item。推荐的存储方式是把文本拆成多个 chunk,每个 chunk 对应一条 item,并带上 document_id 关联原文档信息。
6. 实操视角:极简知识库向量检索 Demo
6.1 建表与创建向量索引
实操环节我用 Python 的 boto3 示例来说明,整体思路也适用于其他 SDK。建表时,分区键用doc_id,另外预留一个embedding字段作为向量索引的向量来源。
import boto3 dynamodb = boto3.resource("dynamodb", region_name="your-region") table = dynamodb.create_table( TableName="knowledge_base", KeySchema=[ {"AttributeName": "doc_id", "KeyType": "HASH"}, ], AttributeDefinitions=[ {"AttributeName": "doc_id", "AttributeType": "S"}, ], BillingMode="PAY_PER_REQUEST", )接着通过 DynamoDB 的向量索引 API 创建索引。不同版本 SDK 的方法名略有差异,核心参数是向量字段名、维度、距离度量方式,以及你要在索引里冗余哪些字段用于返回。
dynamodb_client = boto3.client("dynamodb", region_name="your-region") dynamodb_client.create_vector_index( TableName="knowledge_base", IndexName="embedding-index", VectorField="embedding", Dimension=512, Metric="COSINE", Projection={"ProjectionType": "INCLUDE", "NonKeyAttributes": ["title", "dept", "status"]}, )这里距离度量方式我建议优先选 COSINE。文本向量经过归一化后,余弦距离等价于内积,但在未归一化数据上,COSINE 对模长不敏感,更符合语义相似度的直觉。
6.2 写入一条带向量的 Item
正常写入知识条目时,先生成向量再和业务字段一起写入。
def put_document(doc_id, title, dept, content_vector): table.put_item( Item={ "doc_id": doc_id, "title": title, "dept": dept, "status": "ACTIVE", "embedding": content_vector, } )向量字段必须是同一维度的浮点数列表,类型别写成整数,否则索引只会报错。如果你用归一化后的向量,记得在写入前统一做一次归一化,而不是靠某个模型顺手输出的原始向量直接入库。
6.3 执行 KNN 查询
查询时的核心逻辑是:拿到用户问题的 query 向量,往向量索引里按 KNN 语义检索,同时用 FilterExpression 做权限和状态过滤。
response = table.query_knn( IndexName="embedding-index", QueryVector=[...], # 由 embedding 模型生成的 query 向量 K=10, FilterExpression="#s = :status AND #d = :dept", ExpressionAttributeNames={"#s": "status", "#d": "dept"}, ExpressionAttributeValues={":status": "ACTIVE", ":dept": "engineering"}, )返回结果中既包含相似度评分,也包含索引冗余字段。如果业务侧需要展示更多详情,再用 doc_id 去主表做一次按需读取。个人建议不要在大查询里遍历大量重复字段,保持索引精简,能显著降低容量消耗。
6.4 真实踩坑记录
我在这个功能上踩过的坑,挑四个典型的分享给你们。
第一个是余弦距离未归一化。有一次 PoC 里,我图省事直接把模型输出的原始向量写入索引,跑出来的 TopK 结果里,排在最前面的全是文本长度极长的段落。后来排查发现是向量模长影响了距离计算结果。文本嵌入模型训练时,通常建议使用归一化后的向量做余弦相似度,你不归一化,相似度排名就“失真”。
第二个是 FilterExpression 字段类型不一致。工程团队在写入时把dept字段存成字符串,查询过滤时却传了数值型。字段类型不匹配,过滤条件直接失效,等于把被过滤的文档也召回回来了。这个问题不好排查,因为查询日志里不会报错,只是结果集变大了。建议建表时严格规范属性类型,并用 IaC 代码把关。
第三个是维度选取过高。有一版方案选了 1536 维的 embedding 模型,写入 20 万条数据后,容量消耗比预期翻了三四倍,查询耗时也上去了。后来改成 512 维模型,召回效果几乎没有明显下降,成本却省了一大截。不是所有场景都需要最高维度的模型,在你的数据样本上做实验再定维度。
第四个是向量索引的 backfill 时间。建完向量索引后,DynamoDB 需要时间把已有数据回填进索引。在这段时间里执行 KNN 查询,返回结果是不全的。我当时测试的时候以为代码写错了,折腾了半天才发现索引状态还没变成 Active。生产上线前一定要检查索引状态,别让用户搜到一个空结果集。
6.5 落地节奏建议
如果你们团队决定采用 DynamoDB 原生向量搜索,我建议用三阶段节奏推进。
阶段一,找一个小业务启动 PoC。最好是团队 wiki 或客服知识库这类的独立域,数据量不超过 5 万条,验证“向量检索 + 过滤 + 权限”三个主链路。PoC 的目标不是性能,而是确认召回效果和业务查询模式是否匹配。
阶段二,和运维同学对齐容量、备份、告警、权限分权。提前为向量索引预留监控面板,把容量告警阈值设好,PITR 备份策略覆盖到全表。
阶段三,灰度上线。先对内部搜索开放,观察检索质量、资源消耗和用户反馈,再逐步替换旧搜索接口。替换过程中保留旧接口作为回退方案,出现严重召回问题时能一键切回。
最后分享一个我个人的体会:在做选型决策时,别被“新功能”三个字冲昏头脑。技术选型的本质不是选最强大的产品,而是选最匹配业务的架构。DynamoDB 原生向量搜索让我觉得最有价值的,不是它多了一个能力,而是它让“业务数据和向量数据保持一致”这件事变得简单了。它减少了系统数量、缩短了数据链路、统一了运维模式,对大量中等规模的企业知识库场景来说,这已经是最好的结果。如果你们团队正处于知识库选型阶段,别只看官方文档,先拿自己的数据做一轮 PoC,再对照本文提到的架构影响、一致性红利、成本分水岭和边界条件,做一个稳妥的决定。