Qdrant 入门系列06 – 集合管理:创建、配置与管理向量

Qdrant 集合承载向量、载荷及用于加速条件检索的索引。集合结构设计正确,后续应用会更容易维护;设计不当,则可能同时增加运行内存与迁移成本。对于已有 PostgreSQL 的系统,也可结合 pgvector 的单表方案比较取舍。

本文通过可运行的 Python 示例和对应 Web UI 配置,介绍常用集合参数,包括稠密与稀疏向量共存、磁盘存储、多租户以及通过原子别名切换替换集合。

原文于 2026 年 5 月使用 Ubuntu 24.04.4 LTS、Qdrant 1.18.1、qdrant-client 1.18.0 测试,11 类集合模式均在实际 Docker 实例中创建并检查,截图来自运行中的服务器。

集合的组成

集合包含数据点。每个点由唯一 ID、一个或多个向量、可选 JSON 载荷组成。集合还配置 HNSW 图、优化阈值、WAL、可选量化和载荷索引。

配套代码位于 c4geeks/qdrant 的 collections 目录。先按本系列对应操作系统教程启动实例,再连接自己的集群。

安装 Python 客户端:

python3 -m venv venv
source venv/bin/activate
pip install "qdrant-client[fastembed]"

后续脚本使用相同的连接代码:

from qdrant_client import QdrantClient, models
client = QdrantClient(url="http://localhost:6333")

启用认证时再传入 api_key="..."。使用 TLS 时改用 HTTPS 地址;如希望优先采用 6334 端口的 gRPC,可设置 prefer_grpc=True。

向量参数:维度与距离度量

稠密向量必须配置 size 与 distance,前者必须匹配嵌入模型维度,后者决定相似度计算方式。最简单的集合每个点只有一个向量:

client.create_collection(
    collection_name="basic_docs",
    vectors_config=models.VectorParams(
        size=384,
        distance=models.Distance.COSINE,
    ),
)

常见模型维度包括:all-MiniLM-L6-v2 为 384,BGE-base 为 768,OpenAI text-embedding-3-small 默认 1536,text-embedding-3-large 默认 3072。若模型支持输出维度配置,应以实际输出为准。集合与写入向量的维度不一致时,upsert 会失败。

度量 Python 枚举 常见用途
余弦 Distance.COSINE Sentence Transformers、MiniLM、BGE、MPNet 等文本嵌入
点积 Distance.DOT 模型明确使用点积,或向量长度具有意义的场景
欧氏距离 Distance.EUCLID 部分图像特征、地理向量及绝对尺度有意义的特征
曼哈顿距离 Distance.MANHATTAN 适合 L1 距离的高维特征及相关编码方案

不了解模型要求时,可先评估余弦,但应优先遵循模型卡给定的度量。余弦不是省略必填参数的理由;后续切换度量通常需要建立新的向量空间。

下例为每种度量创建独立集合,便于测试:

for dist in [models.Distance.COSINE, models.Distance.DOT,
             models.Distance.EUCLID, models.Distance.MANHATTAN]:
    name = f"dist_{dist.value.lower()}"
    client.create_collection(
        collection_name=name,
        vectors_config=models.VectorParams(size=128, distance=dist),
    )

这些集合各自维护索引,一次普通集合查询不会自动跨集合搜索。

命名向量:一个集合中的多个向量空间

生产数据可能同时需要标题与描述的文本嵌入、商品图片嵌入及关键词稀疏向量。命名向量将它们附加在同一个点上,通过共同 ID 对齐,但分别维护索引。

client.create_collection(
    collection_name="named_vectors",
    vectors_config={
        "text":  models.VectorParams(size=384, distance=models.Distance.COSINE),
        "image": models.VectorParams(size=512, distance=models.Distance.COSINE),
    },
)

Web UI 的 Info 以嵌套映射显示各向量空间及其独立的 size、distance:

文本与图像命名向量的集合配置

upsert 时按名称提交各向量:

client.upsert(
    collection_name="named_vectors",
    points=[
        models.PointStruct(
            id=1,
            vector={
                "text":  [0.1] * 384,
                "image": [0.2] * 512,
            },
            payload={"sku": "ABC-123"},
        ),
    ],
)

查询时通过 using 指定向量空间:

client.query_points(
    collection_name="named_vectors",
    query=[0.1] * 384,
    using="text",
    limit=10,
)

这样,每次查询可选择所需向量空间;不再需要的命名向量可通过受支持的 schema 更新移除,无须重建其他空间。

用稀疏向量进行关键词式检索

