第5章:鲲鹏路线 2——基于 UB 内存池的全局向量检索
用 UB 共享底座装下千亿级 HNSW 整图,避开 HNSW 分片方案的召回质量损失——展开整图布局、跨节点访问模式、典型 P99 延迟、以及与 HNSW 分片 / DiskANN / SPANN 的精确对照
Ch3 给出第二条缝隙:千亿规模 + 全图整库 + 高召回,能不能同时做到? HNSW 在中等规模上是高召回标杆,但单机内存装不下千亿级——分片之后召回质量必然下降。鲲鹏路线 2 的解法是用 UB 内存池作为多节点共享的”虚拟单机内存”,把完整的 HNSW 整图装进去,让所有 Agent 节点都看到同一份完整的图——避开分片召回损失,又拿到接近单机内存的 P99 延迟。这一章把这套系统的整图布局、跨节点访问、延迟分布、与三条主流路线的对照逐一拆开。
📑 目录
- 1. 鲲鹏路线 2 的工程目标
- 2. 整图在 UB 池里的内存布局
- 3. 跨节点访问模式与一致性
- 4. 千亿规模下的延迟与召回数字
- 5. 与三条主流路线的精确对照
- 6. 为什么这件事不是”用 RDMA 凑合”就行
- 自我检验清单
- 参考资料
1. 鲲鹏路线 2 的工程目标
1.1 把 Ch3 的能力对照矩阵再看一眼
| HNSW 整图 | HNSW 分片 | DiskANN | SPANN | 鲲鹏路线 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 全局整图的关键约束
要让”全局整图”工程上可行,必须解决三件事:
- 整图必须装得下——千亿向量 + 图结构 ≈ 数百 TB 量级,单机 DRAM 完全不可能
- 跨节点访问要快——查询过程中要跳几十次图节点,每跳如果是毫秒级就废了
- 共享读 + 偶尔写要保一致——图增量更新 / 删除时不能让查询读到坏数据
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 TB | 30 TB × 16 | ~30 TB × 16(缓存) | ~10 TB | UB 池 ~300 TB(共享) |
| 单机内存需求 | 不可行 | 30 TB / 节点 | 30 TB / 节点 | 10 TB | 任意(共享池) |
| recall@10 | 0.97 | 0.85-0.90(分片损失) | 0.95 | 0.85-0.92 | 0.96+(整图) |
| P50 延迟 | 5 ms | 15 ms | 8 ms | 12 ms | 1.5-2 ms |
| P99 延迟 | 20 ms | 100-300 ms | 50-200 ms | 80-200 ms | 5-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@10 | 0.85-0.90 | 0.96+ |
| P99 | 100-300 ms | 5-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?
| 维度 | 普通 RDMA | UB |
|---|---|---|
| 编程模型 | 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 的内存做高召回检索”做到极致。这是当成本压力大于召回压力时的另一种工程选择。