存储计算融合架构 + DiskANN + Milvus + RaBitQ 设计方案
一、产品定位与核心指标
1.1 产品定位
本方案面向百亿级向量规模、成本敏感型的检索场景,以3节点 NVMe SSD 本地存储集群为底座,将 Milvus 原生部署于融合架构上,通过 DiskANN + RaBitQ 最大化硬件性价比。
1.2 目标性能指标
| 指标 | 目标值 | 来源/依据 |
|---|---|---|
| 单节点 QPS | ≥600 | 单机 NVMe 优化后可达 600+ QPS(批量=1, top-k=10) |
| 3节点总 QPS | 1500+ | 水平扩展接近线性 |
| P99 延迟 | ≤20ms(查询) | 磁盘索引的典型时延范围 |
| 内存占用 | 社区版 DiskANN 的 1/5 以下 | 火山 Milvus 集成 RaBitQ 后内存降低 85% |
| 召回率 | ≥95% | RaBitQ 配合 PQ 可达 95% 召回率 |
| 支撑规模 | 10亿~100亿向量(768维) | 3节点融合架构容量测算 |
1.3 磁盘索引理论提升数据
根据公开测试数据,火山 Milvus 集成 DiskANN+RaBitQ 后,磁盘索引 QPS 达到 Milvus 社区版的 5倍以上,Per QPS 单价降低 80%。性能版磁盘索引内存仅需 48GB,而社区版需要 192GB。此为本方案的核心技术增益理论基准。
注:以上数据来源于特定测试环境,实际生产部署需根据自身数据集规模和硬件条件进行基准测试验证。
二、硬件规划与资源分配
2.1 3节点硬件清单(每节点)
基于 DiskANN 对存储的严格要求和 3 节点融合架构的负载均衡考量,每节点配置如下:
| 组件 | 规格 | 数量 | 说明 |
|---|---|---|---|
| CPU | AMD EPYC 7443P (24核/48线程, PCIe 4.0) 或 Intel Xeon Gold 6330 | 1 | 多核适用于 RaBitQ 解码计算 |
| 内存 | 128GB~256GB DDR4-3200 | 1 | 单节点 256GB 可承载 10亿+ 向量 |
| NVMe(数据) | U.2 3.84TB (PCIe 4.0, 1 DWPD) | 2 | RAID0 做向量段/索引数据 |
| NVMe(WAL/元数据) | U.2 1.92TB (PCIe 4.0, 3 DWPD) | 1 | 单盘,专职写入密集型操作 |
| 系统盘 | 960GB SATA SSD | 2 | RAID1 安装 OS |
| 网卡 | 10GbE x2 | - | LACP 链路聚合 |
容量规划公式:
向量原始数据大小 = 向量数 × 维度 × 4字节(FP32) DiskANN索引文件 ≈ 原始数据 × 1.5~2倍 总磁盘需求 = 原始数据 × 2.5~3倍(含 WAL 和元数据)
3节点总数据容量(实测,含索引 + WAL) :~15TB~23TB,可支撑:
-
768维向量:约 5亿~8亿条(FP32)
-
1024维向量:约 4亿~6亿条
2.2 单节点资源分配
| 用途 | CPU | 内存 | 磁盘 |
|---|---|---|---|
| Milvus Query Node | 8核 | 64GB | /var/lib/milvus/data (NVMe RAID0) |
| Milvus Index Node | 4核 | 32GB | /var/lib/milvus/data (共享) |
| Milvus Proxy | 2核 | 8GB | - |
| etcd | 2核 | 8GB | /var/lib/etcd (NVMe WAL盘) |
| MinIO (S3兼容) | 4核 | 32GB | /var/lib/minio (NVMe RAID0) |
| 预留/其他 | 4核 | 112GB | - |
| 总计 | 24核 | 256GB | 2×3.84TB(NVMe数据) + 1×1.92TB(NVMe WAL) |
此分配是基于单节点硬件清单(共24核 / 256GB内存)的分解。若实际CPU核心数不同,请按比例调整各组件分配。
核心原则:NVMe 数据盘采用 XFS 格式(noatime,inode64),WAL 盘采用 ext4(noatime,commit=120)并定期 fstrim;务必关闭 BIOS 的 C-State 深度休眠和 PCIe ASPM,以防 NVMe 随机读性能下降。
三、软件架构设计
3.1 Milvus 原生架构与融合部署的对比
Milvus 2.x 采用存储计算分离(disaggregated architecture)架构设计,主要分为四个层:Access Layer(Proxy)、Coordinator Service、Worker Node(QueryNode/IndexNode/DataNode)、Storage Layer。其原生架构是存算分离的:Worker Node 负责计算,MinIO/S3 负责持久化存储。
本方案选择存储计算融合部署的原因:
-
DiskANN 需要 NVMe SSD 本地数据路径才能发挥性能,而存算分离架构中数据存储层(MinIO/S3)与计算层分离,会导致查询时跨网络访问向量数据,增加 I/O 延迟
-
3节点小规模集群下,融合部署简化运维,避免额外的对象存储集群开销
-
通过 Docker Compose 将每个节点的计算组件(QueryNode、IndexNode)与存储组件(MinIO)部署在同一物理节点上,实现物理融合、逻辑分离——组件间仍通过内部网络通信,但数据读写发生在本地 NVMe SSD 上
3.2 3节点融合部署架构图
-
所有节点运行完整的 Milvus 组件栈(Proxy + QueryNode + IndexNode + DataNode + MinIO)
-
数据持久化在本地 NVMe SSD,确保 DiskANN 磁盘 I/O 达到最优
-
etcd 作为 3 节点 Raft 集群运行,提供分布式协调与元数据存储
-
Proxy 层多节点部署,建议前置负载均衡器(Nginx/HAProxy)提供统一接入点
四、操作系统与磁盘调优
在部署 Milvus 之前,需要完成底层操作系统和 NVMe SSD 的性能调优。
4.1 操作系统要求
| 项目 | 要求 |
|---|---|
| 操作系统 | Ubuntu 22.04 LTS(Kernel 5.15+) |
| 内核参数 | aio-max-nr ≥ 10485760 |
| 文件系统 | XFS(数据盘)/ ext4(WAL盘) |
| 网络 | 10Gbps 内网互联 |
DiskANN 要求 Milvus 实例运行在 Ubuntu 18.04.6 或更高版本上。
4.2 系统调优脚本(每个节点执行)
#!/bin/bash
# 文件名: setup_node.sh
# 执行: sudo bash setup_node.sh
set -e
# 1. 设置异步IO上限(DiskANN 高并发IO必需)
echo "10485760" > /proc/sys/fs/aio-max-nr
echo "fs.aio-max-nr = 10485760" >> /etc/sysctl.conf
# 2. 优化网络参数(10G网络)
cat >> /etc/sysctl.conf << EOF
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728
net.core.netdev_max_backlog = 500000
EOF
sysctl -p
# 3. 安装依赖
apt-get update
apt-get install -y docker-ce docker-compose-plugin nvme-cli fio
# 4. 格式化 NVMe 数据盘(如果从新盘开始)
# 假设 /dev/nvme0n1 和 /dev/nvme1n1 为数据盘
mdadm --create /dev/md0 --level=0 --raid-devices=2 /dev/nvme0n1 /dev/nvme1n1
mkfs.xfs -f -m reflink=0 -d agcount=16 /dev/md0
mkdir -p /var/lib/milvus
mount -o noatime,inode64,nobarrier /dev/md0 /var/lib/milvus
echo "/dev/md0 /var/lib/milvus xfs defaults,noatime,inode64,nobarrier 0 0" >> /etc/fstab
# 5. 格式化 NVMe WAL 盘(假设 /dev/nvme2n1)
mkfs.ext4 -F /dev/nvme2n1
mkdir -p /var/lib/etcd
mount -o noatime,commit=120 /dev/nvme2n1 /var/lib/etcd
echo "/dev/nvme2n1 /var/lib/etcd ext4 defaults,noatime,commit=120 0 0" >> /etc/fstab
echo "节点配置完成,请重启后继续部署 Milvus"
4.3 磁盘性能基准测试
部署前必须验证 NVMe SSD 是否满足要求:
# 随机读 IOPS 测试(最关键指标)
fio --name=randread --ioengine=libaio --rw=randread --bs=4k \
--size=10G --numjobs=8 --runtime=60 --group_reporting \
--filename=/var/lib/milvus/testfile
# 期望结果:iops ≥ 500k,延迟 p99 ≤ 2ms
# 若不达标,检查 BIOS 设置和内核参数
DiskANN 的查询性能高度依赖磁盘随机读性能,此步骤不可跳过。
五、3节点 Milvus 集群部署
5.1 集群角色分配
| 节点 | IP地址 | 组件 | 说明 |
|---|---|---|---|
| node1 | 10.0.0.101 | Milvus(Proxy/QN/IN/DN) + MinIO + etcd | 主协调节点(etcd leader) |
| node2 | 10.0.0.102 | Milvus(Proxy/QN/IN/DN) + MinIO + etcd | 工作节点 |
| node3 | 10.0.0.103 | Milvus(Proxy/QN/IN/DN) + MinIO + etcd | 工作节点 |
| - | 10.0.0.100 | 负载均衡 VIP | 外部访问入口 |
5.2 集群部署目录结构
/opt/milvus-cluster/ ├── docker-compose.yml # 主 compose 文件 ├── .env # 环境变量配置 ├── configs/ │ ├── milvus.yaml # Milvus 配置文件(核心调优参数) │ └── etcd.yaml # etcd 配置 └── data/ # 挂载到各节点的数据目录
5.3 环境变量文件
# /opt/milvus-cluster/.env
# 集群配置
CLUSTER_SIZE=3
NODE_ID=1 # 各节点分别设为1/2/3
PUBLIC_IP=10.0.0.101 # 各节点自身IP
# 节点地址列表(etcd 集群发现用)
ETCD_NODE1=10.0.0.101
ETCD_NODE2=10.0.0.102
ETCD_NODE3=10.0.0.103
# MinIO 集群配置(各节点独立数据目录)
MINIO_ACCESS_KEY=minioadmin
MINIO_SECRET_KEY=minioadmin
# Milvus 版本
MILVUS_VERSION=v2.6.0
5.4 Docker Compose 配置文件
# /opt/milvus-cluster/docker-compose.yml
# 注意:各节点的 NODE_ID 需根据实际 IP 调整
version: '3.8'
networks:
milvus-net:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
services:
# ========== etcd 集群 ==========
etcd:
image: quay.io/coreos/etcd:v3.5.5
container_name: milvus-etcd-${NODE_ID}
command:
- --name=etcd-${NODE_ID}
- --data-dir=/etcd-data
- --initial-advertise-peer-urls=http://${PUBLIC_IP}:2380
- --listen-peer-urls=http://0.0.0.0:2380
- --advertise-client-urls=http://${PUBLIC_IP}:2379
- --listen-client-urls=http://0.0.0.0:2379
- --initial-cluster=etcd-1=http://${ETCD_NODE1}:2380,etcd-2=http://${ETCD_NODE2}:2380,etcd-3=http://${ETCD_NODE3}:2380
- --initial-cluster-state=new
- --initial-cluster-token=milvus-etcd-token
volumes:
- /var/lib/etcd:/etcd-data
ports:
- "2379:2379"
- "2380:2380"
networks:
milvus-net:
ipv4_address: 172.20.0.1${NODE_ID}
restart: always
# ========== MinIO 存储节点(融合部署) ==========
minio:
image: minio/minio:latest
container_name: milvus-minio-${NODE_ID}
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: ${MINIO_ACCESS_KEY}
MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY}
volumes:
- /var/lib/milvus/minio_data:/data
ports:
- "9000:9000"
- "9001:9001"
networks:
milvus-net:
ipv4_address: 172.20.0.2${NODE_ID}
restart: always
# ========== Milvus Coordinator(root coord) ==========
rootcoord:
image: milvusdb/milvus:${MILVUS_VERSION}
container_name: milvus-rootcoord-${NODE_ID}
command: ["milvus", "run", "rootcoord"]
environment:
- TZ=Asia/Shanghai
volumes:
- ./configs/milvus.yaml:/milvus/configs/milvus.yaml
- /var/lib/milvus/rootcoord_data:/var/lib/milvus
ports:
- "53100:53100"
networks:
milvus-net:
ipv4_address: 172.20.0.3${NODE_ID}
depends_on:
- etcd
- minio
restart: always
# ========== Milvus Proxy ==========
proxy:
image: milvusdb/milvus:$