稀疏向量仅存储实际出现的维度及权重,即 (index, value) 对。理论维度可以很大,例如词表约五万维,但每条记录只保存少量非零值。Qdrant 使用倒排索引处理它们,并允许与稠密向量共存。

client.create_collection(
    collection_name="sparse_demo",
    vectors_config={
        "dense": models.VectorParams(
            size=384, distance=models.Distance.COSINE,
        ),
    },
    sparse_vectors_config={
        "sparse_idx": models.SparseVectorParams(
            index=models.SparseIndexParams(on_disk=False),
        ),
    },
)

Info 会分别显示两个向量空间:

稠密与稀疏向量共存的配置

稀疏向量写入结构与稠密向量不同:

client.upsert(
    collection_name="sparse_demo",
    points=[
        models.PointStruct(
            id=1,
            vector={
                "dense": [0.1] * 384,
                "sparse_idx": models.SparseVector(
                    indices=[42, 1024, 5000],
                    values=[0.7, 0.3, 0.9],
                ),
            },
            payload={"title": "Rust vector database"},
        ),
    ],
)

常见生成器包括 SPLADE、FastEmbed 的 Qdrant/bm25,以及自定义 TF-IDF 流程。混合检索可通过 RRF(倒数排名融合)等方法合并稠密、稀疏结果,过滤篇会展示完整模式。

载荷结构与七类索引

载荷索引让查询规划器利用字段类型对应的索引结构,缩小需要检查的点范围,并配合向量搜索。未建索引的字段过滤可能需要扫描较多数据;建立索引通常可明显改善性能,但具体延迟取决于数据规模与选择性,不能将原文的亚毫秒描述视为保证。

为需要过滤的字段创建索引:

client.create_collection(
    collection_name="payload_indexed",
    vectors_config=models.VectorParams(size=64, distance=models.Distance.COSINE),
)

schemas = {
    "category":     models.PayloadSchemaType.KEYWORD,
    "view_count":   models.PayloadSchemaType.INTEGER,
    "rating":       models.PayloadSchemaType.FLOAT,
    "is_published": models.PayloadSchemaType.BOOL,
    "location":     models.PayloadSchemaType.GEO,
    "published_at": models.PayloadSchemaType.DATETIME,
}
for field, kind in schemas.items():
    client.create_payload_index(
        "payload_indexed", field_name=field, field_schema=kind,
    )

# Text index needs explicit tokenizer params
client.create_payload_index(
    "payload_indexed", field_name="body",
    field_schema=models.TextIndexParams(
        type="text",
        tokenizer=models.TokenizerType.WORD,
        min_token_len=2, max_token_len=20, lowercase=True,
    ),
)

Info 会按声明类型显示索引,便于核对 SDK 写入的配置:

载荷索引与集合配置 JSON

类型 用途 过滤方式
KEYWORD 标签、SKU、国家代码等分类字符串 精确 match、any、except
INTEGER 计数、外部系统 ID range、精确匹配
FLOAT 分数、价格、小数权重 range
BOOL 发布、归档等布尔开关 精确匹配
GEO 经纬度,先经度后纬度 geo_radius、geo_bounding_box、geo_polygon
TEXT 载荷字符串的全文检索 采用指定分词方式的 match_text
DATETIME ISO 8601 时间戳 时间范围 range

建立索引后,可用一千个点进行快速验证:

upserted 1000 points

filtered query (category=doc AND is_published=true) returned 3 in 4.6 ms
geo_radius (5000 km of (0,0)) returned 3 in 2.4 ms
full-text match (body~='sparse') returned 5 in 2.5 ms

小数据集可能掩盖扫描成本;到一千万点时,不合理的过滤与索引设计会明显影响查询。

将向量与载荷放到磁盘

默认内存策略提供较低搜索延迟,但也受物理内存限制。十亿条 1536 维 float32 向量,仅原始向量就约需 6.144 TB,另加 HNSW 和载荷开销。原文写为约 6 GB,这是数量级错误,此处已更正。

使用三个选项将对应层移到磁盘:

client.create_collection(
    collection_name="on_disk_collection",
    vectors_config=models.VectorParams(
        size=1536,
        distance=models.Distance.COSINE,
        on_disk=True,                       # vectors on disk (memory-mapped)
    ),
    on_disk_payload=True,                   # payload on disk
    hnsw_config=models.HnswConfigDiff(
        on_disk=True,                       # HNSW graph on disk
    ),
)

Info 可同时显示这些设置:

向量、载荷和 HNSW 的磁盘存储配置

