跳到主要内容
Agent Memory 分离式协同

第5章:鲲鹏路线 2——基于 UB 内存池的全局向量检索

用 UB 共享底座装下千亿级 HNSW 整图,避开 HNSW 分片方案的召回质量损失——展开整图布局、跨节点访问模式、典型 P99 延迟、以及与 HNSW 分片 / DiskANN / SPANN 的精确对照

鲲鹏 UB 内存池 HNSW 全局向量检索 千亿规模 整图召回

Ch3 给出第二条缝隙:千亿规模 + 全图整库 + 高召回,能不能同时做到? HNSW 在中等规模上是高召回标杆,但单机内存装不下千亿级——分片之后召回质量必然下降。鲲鹏路线 2 的解法是用 UB 内存池作为多节点共享的”虚拟单机内存”,把完整的 HNSW 整图装进去,让所有 Agent 节点都看到同一份完整的图——避开分片召回损失,又拿到接近单机内存的 P99 延迟。这一章把这套系统的整图布局、跨节点访问、延迟分布、与三条主流路线的对照逐一拆开。

📑 目录


1. 鲲鹏路线 2 的工程目标

1.1 把 Ch3 的能力对照矩阵再看一眼

HNSW 整图HNSW 分片DiskANNSPANN鲲鹏路线 2
千亿规模⚠️
高召回(>0.95 recall@10)❌(分片局部最优)⚠️
P50 延迟 ≤ 10ms⚠️
P99 延迟 ≤ 50ms⚠️⚠️
单机内存可装❌(合计)⚠️⚠️N/A(用共享池)

🌟 核心目标:把”千亿规模 + 高召回 + 低 P99”这三件事同时做到——这是 HNSW / DiskANN / SPANN 在传统硬件下做不到的事。

1.2 为什么是”全局整图”而不是分片

回顾 HNSW 分片方案的两个具体伤害:

伤害 1:每个分片只看自己的局部图

  • 全图视角下”距离 query 最近的 100 个邻居”可能分布在多个分片里
  • 分片方案让每个分片各自找”自己分片里最近的 100 个”——其中可能根本没有真正的全局最近邻
  • 这是召回质量下降的根因

伤害 2:跨分片合并的尾延迟不可控

  • N 个分片各自返回 top-k 后,合并阶段要做 N×k 的 merge
  • 跨节点网络往返的 P99 比 P50 经常差 10-100 倍
  • 千亿级别下,分片数量越多,合并尾延迟越糟

🍎 直觉比喻:HNSW 分片好比”把全国人口按省分开存档,找’最像 X 的 10 个人’要让每个省各自找 10 个,再从 31 × 10 个里选 10 个”——但全国最像的 10 个人可能根本不在你查的省里。

1.3 全局整图的关键约束

要让”全局整图”工程上可行,必须解决三件事:

  1. 整图必须装得下——千亿向量 + 图结构 ≈ 数百 TB 量级,单机 DRAM 完全不可能
  2. 跨节点访问要快——查询过程中要跳几十次图节点,每跳如果是毫秒级就废了
  3. 共享读 + 偶尔写要保一致——图增量更新 / 删除时不能让查询读到坏数据

UB 内存池同时满足这三件事:

  • 池容量跨节点合池,可承载数百 TB
  • 微秒级跨节点访问,每跳 < 5 µs
  • load/store 语义 + 原子操作,可做轻量同步

2. 整图在 UB 池里的内存布局

2.1 HNSW 整图的核心数据结构

先回忆 HNSW 的内部结构:

Layer L (顶层稀疏图)
   ●─────●─────●
       /         \
       /           \
Layer L-1
   ●──●─●─●──●─●─●─●
   ...
Layer 0 (底层稠密图,包含所有向量)
   ●●●●●●●●●●●●●●●●●●●
  • 每个向量在 Layer 0 都有一个节点
  • 向量按概率分布出现在更高层(顶层只有 ~log(N) 个节点)
  • 每个节点存:原始向量 + 邻居列表

2.2 在 UB 池里的物理布局

把这个图装进 UB 池,鲲鹏路线 2 采用 “节点级 + 邻居级双层布局” 的方式:

UB 池里:

[NodeRegion]                    [EdgeRegion]
┌────────────────┐              ┌────────────────┐
│ id: 1          │              │ node_id: 1     │
│ vector[768]    │              │ neighbors:     │
│ layer: 3       │              │   layer 3: [...]│
│ ptr to edges   │ ───────────► │   layer 2: [...]│
└────────────────┘              │   layer 1: [...]│
                                │   layer 0: [...]│
                                └────────────────┘

为什么把 vector 和 neighbors 分两个 region

  • 查询主要靠邻居列表导航——大部分时候不需要读 vector
  • 只有最后做距离计算时才需要读 vector
  • 分开布局让查询过程中只读邻居那部分,节省一倍带宽

2.3 区域分布与节点亲和性

虽然是”全局整图”,但 UB 池仍然有物理位置——某段 region 物理上在节点 A 上比在节点 B 上访问快。鲲鹏路线 2 利用这个事实做”亲和性放置”:

