向量嵌入存同一张表还是单独建表?实测Postgres连接开销约 0.1–0.2 毫秒
谷歌AI开发者博客作者Gleb Otochkin在AlloyDB Omni上用 3 万行商品数据对比两种向量存储布局,发现把嵌入拆到独立表后,语义搜索、带过滤搜索和多表聚合的总耗时最多只增加约 0.2 毫秒。
AI解读:这条测试回答的是一个具体取舍:把向量嵌入和业务数据放同一张表,还是拆到单独的表。拆开的好处是换嵌入模型时可以整表替换、方便做版本对比和维护;代价是每次语义搜索都要多一次主键连接。作者实测的结论是,这个代价比多数人想象的小。
测试用 3 万行商品数据和真实嵌入,在AlloyDB Omni上跑。纯语义搜索取前 10 条,同表方案总耗时约 2.10 毫秒,独立表加连接约 2.20 毫秒;带品类和价格过滤的搜索相差约 0.1 毫秒;再叠上评论聚合的多表连接,总耗时 2.6–2.7 对 2.8–2.9 毫秒。
换句话说,向量搜索真正的耗时大头通常不在这次连接上,作者称在他经验里模型推理带来的波动往往比主键连接更大。需要说明的是,这是特定表结构、3 万行数据和单次查询下的结果,换数据量、索引或查询写法都可能不同,作者也只是建议按自己的场景实测,而不是给出通用结论。
谷歌AI开发者博客作者Gleb Otochkin发布一篇实测,比较在Postgres系数据库里把向量嵌入与业务数据放在同一张表,和放进专用嵌入表两种布局的性能差异。测试在AlloyDB Omni上完成,用的是 3 万行示例商品数据,嵌入基于商品描述生成,向量维度 768。
作者给出的背景是:嵌入模型在持续更新,嵌入需要定期用新版本刷新;上一篇文章讨论了刷新嵌入导致的表膨胀、TOAST段和索引问题,因此提出把嵌入放到专用表作为替代布局。这篇实测的是这种拆分带来的查询性能代价。
测试覆盖三类查询:纯语义搜索、带过滤的语义搜索、以及多表连接加聚合。同表布局下,嵌入列直接存在ecomm.products表,并建有hnsw(embedding vector_cosine_ops)索引;拆分布局下,嵌入存在ecomm.product_embeddings表,主键为product_id,同样建hnsw索引,通过product_id与商品表连接。
纯语义搜索:执行耗时差 0.16 毫秒,总耗时差 0.1 毫秒
纯语义搜索取前 10 条,查询按嵌入与搜索向量的相似度排序并LIMIT 10。嵌入存在ecomm.products时,执行计划走idx_products_vector的HNSW索引,执行时间约 1.224 毫秒,总响应时间约 2.10 毫秒。作者说明总响应时间高于执行计划中的执行时间,是因为叠加了规划、网络和客户端展示结果的软件开销。
嵌入拆到ecomm.product_embeddings后,执行计划变为嵌套循环:先在嵌入表上用HNSW索引取前 10 条,再按主键products_pkey回商品表取字段。执行时间约 1.381 毫秒,总响应时间约 2.20 毫秒。作者称执行时间稳定在 2.2 毫秒左右。
作者提到,把LIMIT从 10 提高到 1000,响应时间只增加约 0.3 毫秒,并称PostgreSQL的主键连接很快。
- 同表布局:执行约 1.22 毫秒,总耗时约 2.10 毫秒
- 独立表布局:执行约 1.38 毫秒,总耗时约 2.20 毫秒
- 差值:执行 +0.16 毫秒,总耗时 +0.10 毫秒
带过滤搜索:总耗时 2.20 对 2.3–2.4 毫秒
带过滤的语义搜索在向量排序之外增加了品类和零售价两个条件:category = 'Outerwear & Coats'、retail_price BETWEEN 30 AND 150,仍取前 10 条。
嵌入在ecomm.products时,执行计划在向量索引扫描之外多了一步过滤,执行时间约 1.31 毫秒,总耗时约 2.20 毫秒。
嵌入拆到专用表时,查询在嵌入表上先取 17 条候选,再回商品表按主键逐条匹配并过滤,执行时间约 1.41 毫秒,总耗时 2.3–2.4 毫秒。作者称两项相差约 0.1 毫秒。
- 同表布局:执行约 1.31 毫秒,总耗时约 2.20 毫秒
- 独立表布局:执行约 1.41 毫秒,总耗时 2.3–2.4 毫秒
- 差值:执行 +0.10 毫秒,总耗时 +0.10–0.20 毫秒
多表连接加聚合:总耗时 2.6–2.7 对 2.8–2.9 毫秒
第三个场景在过滤搜索基础上,对取出的前 10 条商品左连接ecomm.product_reviews,统计评论数并计算平均评分,按相似度排序输出。
嵌入在ecomm.products时,该查询总耗时稳定在 2.6–2.7 毫秒;改用ecomm.product_embeddings后,总耗时 2.8–2.9 毫秒,相差约 0.2 毫秒。作者称第二种写法多了一次嵌入表与商品表的连接,除此之外执行路径相似,并说明这个场景的单条执行计划较长,没有在文中展开。
- 同表布局总耗时:2.6–2.7 毫秒
- 独立表布局总耗时:2.8–2.9 毫秒
- 差值:约 +0.20 毫秒
作者结论:开销可能很小,但要按自己的数据实测
作者汇总称,对比嵌入与源数据同表存储、以及拆到专用表存储,性能差异相对较小,多数情况下响应时间不超过 0.2 毫秒。他也指出,具体表现会随表结构、数据和单条查询不同而变化,因此建议实际测试后再考虑专用嵌入表的布局。
作者给出的理由是:这种布局有助于未来的模型维护和模型评估——只需再加一张装新模型嵌入的表,用新生成的嵌入跑查询即可。他还表示,如果schema设计得当,开销有可能保持在很低水平;在他看来,模型推理时间带来的波动通常比基于主键的嵌入表连接大得多。
- 多数测试场景响应时间差值不超过 0.2 毫秒
- 作者的适用范围声明:取决于表结构、数据和查询
- 作者建议:实测后考虑专用嵌入表布局,便于模型维护和评估