向量参数中的 on_disk=True 将原始向量置于内存映射文件,由操作系统页缓存按需读取;on_disk_payload=True 将载荷放到磁盘,适合 HTML、图像元数据、转录文本等较大字段;hnsw_config.on_disk=True 将 HNSW 图也放到磁盘。

这种方式以一定访问延迟换取容量。原文在 163,000 点、384 维 Prefix Cache 示例上观察到磁盘路径约慢 30%。是否能在 64 GB 主机容纳一亿点,还取决于维度、量化、索引、磁盘容量及工作集,不能仅由 on_disk 推导。

通过租户载荷索引支持多租户

为一千个小租户创建一千个集合,会重复承担 HNSW、段和优化器开销。可考虑共享集合,并建立具备租户语义的载荷索引。

client.create_collection(
    collection_name="tenants",
    vectors_config=models.VectorParams(
        size=384, distance=models.Distance.COSINE,
    ),
)

client.create_payload_index(
    "tenants", field_name="tenant_id",
    field_schema=models.KeywordIndexParams(
        type="keyword",
        is_tenant=True,        # the key flag
    ),
)

is_tenant=True 帮助按字段值组织数据,查询同时固定 tenant_id 时,可改善租户内访问局部性。这是存储优化,不能代替授权隔离。

每次搜索都应包含租户条件:

client.query_points(
    collection_name="tenants",
    query=[0.1] * 384,
    query_filter=models.Filter(
        must=[
            models.FieldCondition(
                key="tenant_id",
                match=models.MatchValue(value="acme-corp"),
            ),
        ],
    ),
    limit=10,
)

若没有过滤,查询仍可能覆盖所有租户。可以利用 JWT claims 固定租户约束,避免完全依赖应用逐次添加条件。

优化器配置:段、索引与内存映射

后台优化器负责合并段和建立 HNSW。大量写入、大集合或低内存主机可能需要调整:

client.create_collection(
    collection_name="tuned",
    vectors_config=models.VectorParams(size=384, distance=models.Distance.COSINE),
    optimizers_config=models.OptimizersConfigDiff(
        default_segment_number=4,
        indexing_threshold=20000,
        memmap_threshold=200000,
        max_optimization_threads=2,
    ),
)

在 Info 中确认配置:

段、索引与 mmap 优化器配置

  • default_segment_number:优化器的目标段数量。写入负载可结合 CPU 并行度评估,低内存环境则需控制数量。
  • indexing_threshold:超过阈值后建立向量索引。Qdrant 1.18.1 按向量数据的 KB 计算,不是点数;示例的 20000 是显式配置,并非该版本默认值。官方配置给出的默认值为 10000 KB。
  • memmap_threshold:旧式内存映射阈值,同样按 KB。该参数从 1.15 起已弃用,应优先使用各层的 on_disk 设置;这里保留原文示例以便对应旧配置。
  • max_optimization_threads:每个分片允许的并行优化作业数量。1.18.1 中 null 表示动态选择,0 表示关闭优化,原文将 0 解释为自动选择,此处更正。

配置不合适通常表现为写入变慢、长时间黄色状态或搜索与优化争用资源。应先建立基准,再调整并验证。

WAL 容量与预分配

WAL 是持久性机制,upsert 和删除先记录日志,再由段处理与落盘。两个参数控制其行为:

client.create_collection(
    collection_name="wal_tuned",
    vectors_config=models.VectorParams(size=384, distance=models.Distance.COSINE),
    wal_config=models.WalConfigDiff(
        wal_capacity_mb=64,
        wal_segments_ahead=2,
    ),
)

wal_capacity_mb 是单个 WAL 段大小,大载荷突发写入可评估增大,小主机可减小;wal_segments_ahead 指定提前创建的空日志段数。示例使用 2,持续写入时也可测试 4 或 8,以减少分配抖动。Qdrant 1.18.1 官方配置的默认值为 0,原文将 2 写作默认值,此处更正。

日志位于 {storage_dir}/collections/{name}/wal。必要时可规划独立存储以隔离日志写入与搜索读取 IOPS,但应结合性能测试验证。

原地更新集合

许多设置无需重建集合,可通过 update_collection 的差异配置更新:

client.update_collection(
    collection_name="basic_docs",
    hnsw_config=models.HnswConfigDiff(m=32, ef_construct=256),
    optimizers_config=models.OptimizersConfigDiff(indexing_threshold=10000),
)

现有点保留,后台逐步按新参数重建相关索引。示例在 basic_docs 上执行后的输出为:

after update: hnsw.m=32  ef_construct=256
              indexing_threshold=10000

