编程 Qdrant 1.19 深度拆解:当向量数据库把内存成本「榨」到极限——从 HNSW 图、TurboQuant 量化到内存分层与分布式 Raft 的全链路实战

2026-08-19 02:14:48 +0800 CST views 11

Qdrant 1.19 深度拆解:当向量数据库把内存成本「榨」到极限——从 HNSW 图、TurboQuant 量化到内存分层与分布式 Raft 的全链路实战

2026 年,RAG、AI Agent、多模态检索已经从「概念」变成「基础设施」。但凡你做过一个真正上线的语义搜索或 Agent 记忆系统,都会被同一个问题卡住:向量一多,内存先爆,精度再掉,过滤还慢。这篇我们就把当下最值得关注的向量数据库 Qdrant 1.19(2026 年 8 月发布,主打 TurboQuant 量化Memory Tiers 内存分层)从头到脚拆一遍——不止讲怎么用,更讲它为什么这样设计,以及那些决定你生产环境是「丝滑」还是「半夜被报警叫醒」的工程细节。


一、背景:为什么 2026 年还需要一个「向量数据库」

先泼一盆冷水:向量检索本身没有多高深,numpy.dot 一行就能算余弦相似度。真正难的,从来不是「算」,而是在十亿级向量、带元数据过滤、低延迟、可水平扩展、机器会宕机这些约束同时成立时,还能稳定地把「最像的那几个」捞出来。

一个典型的演进路径是这样的:

  1. 原型期:用 sentence-transformers 把文本变成 384/768/1536 维向量,塞进一个 Python list,线性扫描 np.dot。几百条数据,爽。
  2. 成长期:数据涨到百万级,线性扫描 O(N) 扛不住了,上 FAISS,内存里建个 IVF 或 HNSW 索引。快了,但——索引是进程内的,服务一重启就没了;多进程共享要自己搞;想按「时间范围 + 类目」过滤,FAISS 基本帮不上忙。
  3. 生产期:你需要持久化、多租户、过滤、备份、扩缩容、权限、可观测性。这时候你会发现,你不是在「用向量检索」,而是在「养一个数据库」。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)节点最多、最密,负责精细定位。
  • 每个点被分配到第 0l 层,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=newsdate > 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 的生命周期:

  1. 请求进入,生成操作记录,追加进该 Collection 的 WAL
  2. 写入对应的 Segment(新数据可能进一个可写 Segment,老的做只读 Segment)。
  3. 向量进入 HNSW 索引(连边),payload 进入 payload 存储并触发相关 payload index 更新。
  4. 若启用量化,向量同时被量化器压缩,量化副本常驻(或按 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 的并行建图,则是「又快又稳」的底层保障。

往后看,几个趋势基本确定:

  1. 量化成为默认:不做量化的向量数据库在十亿级场景里没有性价比,TurboQuant 这类「新型混合量化」会越来越多。
  2. 磁盘/分层 ANN 普及:随着 DiskANN 思路与 mmap 成熟,「全 RAM」不再是必选项,成本曲线会大幅下移。
  3. 混合检索(稠密+稀疏+重排)成为 RAG/Agent 标配:向量库不再只是「存向量」,而是「检索编排层」。
  4. 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 共识与向量索引异步合并的解耦设计

推荐文章

windows下mysql使用source导入数据
2024-11-17 05:03:50 +0800 CST
动态渐变背景
2024-11-19 01:49:50 +0800 CST
程序员茄子在线接单