Cluster: 8 nodes, 100B vectors total
                                                              
Node 1 (主)         Node 2          Node 3          ...     Node 8
┌──────────┐       ┌──────────┐    ┌──────────┐            ┌──────────┐
│ NodeRgn  │       │ NodeRgn  │    │ NodeRgn  │            │ NodeRgn  │
│ 0..12.5B │       │ 12.5B..  │    │ 25B..    │            │ 87.5B..  │
│ EdgeRgn  │       │ EdgeRgn  │    │ EdgeRgn  │            │ EdgeRgn  │
└──────────┘       └──────────┘    └──────────┘            └──────────┘


                  ┌────────────────────────┐
                  │   UB 共享池(虚拟全局) │
                  │   每个节点访问任意      │
                  │   region 都是 µs 级     │
                  └────────────────────────┘

注意:每个 Node 物理上”主驻”了 1/8 的图,但所有 Node 都能跨节点访问任何 region——这就是”全局整图”的物理实现。

3. 跨节点访问模式与一致性

3.1 一次查询的跨节点访问 trace

让我们 trace 一次完整 ANN 查询。假设来到 Node 1 的一个查询:

Query 在 Node 1 入口


读 Layer 3 入口节点(恰好在本地) → 5 ns


跳 Layer 3 邻居(一半本地,一半 Node 4) → 邻居读取本地 5 ns + Node 4 读 2 µs


跳 Layer 2 邻居(更多 Node 5、Node 6) → 平均 ~2 µs / 跳


跳 Layer 1, 0 ... 大量跳跃


最后阶段:top-k 候选集精确距离计算


返回 top-k

3.2 整查询的延迟 breakdown

把每段时间合在一起,千亿级整图查询的 P50 延迟分布:

阶段跳数 / 操作单段时间累计
Layer 3 (顶层,本地居多)~5 跳~10 ns / 跳~50 ns
Layer 2~10 跳~1-2 µs / 跳~15 µs
Layer 1~20 跳~1-2 µs / 跳~30 µs
Layer 0 (底层稠密)~50 跳~1-2 µs / 跳~75 µs
精确距离计算 (top-k)k=200 个 vector~3 µs / vec~600 µs
跨节点合并 / 排序-< 100 µs~700 µs
合计 P50~1.5 ms
合计 P99~5-10 ms

关键观察:千亿规模整图 P99 ~10ms——这是单机 DRAM 装不下的规模上做到接近单机 DRAM 整图 P99(~5ms)的相同量级。这就是 UB 池的价值。

3.3 一致性策略:Read-Mostly + 增量批次更新

整图查询是读密集的,但图本身偶尔需要增量更新(新向量插入、旧向量删除、邻居列表更新)。鲲鹏路线 2 的一致性策略:

操作频率一致性级别
查询(读邻居 / 读 vector)每秒数万最终一致(容忍 ms 级旧数据)
单点插入数百 / 秒不阻塞查询
邻居列表更新数百 / 秒用 CAS / 原子写入
批量重建(每日 / 每周)极低频双 buffer 切换

具体机制:

  • 节点级 epoch:每个节点有一个 epoch 号,写时递增
  • 查询带 epoch tolerance:查询时只要 epoch 在 [now-Δ, now] 范围内就接受
  • CAS 原子更新邻居指针:保证查询不会读到”半更新”的邻居列表
  • 批量重建用双 buffer:新版本图在另一段池里构建好后原子切换入口指针

🌟 关键判断:HNSW 整图本身就是读密集 + 增量写入的工作负载——这和 UB 池的硬件特性(高带宽顺序读 + 微秒级随机读 + 跨节点原子写)完美匹配。

4. 千亿规模下的延迟与召回数字

4.1 三个对比基线

让我们把鲲鹏路线 2 和三条主流方案在同一千亿规模下放在一张表里。规模设定:100B 向量,768 维 float32,原始 ~300 TB。

指标HNSW 整图(理论)HNSW 分片(16 节点)DiskANN(多节点)SPANN鲲鹏路线 2
总内存需求~300 TB30 TB × 16~30 TB × 16(缓存)~10 TBUB 池 ~300 TB(共享)
单机内存需求不可行30 TB / 节点30 TB / 节点10 TB任意(共享池)
recall@100.970.85-0.90(分片损失)0.950.85-0.920.96+(整图)
P50 延迟5 ms15 ms8 ms12 ms1.5-2 ms
P99 延迟20 ms100-300 ms50-200 ms80-200 ms5-10 ms
介质年成本(粗估)1.5M USD~1.5M USD~150K USD~80K USD~120K USD

核心结论:鲲鹏路线 2 在 召回质量、P99 延迟、介质成本 三个维度上同时优于任何一条主流路线——这是 UB 池作为”中间档介质 + 跨节点共享”的直接红利。

4.2 这些数字背后的关键事实

事实 1:召回质量从 0.85 (分片) 跳到 0.96 (整图)——这 11 个百分点是业务可见的差异。比如风控场景下,召回从 85% 升到 96% 意味着漏检率从 15% 降到 4%——这是用户体验的台阶式改善。