已有向量的维度、距离度量或稠密/稀疏类型改变,通常需要新向量空间或新集合并重新写入。Qdrant 自 1.18.0 起支持在已有集合中添加、删除命名向量定义,因此不能把“命名向量集合不可修改”当成该版本的限制。

用别名原子切换集合

在旧集合旁构建新集合并写入数据,然后原子调整别名。应用始终使用同一别名,避免切换时出现未指向任何集合的中间状态。

# Two real collections, one with 384-dim vectors, one with 768-dim
client.create_collection("docs_v1", models.VectorParams(size=384, distance=models.Distance.COSINE))
client.create_collection("docs_v2", models.VectorParams(size=768, distance=models.Distance.COSINE))

# Application code reads from "docs_live"
client.update_collection_aliases([
    models.CreateAliasOperation(create_alias=models.CreateAlias(
        collection_name="docs_v1",
        alias_name="docs_live",
    )),
])

应用通过 docs_live 访问 docs_v1。使用新模型将数据写入 docs_v2 后,通过单次原子操作切换:

client.update_collection_aliases([
    models.DeleteAliasOperation(delete_alias=models.DeleteAlias(
        alias_name="docs_live")),
    models.CreateAliasOperation(create_alias=models.CreateAlias(
        collection_name="docs_v2",
        alias_name="docs_live",
    )),
])

两个操作在同一调用中执行。通过 get_aliases 验证:

after swap   -> all aliases:
  alias=docs_live  -> collection=docs_v2

确认新集合稳定服务后再删除 docs_v1。此模式适合模型升级、维度变化及不能原地完成的迁移。

集中核对配置

运行全部示例后,Web UI 中会出现对应的测试集合:

示例创建的十四个测试集合

点击集合并打开 Info,可以检查 Python SDK 设置的各项参数。将脚本输出与面板并排比较,便于核对:

Python SDK 创建集合的终端输出

绿色状态表示集合健康、可查询;是否满足预期持久性还应结合存储、WAL、写入确认与备份策略判断。

容易忽略的问题

维度与距离度量不能随意修改。 已建的 384 维余弦空间不能直接变成 768 维欧氏空间。可预先规划新空间或新集合及别名切换。

地理坐标先经度后纬度。 与一些地图 API 的习惯相反;写反会把点放到错误位置,使地理过滤返回空结果。

文本字段不会自动获得全文索引。 需要显式创建 TextIndexParams,并选择 WORD、WHITESPACE、PREFIX、MULTILINGUAL 等分词方式。分词器是索引配置,修改时需重建相关索引;无索引时的匹配行为与性能应按实际版本核对。

别名不是独立集合,但可以用于集合请求。 get_collections 列出真实集合,get_aliases 或 get_collection_aliases 查看映射。官方说明集合查询可使用别名,因此原文“不能用 get_collection(alias_name)”的绝对说法不准确,此处更正。

优化阈值按段计算,且不是简单点数。 集合总点数很大,不代表每个段都达到建索引阈值。应结合单段向量数据量和 Info 中的 indexed_vectors_count 判断。

按症状选择参数

症状 优先考虑
过滤搜索慢 为过滤字段创建类型匹配的载荷索引
内存不足、目标数据量很大 向量 on_disk=True、on_disk_payload=True、HNSW on_disk=True,再核算磁盘与工作集
上千租户的 SaaS 共享集合,配合 tenant_id 的 KeywordIndexParams(is_tenant=True) 及访问控制
同时需要语义与关键词召回 同一集合中的命名稠密向量与稀疏向量
写入停滞、长期黄色 检查优化器积压、阈值和并行度,基于测量调整
升级嵌入模型 并行构建 v2,校验后原子切换别名
不同用途采用不同度量 使用独立向量空间;单个命名向量空间的度量保持一致

这些模式覆盖集合创建、配置、字段索引、磁盘存储、租户组织、优化及模型迁移。本系列 API 篇将进一步展示对应的 REST 与 gRPC 调用。

Avatar photo

关于 木子

Email: muzi@vip.rockylinux.cn 微信:VebinLee QQ: 2306867585
Founder of the Rocky Linux Chinese community, MVP、VMware vExpert、TVP, advocate for cloud native technologies, with over ten years of experience in site reliability engineering (SRE) and the DevOps field. Passionate about Cloud Computing、Microservices、CI&CD、DevOps、Kubernetes, currently dedicated to promoting and implementing Rocky Linux in Chinese-speaking regions.
用一杯咖啡支持我们,我们的每一篇[文档]都经过实际操作和精心打磨,而不是简单地从网上复制粘贴。期间投入了大量心血,只为能够真正帮助到您。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