Qdrant 1.19 深度拆解:当向量数据库把内存成本「榨」到极限——从 HNSW 图、TurboQuant 量化到内存分层与分布式 Raft 的全链路实战
2026 年,RAG、AI Agent、多模态检索已经从「概念」变成「基础设施」。但凡你做过一个真正上线的语义搜索或 Agent 记忆系统,都会被同一个问题卡住:向量一多,内存先爆,精度再掉,过滤还慢。这篇我们就把当下最值得关注的向量数据库 Qdrant 1.19(2026 年 8 月发布,主打 TurboQuant 量化与 Memory Tiers 内存分层)从头到脚拆一遍——不止讲怎么用,更讲它为什么这样设计,以及那些决定你生产环境是「丝滑」还是「半夜被报警叫醒」的工程细节。
一、背景:为什么 2026 年还需要一个「向量数据库」
先泼一盆冷水:向量检索本身没有多高深,numpy.dot 一行就能算余弦相似度。真正难的,从来不是「算」,而是在十亿级向量、带元数据过滤、低延迟、可水平扩展、机器会宕机这些约束同时成立时,还能稳定地把「最像的那几个」捞出来。
一个典型的演进路径是这样的:
- 原型期:用
sentence-transformers把文本变成 384/768/1536 维向量,塞进一个 Python list,线性扫描np.dot。几百条数据,爽。 - 成长期:数据涨到百万级,线性扫描 O(N) 扛不住了,上 FAISS,内存里建个 IVF 或 HNSW 索引。快了,但——索引是进程内的,服务一重启就没了;多进程共享要自己搞;想按「时间范围 + 类目」过滤,FAISS 基本帮不上忙。
- 生产期:你需要持久化、多租户、过滤、备份、扩缩容、权限、可观测性。这时候你会发现,你不是在「用向量检索」,而是在「养一个数据库」。PostgreSQL 的
pgvector能顶一阵,但它在事务引擎里塞向量索引,写入吞吐和过滤组合查询的优化空间有限;专门干这事的向量数据库,就该出场了。
Qdrant 是其中用 Rust 写、主打高性能 + 生产级过滤 + 量化省内存的一员。它的核心主张很朴素:把「向量索引」「元数据过滤」「量化压缩」「分布式一致性」做成一套内聚的存储引擎,而不是一堆拼起来的脚本。1.19 这一版,把「省钱」(TurboQuant)和「弹性」(Memory Tiers)推到了新高度——这也是为什么它值得一篇深度拆解。
说明:本文不点名具体个人,相关设计与发布均归于 Qdrant 核心团队;所有 API 用法以
qdrant-client稳定版为准,TurboQuant 作为 1.19 新增量化类型,按其公开设计意图讲解。
二、核心概念:向量检索到底在算什么
2.1 嵌入与相似性度量
向量检索的本质,是把「对象」(文本、图片、用户行为)映射成一个高维空间里的点,然后找「离查询点最近的点」。「近」在数学上有好几种定义,选错度量,结果会漂移:
| 度量 | 公式直觉 | 适用地形 |
|---|---|---|
| Cosine(余弦) | 看夹角,不在乎长度 | 归一化后的文本稠密向量(最常见) |
| Dot(点积) | 长度 × 夹角 | 未归一化、长度本身有意义的向量(如某些检索模型) |
| Euclidean(欧氏) | 直线距离 | 聚类、几何特征 |
| Manhattan(曼哈顿) | 各维度差的绝对值之和 | 稀疏、分块特征 |
Qdrant 在建集合时必须声明 distance,因为它直接决定了 HNSW 图里的「邻居」是怎么算出来的——索引和度量是绑定的。一个常见坑:用 text-embedding-3-small 这类已经归一化的向量,配 Cosine 最稳;如果向量没归一化却用 Cosine,先 vec / norm 再存。
2.2 为什么需要 ANN:精确搜索的代价
精确最近邻(Exact NN)要么线性扫描(慢),要么靠树/哈希做空间划分(高维下退化成扫描,所谓「维度灾难」)。当维度上百、数据上亿,ANN(Approximate Nearest Neighbor,近似最近邻) 是工程上唯一现实的选择:允许极小的概率误差,换来几个数量级的提速。
主流 ANN 索引三类:
- IVF(倒排文件):K-means 把空间切成若干桶,查询时只进最近的几个桶。内存小,但要先训练、精度略低。
- PQ / SQ(量化):不直接比原始向量,比「压缩版」,省内存、省算力。
- HNSW(分层可导航小世界图):Qdrant 的默认王牌,下面专讲。
2.3 HNSW 算法原理:一张会「跳跃」的图
HNSW(Hierarchical Navigable Small World)是 2016 年提出的算法,核心就一句话:用「多层跳表 + 小世界图」组织向量,让搜索像坐电梯一样,先从高层稀疏处粗定位,再逐层下到最密的那层精修。
把它拆成几个直觉:
1) 小世界(Small World)
在图里,任意两点之间只需很少的「跳数」就能到达(类似社交网络「六度分隔」)。这意味着从任一节点出发,几步就能漫游到目标附近。
2) 分层(Hierarchical / 跳表)
如果所有边都密密麻麻,贪心搜索会迷路。HNSW 把图分成 L 层:
- 最顶层节点最少、边最长(「高速公路」),负责大跨度移动;
- 最底层(layer 0)节点最多、最密,负责精细定位。
- 每个点被分配到第
0到l层,l由概率l = floor(-ln(uniform(0,1)) * ml)决定——层数越高越稀有,自然地形成「金字塔」。
3) 查询(贪心 + 最佳优先)
从顶层 entry point 出发
对每一层 layer 从顶到底:
在当前层做贪心: 不断跳到「比当前更靠近 query 的邻居」,
直到局部最优(没有更近的邻居)才下降到下一层
在 layer 0 用 ef_search 个候选做精细搜索, 返回 top-k
4) 关键参数
M:每个节点在每层最多连几条边(layer 0 通常2M)。越大召回越高、内存越贵。ef_construct:建索引时每层保留多少候选邻居,越大图质量越高、建索引越慢。ef_search:查询时在 layer 0 维护的动态候选集大小,直接决定查询质量 vs 延迟。这是线上最该调的旋钮。
复杂度约 O(log N) 跳数,是它快的根本原因。代价是:图是内存结构,且插入是「边来边连」的,所以原生 HNSW 不支持「先过滤再搜」——这就引出了下面的大坑。
2.4 量化:用精度换内存(SQ / PQ / BQ / TurboQuant)
向量占内存是实打实的:1 亿 × 768 维 × 4 字节(float32) ≈ 307 GB。量化就是把每个维度从 float32 压成更小的表示:
- Scalar Quantization(SQ,标量量化):整体或分块地,把
[min, max]映射到int8(或 int4)。实现简单、几乎不掉精度,是「性价比首选」。Qdrant 用分位数(quantile)定范围,避免个别离群值把整个范围撑爆。 - Product Quantization(PQ,乘积量化):把向量切成
m段(subvector),每段用 k-means 训一个码本(codebook),只存「段索引」。比如 768 维切成 64 段、每段 256 个簇 → 每维只需 1 字节。压缩比极高(可达 1/32),但有损更明显,且需要训练码本。 - Binary Quantization(BQ,二值量化):只保留符号位(正/负),每维 1 bit。内存直接砍到 1/32,但精度损失大,适合「召回粗排 + 原始向量重排」的两段式。
- TurboQuant(1.19 新增):Qdrant 1.19 的头号特性。它在「内存」和「精度」之间又撕开一道缝——保留每维 1 bit 的紧凑主体,再叠加一小撮每块的缩放/误差修正信息,目标是拿到接近 SQ 的召回,却只花接近 BQ 的内存。它是 1.19 把「内存成本榨到极限」这句话的技术支点。
量化的工程现实:量化向量常驻 RAM 用于快速初筛,原始 float 向量可以放到磁盘/仅按需读取用于最终重排。这正是 1.19 Memory Tiers 的用武之地。
2.5 过滤的难题:预过滤、后过滤与「原地过滤」
这是向量数据库最容易被低估的坑。设想你要「在所有 category=news 且 date > 2026-01-01 的点里找最像 query 的 10 个」:
- 预过滤(pre-filter):先按元数据筛出候选集,再在候选集里做 ANN。问题:候选集可能极小甚至为空,HNSW 图根本没为「这个子集」建过索引,要么搜不动,要么退化成扫描。
- 后过滤(post-filter):先无视过滤做 ANN 取 top-N,再丢掉不符合的。问题:可能一个都不剩,于是得反复「扩大 N 重搜」,延迟飘忽不定。
Qdrant 的做法是第三种——原地过滤(in-graph / in-place filtering):在 HNSW 遍历图的过程中,每一步只把「满足过滤条件」的邻居纳入候选。配合其自研的 ACORN 这类「过滤感知」遍历策略,既不会预过滤的「候选枯竭」,也不会后过滤的「结果漂没」。这要求过滤字段本身有索引(payload index),否则就是边遍历边全量判断,照样慢。结论:凡是高频过滤字段,务必建 payload index。
三、架构分析:Qdrant 1.19 的内脏
3.1 总体分层
从外到内大致是四层:
REST / gRPC API
│
Collection 抽象(一个集合 = 一组同构向量 + payload)
│
Segment(写时复制的存储单元:WAL + 向量索引 + payload 索引)
│
HNSW 索引 / 量化器 / 存储后端(RAM / mmap / 磁盘)
请求进来先到 API 层做鉴权、校验、转化为内部命令;落到某个 Collection;再路由到具体的 Segment。Segment 是 Qdrant 并发与一致性的基本单位——写操作先写 WAL(预写日志),再应用到内存中的 Segment,后台异步做索引合并。这保证了崩溃后可重放 WAL 恢复,也给出了「至少一次」的持久性语义。
3.2 段(Segment)与写路径
一次 upsert 的生命周期:
- 请求进入,生成操作记录,追加进该 Collection 的 WAL。
- 写入对应的 Segment(新数据可能进一个可写 Segment,老的做只读 Segment)。
- 向量进入 HNSW 索引(连边),payload 进入 payload 存储并触发相关 payload index 更新。
- 若启用量化,向量同时被量化器压缩,量化副本常驻(或按 Memory Tiers 策略放置)。
Segment 这种「多段 + 后台合并」的设计,让你能在不影响读的情况下做压缩与去重(类似 LSM-Tree 的思路)。代价是要有 compaction 策略,否则段越积越多。生产上要关注 segments 相关指标。
3.3 HNSW 索引实现要点
Qdrant 用 Rust 实现 HNSW,几个工程要点值得记:
- 并行建图:插入新节点时,对多层、多候选邻居的连接计算用
rayon并行,吃满多核。 - 链接裁剪(link pruning):邻居不是「来者不拒」,会按一定启发式(如基于距离的方向性)裁剪冗余边,避免图退化为「星型」或「毛线团」,保持搜索质量。
- 图压缩(1.13 起):HNSW 的边用 Delta 编码(只存差值)压缩,进一步省内存——和 TurboQuant 一样,是「把每一字节都算计到」的思路延续。
3.4 量化管线
量化不是「存完就完」。Qdrant 的量化器在查询时参与打分:先用量化向量做高速初筛(cheap distance),对 top 候选再用原始向量精确重排(rerank)。这样既省了内存和带宽,又不牺牲最终返回质量。量化配置里有个关键开关 always_ram:即便原始向量放到磁盘,量化向量也常驻 RAM,保证初筛不触发磁盘 IO。
3.5 内存分层(Memory Tiers):1.19 的核心
这是 1.19 最实用的能力,一句话概括:让数据的每一部分(原始向量 / 量化向量 / 索引 / payload)各自选择住在 RAM、mmap 文件、还是纯磁盘,按「访问频率 × 延迟敏感度」做最优 placement。
- 全 RAM:延迟最低,但最贵,十亿向量轻松上百 GB。
- mmap(内存映射文件):向量躺在 OS 页缓存里,热数据自动留 RAM、冷数据自动换出,无需手动管理,性价比极高。
- 纯磁盘(on_disk):原始 float 向量放磁盘,只用于最终重排;量化向量留 RAM 扛初筛。内存占用可砍掉一大半,换来一点点重排延迟。
1.19 把这套分层做成了更细、更易组合的「档位」,让你能在「成本」和「P99 延迟」之间拧出精确的平衡点——对云上按内存计费的场景,这就是真金白银。
3.6 TurboQuant:1.19 的新量化类型
回到 2.4 的伏笔。TurboQuant 的公开设计意图是:以接近二值量化的内存占用,拿到接近标量量化的召回率。机制上,它保留每维 1 bit 的紧凑主体(符号/方向),再叠加少量每块(block)级的缩放与误差修正信息,使得初筛阶段的相似度估计比纯 BQ 准得多,从而减少「漏掉真正近邻」的概率。
它接入方式与既有量化类型一致(都属于 QuantizationConfig 联合类型),对你的代码是「换一个类型名」的迁移成本。是否启用它,取决于你的瓶颈:如果内存吃紧、且 BQ 召回不够、又不想上 PQ 的训练/调参成本,TurboQuant 是 1.19 给的答案。
3.7 分布式:Raft 与分片
单机能扛的终有上限。Qdrant 的分布式把**分片(sharding)和复制(replication)**分开:
- 一个 Collection 切成多个 shard,分散在多节点,提升容量与写入并行度。
- 每个 shard 可有多个 replica,提升读吞吐与可用性。
- 元数据变更走 Raft 共识,保证强一致;而向量索引的更新是异步、批量合并的——把「需要强一致的元数据」和「可以最终一致的高频索引变更」解耦,是 Qdrant 分布式设计里很聪明的一刀。它不依赖外部 ZooKeeper/etcd,自身用 Rust 的 raft 实现闭环。
查询时,协调节点把请求扇出到相关 shard 的多个 replica(取负载最低的),各自本地搜完做归并(merge),再返回全局 top-k。
四、代码实战
下面所有代码基于 qdrant-client 稳定版,可直接跑。先装依赖:
pip install qdrant-client sentence-transformers
# 本地起一个单机 Qdrant(也可以 docker run -p 6333:6333 qdrant/qdrant)
4.1 建集合:HNSW + 量化 + 内存分层一步到位
from qdrant_client import QdrantClient
from qdrant_client.models import (
Distance, VectorParams, HnswConfigDiff,
ScalarQuantization, ScalarQuantizationConfig, ScalarType,
QuantizationConfig,
)
client = QdrantClient(host="localhost", port=6333)
client.create_collection(
collection_name="articles",
vectors_config=VectorParams(
size=384, # 与你的 embedding 维度一致
distance=Distance.COSINE,
hnsw_config=HnswConfigDiff(
m=16, # 每层连接数:召回 vs 内存的旋钮
ef_construct=100, # 建索引质量:越大越准越慢
full_scan_threshold=10000, # 低于此规模直接暴力扫描更快
),
# 1.19 Memory Tiers:原始向量放磁盘,量化向量留 RAM
on_disk=True,
),
# 量化:int8 标量量化,分位数定范围,量化向量常驻 RAM
quantization_config=ScalarQuantization(
scalar=ScalarQuantizationConfig(
type=ScalarType.INT8,
quantile=0.99,
always_ram=True,
)
),
)
print("collection created")
要点:on_disk=True 把原始向量下沉(Memory Tiers 的「磁盘档」),always_ram=True 保证量化向量不被换出(保证初筛不碰磁盘)。两者组合正是 1.19 省钱又不掉速的精髓。
4.2 批量插入 + payload
from qdrant_client.models import PointStruct
import uuid
docs = [
("Rust 异步运行时 Tokio 的调度模型", "rust", "2026-07-01"),
("用 HNSW 做十亿级向量检索的工程细节", "vector", "2026-08-10"),
("PostgreSQL 18 的 io_uring 异步 I/O", "database", "2026-08-12"),
("Agent 记忆层为什么需要向量数据库", "ai", "2026-08-15"),
]
# 假设已有 embedding 函数
# embeddings = model.encode([text for text,_,_ in docs])
embeddings = [[0.0] * 384 for _ in docs] # 占位,实际替换为真实向量
points = [
PointStruct(
id=str(uuid.uuid4()),
vector=vec,
payload={"title": t, "category": c, "date": d},
)
for (t, c, d), vec in zip(docs, embeddings)
]
# 批量写入,分批控制内存与网络抖动
client.upsert(collection_name="articles", points=points, wait=True)
4.3 带过滤的检索(务必配 payload index)
from qdrant_client.models import Filter, FieldCondition, MatchValue, Range
# 高频过滤字段先建索引,否则原地过滤会退化
client.create_payload_index(
collection_name="articles",
field_name="category",
field_schema="keyword",
)
client.create_payload_index(
collection_name="articles",
field_name="date",
field_schema="datetime",
)
q = embeddings[3] # 查询向量(占位)
resp = client.search(
collection_name="articles",
query_vector=q,
query_filter=Filter(
must=[
FieldCondition(key="category", match=MatchValue(value="vector")),
FieldCondition(key="date", range=Range(gte="2026-08-01")),
]
),
limit=5,
with_payload=True,
)
for r in resp:
print(round(r.score, 4), r.payload["title"])
这就是 2.5 说的「原地过滤」:条件在图遍历时就被应用,不会预过滤枯竭、也不会后过滤漂没。没建 category/date 的 payload index,这条查询就会慢——这是生产事故最常见的根因。
4.4 混合检索(稠密 + 稀疏,RRF 融合)
纯稠密向量对「关键词强匹配」不敏感,纯稀疏(BM25 类)又不懂语义。Qdrant 原生支持两者并存 + 融合:
from qdrant_client.models import (
SparseVectorParams, NamedVector, NamedSparseVector,
FusionQuery, Fusion,
)
client.create_collection(
"hybrid",
vectors_config={"dense": VectorParams(size=384, distance=Distance.COSINE)},
sparse_vectors_config={"sparse": SparseVectorParams()},
)
client.upsert("hybrid", points=[{
"id": 1,
"vector": {
"dense": [0.0] * 384, # 占位,真实稠密向量
"sparse": {"indices": [10, 25, 99], "values": [0.5, 0.9, 0.3]},
},
"payload": {"title": "Qdrant 混合检索实战"},
}])
# 用 RRF(Reciprocal Rank Fusion)融合两种检索结果
res = client.query_points(
"hybrid",
query=[
FusionQuery("dense", query_vector=[0.0] * 384), # 占位稠密
FusionQuery("sparse", query_sparse={"indices": [10, 99], "x": 0}),
],
fusion=Fusion.RRF,
limit=5,
)
混合检索正在成为 RAG / Agent 的默认范式——Qdrant 把「稠密语义 + 稀疏关键词」做成一等公民,配合 payload 过滤和late-interaction重排,能在「召回全」和「答得准」之间取得平衡。
4.5 手写一个迷你 HNSW(教学向)
光看不动手,HNSW 永远是黑盒。下面约 60 行实现一个教学版 HNSW(省略了大量生产级启发式,仅用于理解「分层 + 贪心」):
import math, heapq, random
import numpy as np
class MiniHNSW:
def __init__(self, dim, M=8, ef=32, ml=4, seed=0):
random.seed(seed)
self.dim, self.M, self.ef, self.ml = dim, M, ef, ml
self.vecs, self.layer, self.graph = {}, {}, {}
self.entry, self.top = None, -1
def _dist(self, a, b):
return 1 - np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b) + 1e-9)
def _level(self):
return int(-math.log(random.random() + 1e-12) * self.ml)
def _search_layer(self, q, eps, ef, lc):
seen = set(eps)
cand = [(self._dist(q, self.vecs[e]), e) for e in eps] # min-heap, 最近优先
heapq.heapify(cand)
res = [(-d, e) for d, e in cand] # max-heap, 最远优先
heapq.heapify(res)
while cand:
d, c = heapq.heappop(cand)
if -res[0][0] < d: # 最差结果都比候选近 => 停止
break
for nb in self.graph[c].get(lc, []):
if nb not in seen:
seen.add(nb)
nd = self._dist(q, self.vecs[nb])
if -res[0][0] > nd or len(res) < ef:
heapq.heappush(cand, (nd, nb))
heapq.heappush(res, (-nd, nb))
if len(res) > ef:
heapq.heappop(res)
return [e for _, e in sorted(res, reverse=True)] # 近 -> 远
def insert(self, pid, vec):
self.vecs[pid] = np.asarray(vec, float)
lv = self._level()
self.layer[pid] = lv
self.graph[pid] = {l: [] for l in range(lv + 1)}
if self.entry is None:
self.entry, self.top = pid, lv
return
ep = [self.entry]
for lc in range(self.top, lv, -1):
ep = self._search_layer(self.vecs[pid], ep, 1, lc)
for lc in range(min(self.top, lv), -1, -1):
neigh = self._search_layer(self.vecs[pid], ep, self.ef, lc)
for nb in neigh[:self.M]:
self.graph[pid][lc].append(nb)
if len(self.graph[nb][lc]) < self.M * 2:
self.graph[nb][lc].append(pid)
ep = neigh
if lv > self.top:
self.top, self.entry = lv, pid
def search(self, q, k=5, ef=None):
ef = ef or self.ef
ep = [self.entry]
for lc in range(self.top, 0, -1):
ep = self._search_layer(q, ep, 1, lc)
return self._search_layer(q, ep, ef, 0)[:k]
跑一下验证它「能找到近邻」:
np.random.seed(1)
mh = MiniHNSW(dim=16, M=8, ef=32)
data = [np.random.randn(16) for _ in range(500)]
for i, v in enumerate(data):
mh.insert(i, v)
q = data[0]
# 与它最像的应该是它自己(id=0)
print("top-5:", mh.search(q, k=5)) # 期望 0 在列
你会发现:它确实能工作,但召回率、插入顺序敏感度、内存都远不如生产实现——这正是 HNSW 工程化(链接裁剪、并行、量化、压缩)存在的理由。
4.6 量化数学可视化
把 2.4 的标量量化落到代码,看清「怎么压、怎么近似打分」:
import numpy as np
def scalar_quantize(vec, q=0.99, dtype=np.int8):
a = np.asarray(vec, float)
bound = np.quantile(np.abs(a), q) # 用分位数定对称范围, 抗离群值
scale = np.iinfo(dtype).max / max(bound, 1e-8)
qv = np.clip(np.round(a * scale),
np.iinfo(dtype).min, np.iinfo(dtype).max).astype(dtype)
return qv, scale
def approx_dot(q_int, d_int, scale_q, scale_d):
# 对称量化下: dot(q_int, d_int) * scale_q * scale_d ≈ dot(query, db)
return np.dot(q_int.astype(float), d_int.astype(float)) * (scale_q * scale_d)
rng = np.random.default_rng(0)
v_db = rng.standard_normal(768)
v_q = rng.standard_normal(768)
q_db, s_db = scalar_quantize(v_db)
q_q, s_q = scalar_quantize(v_q)
exact = float(np.dot(v_q, v_db))
approx = approx_dot(q_q, q_db, s_q, s_db)
print(f"exact={exact:.3f} approx={approx:.3f} "
f"误差={abs(exact-approx)/abs(exact):.2%}")
print(f"压缩比: float32(4B) -> int8(1B) = 4x, 内存 {768*4}B -> {768}B")
运行后你会看到:int8 对称量化在「标准正态」向量上误差通常很小,但一旦向量分布有偏或离群点多,误差会跳涨——这就是为什么 Qdrant 用分位数而非全局 max 定范围,也是为什么极端场景要上 TurboQuant / PQ 而非无脑 SQ。
五、性能优化清单
5.1 参数调优(按瓶颈对号入座)
| 现象 | 调什么 | 方向 |
|---|---|---|
| 召回太低 | ef_search | 调大 |
| 查询延迟高 | ef_search / M | 调小(权衡召回) |
| 建索引太慢 | ef_construct | 调小 |
| 内存爆 | M / 量化 / on_disk | 降连接数、上量化、下沉磁盘 |
| 过滤慢 | payload index | 给过滤字段建索引 |
5.2 量化选型矩阵
- 内存充裕、要几乎无损 → 不上量化(原始 float)。
- 想省内存又怕掉精度(默认首选) → ScalarQuantization (int8)。
- 内存极度紧张、可接受粗排+重排 → BinaryQuantization,配合原始向量重排。
- 超高精度压缩、愿训练调参 → ProductQuantization。
- 1.19 新选项:内存紧 + BQ 召回不够 + 不想 PQ 调参 → TurboQuant。
5.3 Memory Tiers 决策
- 全 RAM:P99 延迟要求极致、预算充足。
- mmap:大多数云上场景的甜点,热数据自动留内存。
- 原始向量
on_disk+ 量化always_ram:十亿级且按内存计费时的最佳组合(1.19 强推)。
5.4 过滤与分片
- 所有高频
must/should过滤字段都建 payload index。 - 写入吞吐不够 → 增加 shard 数,分散写压力。
- 读吞吐/可用性 → 增加 replica。
- 分布式下关注 Raft 落后(lag)指标,避免读到一个过旧的 replica。
5.5 生产 checklist
-
distance与 embedding 是否匹配(归一化了吗?) - 量化类型是否对照 5.2 选型
-
on_disk+always_ram是否按 Memory Tiers 配好 - 过滤字段是否都有 payload index
-
ef_search是否按「召回达标前提下尽量小」压过一轮 - 批量 upsert 是否分批(如每批 100~500 点)
- 监控:搜索延迟 P99、内存、segment 数、Raft lag
- 备份:WAL + snapshot 策略是否就绪
六、总结与展望
把 Qdrant 1.19 拆完,你会得到一个很清晰的判断:向量数据库的竞争,已经从「谁支持 ANN」卷到了「谁把内存和精度算计得更狠」。TurboQuant 是在量化这条线上继续逼近 Pareto 前沿;Memory Tiers 是把「数据该住哪儿」的权利交还给你,直接转化为账单上的节省;而原地过滤(ACORN 类策略)、HNSW 图压缩、Rust + rayon 的并行建图,则是「又快又稳」的底层保障。
往后看,几个趋势基本确定:
- 量化成为默认:不做量化的向量数据库在十亿级场景里没有性价比,TurboQuant 这类「新型混合量化」会越来越多。
- 磁盘/分层 ANN 普及:随着 DiskANN 思路与 mmap 成熟,「全 RAM」不再是必选项,成本曲线会大幅下移。
- 混合检索(稠密+稀疏+重排)成为 RAG/Agent 标配:向量库不再只是「存向量」,而是「检索编排层」。
- Agent 记忆层:多模态 embedding、带时间戳/来源/置信度的 payload、语义缓存——向量数据库会越来越像「AI 的 hippocampus(海马体)」。
选型上给一句实在话:如果你要的是「上生产、要过滤、要省内存、要能扩」,Qdrant 1.19 是当下很值得认真评估的一个;如果你只是原型验证、数据量小、已经在用 Postgres,先 pgvector 也完全 OK,别为「先进」提前买单。技术的选择,永远是对「当下约束」的诚实回应——而不是对「最新」的盲目追逐。
本文代码均以
qdrant-client稳定 API 为准,版本演进请以官方文档为准;TurboQuant / Memory Tiers 为 Qdrant 1.19 引入能力,落地前建议在你的数据分布上做一组召回率与内存的实测对比。
参考方向(建议延伸阅读)
- Qdrant 官方文档与 1.19 发布说明(TurboQuant & Memory Tiers)
- HNSW 原始论文 Efficient and robust approximate nearest neighbor search using Hierarchical Navigable Small World graphs (2016)
- Qdrant 博客:Pre-Filtering vs Post-Filtering(in-graph filtering / ACORN)
- Product / Binary / Scalar Quantization 各自的精度-内存权衡分析
- Raft 共识与向量索引异步合并的解耦设计