事实 2:P99 延迟从分片的 100-300ms 砍到 5-10ms——这是 20-30 倍的改善。这一项把”千亿底库 + 50ms SLA”从不可达变成了”主要负载下都能满足”。

事实 3:介质成本仍然只是全 DRAM 方案的 1/12——UB 池虽然不便宜,但比单机 DRAM 池仍有数量级优势。

5. 与三条主流路线的精确对照

5.1 vs HNSW 分片:召回 + P99 双赢

维度HNSW 分片鲲鹏路线 2
全图视角❌ 每分片只看局部✅ 所有节点共享同一图
跨分片合并❌ 网络合并尾延迟差✅ 池内访问,无显式合并
recall@100.85-0.900.96+
P99100-300 ms5-10 ms

核心差异:分片是”逻辑分割”——每个节点只看自己那块。鲲鹏路线 2 是”物理分布、逻辑统一”——每个节点都能访问完整图。

5.2 vs DiskANN:当 SSD 不够快时

DiskANN 是优秀的方案,但它有两个对千亿规模的具体短板:

  • 每跳的 SSD 访问 ~80 µs——50 跳就是 4 ms 在介质上,比 UB 慢 ~50 倍
  • PQ 量化做内存常驻是为了少访问 SSD——但量化损失精度
  • P99 受 SSD 尾延迟波动影响——高 IOPS 下尾部可能突增到几百 ms

UB 池本质上是把 DiskANN “希望但拿不到” 的”接近 DRAM 的随机读”做出来——同时容量比 DRAM 大 10-100 倍。

5.3 vs SPANN:聚类 vs 整图的本质取舍

SPANN 用聚类把”必须搜的范围”砍到几个簇——这是个聪明的工程取舍。但代价是:

  • 召回受聚类质量约束——簇间的”边界向量”经常被漏掉
  • 多查询可能命中同一组簇——并发性受限
  • 增量更新比 HNSW 更复杂——添加新向量需要重新评估它属于哪个簇

鲲鹏路线 2 选择了不做聚类——直接用整图的全局视角。这条路径在 UB 池之前不可行(单机内存装不下),UB 池让它第一次成为工程现实

🌟 关键判断鲲鹏路线 2 的工程贡献不是”发明了新算法”,而是”让一个 30 年来都被认为不可行的算法(千亿级 HNSW 整图)变得工程可行”

6. 为什么这件事不是”用 RDMA 凑合”就行

工程师可能会问:RDMA 也能做跨节点共享内存——为什么非要 UB?

维度普通 RDMAUB
编程模型verbs API(手工管理)load/store 语义(透明)
单次访问延迟~5-10 µs~1-3 µs
单次访问粒度RDMA Read 操作 (MTU)cache line 级
多 NIC 设计多 QP 维护复杂池化,无 QP 概念
原子操作有限(compare-and-swap on 64bit)更丰富的硬件级原子

简而言之:

  • 普通 RDMA 是**“网络上的内存”**——你得用网络协议的方式去用它
  • UB 是**“协议上的内存”**——你能用普通指针 / load/store 去用它

对于 HNSW 整图查询这种”50+ 跳每跳几十 µs”的工作负载,UB 的 µs 级 + load/store 是关键。在普通 RDMA 上把同样的图查跑一遍,平均每跳要多 5-10 µs,整体 P99 会从 10ms 拉到 50ms 以上——这就回到了和分片 / DiskANN 同档的水平。

核心判断UB 不是 “RDMA 的别名”——它是 “load/store 语义级别”的跨节点内存协议。这层语义差异直接决定了 HNSW 整图查询是工程可行还是不可行。

🎯 自我检验清单

  • 千亿规模下 HNSW 整图、HNSW 分片、DiskANN、SPANN 各自的核心短板是什么?鲲鹏路线 2 是怎么避开它们的?
  • 解释为什么 HNSW 分片方案的召回从 0.97 跌到 0.85——这件事和”图是局部连通而不是全局连通”有什么关系?
  • UB 池里 NodeRegion 和 EdgeRegion 为什么要分开布局?
  • 整图查询在 UB 池里 P99 ~10ms——这个数字和单机 DRAM 整图 P99 是同一量级吗?为什么这件事很重要?
  • UB 和普通 RDMA 在 Agent Memory 工作负载上的核心差距是什么?

📚 参考资料

  • 鲲鹏 Agent Memory 创新方案(华为鲲鹏团队 2026 公开发表)—— 路线 2 详细技术披露
  • HNSW(Malkov & Yashunin, TPAMI 2018)
  • DiskANN(Microsoft NeurIPS 2019)
  • SPANN(Microsoft NeurIPS 2021)
  • 本系列模块十三 第 2-4 章:RDMA / CXL 协议详解
  • CXL 3.0 Specification(2024)—— 内存池硬件协议参考
  • 本系列模块十四 第 5 章:向量索引的层次化设计

下一章预告:Ch6 进入鲲鹏路线 3——超低内存混合介质向量检索(RabitQ + PCA + SIMD + UB + SSD 混合)——把”用 1/100 的内存做高召回检索”做到极致。这是当成本压力大于召回压力时的另一种工程选择。