存储

一个集合中的所有数据被分成多个段。 每个段都有其独立的向量和有效载荷存储以及索引。

存储在分段中的数据通常不会重叠。 然而,在不同的分段中存储相同的点不会引起问题,因为搜索包含去重机制。

段由向量和有效载荷存储、向量和有效载荷索引以及id映射器组成,id映射器存储内部id和外部id之间的关系。

一个段可以是appendablenon-appendable,这取决于使用的存储和索引类型。 你可以在appendable段中自由地添加、删除和查询数据。 而对于non-appendable段,只能读取和删除数据。

集合中的段配置可以不同且相互独立,但集合中必须至少存在一个“可追加”段。

向量存储

根据应用程序的需求,Qdrant 可以使用其中一种数据存储选项。 选择必须在搜索速度和使用的 RAM 大小之间进行。

内存存储 - 将所有向量存储在RAM中,由于仅在持久化时需要磁盘访问,因此具有最高的速度。

内存映射存储 - 创建一个与磁盘上的文件相关联的虚拟地址空间。维基百科。 内存映射文件不会直接加载到RAM中。相反,它们使用页面缓存来访问文件内容。 这种方案允许灵活使用可用内存。在拥有足够RAM的情况下,它的速度几乎与内存存储一样快。

配置内存映射存储

有两种方法可以配置memmap(也称为磁盘上)存储的使用:

  • 为集合创建API中的向量设置on_disk选项:

自 v1.2.0 版本起可用

PUT /collections/{collection_name}
{
    "vectors": {
      "size": 768,
      "distance": "Cosine",
      "on_disk": true
    }
}
from qdrant_client import QdrantClient, models

client = QdrantClient(url="http://localhost:6333")

client.create_collection(
    collection_name="{collection_name}",
    vectors_config=models.VectorParams(
        size=768, distance=models.Distance.COSINE, on_disk=True
    ),
)
import { QdrantClient } from "@qdrant/js-client-rest";

const client = new QdrantClient({ host: "localhost", port: 6333 });

client.createCollection("{collection_name}", {
  vectors: {
    size: 768,
    distance: "Cosine",
    on_disk: true,
  },
});
use qdrant_client::qdrant::{CreateCollectionBuilder, Distance, VectorParamsBuilder};
use qdrant_client::Qdrant;

let client = Qdrant::from_url("http://localhost:6334").build()?;

client
    .create_collection(
        CreateCollectionBuilder::new("{collection_name}")
            .vectors_config(VectorParamsBuilder::new(768, Distance::Cosine).on_disk(true)),
    )
    .await?;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Collections.Distance;
import io.qdrant.client.grpc.Collections.VectorParams;

QdrantClient client =
    new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());

client
    .createCollectionAsync(
        "{collection_name}",
        VectorParams.newBuilder()
            .setSize(768)
            .setDistance(Distance.Cosine)
            .setOnDisk(true)
            .build())
    .get();
using Qdrant.Client;
using Qdrant.Client.Grpc;

var client = new QdrantClient("localhost", 6334);

await client.CreateCollectionAsync(
	"{collection_name}",
	new VectorParams
	{
		Size = 768,
		Distance = Distance.Cosine,
		OnDisk = true
	}
);
import (
	"context"

	"github.com/qdrant/go-client/qdrant"
)

client, err := qdrant.NewClient(&qdrant.Config{
	Host: "localhost",
	Port: 6334,
})

client.CreateCollection(context.Background(), &qdrant.CreateCollection{
	CollectionName: "{collection_name}",
	VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
		Size:     768,
		Distance: qdrant.Distance_Cosine,
		OnDisk:   qdrant.PtrOf(true),
	}),
})

这将创建一个集合,所有向量立即存储在内存映射存储中。 这是推荐的方式,适用于您的Qdrant实例使用快速磁盘并且您正在处理大型集合的情况。

  • 设置memmap_threshold选项。此选项将设置阈值,超过该阈值后,段将被转换为memmap存储。

有两种方法可以做到这一点:

  1. You can set the threshold globally in the 配置文件. The parameter is called memmap_threshold (previously memmap_threshold_kb).
  2. You can set the threshold for each collection separately during 创作 or 更新.
PUT /collections/{collection_name}
{
    "vectors": {
      "size": 768,
      "distance": "Cosine"
    },
    "optimizers_config": {
        "memmap_threshold": 20000
    }
}
from qdrant_client import QdrantClient, models

client = QdrantClient(url="http://localhost:6333")

client.create_collection(
    collection_name="{collection_name}",
    vectors_config=models.VectorParams(size=768, distance=models.Distance.COSINE),
    optimizers_config=models.OptimizersConfigDiff(memmap_threshold=20000),
)
import { QdrantClient } from "@qdrant/js-client-rest";

const client = new QdrantClient({ host: "localhost", port: 6333 });

client.createCollection("{collection_name}", {
  vectors: {
    size: 768,
    distance: "Cosine",
  },
  optimizers_config: {
    memmap_threshold: 20000,
  },
});
use qdrant_client::qdrant::{
    CreateCollectionBuilder, Distance, OptimizersConfigDiffBuilder, VectorParamsBuilder,
};
use qdrant_client::Qdrant;

let client = Qdrant::from_url("http://localhost:6334").build()?;

client
    .create_collection(
        CreateCollectionBuilder::new("{collection_name}")
            .vectors_config(VectorParamsBuilder::new(768, Distance::Cosine))
            .optimizers_config(OptimizersConfigDiffBuilder::default().memmap_threshold(20000)),
    )
    .await?;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Collections.CreateCollection;
import io.qdrant.client.grpc.Collections.Distance;
import io.qdrant.client.grpc.Collections.OptimizersConfigDiff;
import io.qdrant.client.grpc.Collections.VectorParams;
import io.qdrant.client.grpc.Collections.VectorsConfig;

QdrantClient client =
    new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());

client
    .createCollectionAsync(
        CreateCollection.newBuilder()
            .setCollectionName("{collection_name}")
            .setVectorsConfig(
                VectorsConfig.newBuilder()
                    .setParams(
                        VectorParams.newBuilder()
                            .setSize(768)
                            .setDistance(Distance.Cosine)
                            .build())
                    .build())
            .setOptimizersConfig(
                OptimizersConfigDiff.newBuilder().setMemmapThreshold(20000).build())
            .build())
    .get();
using Qdrant.Client;
using Qdrant.Client.Grpc;

var client = new QdrantClient("localhost", 6334);

await client.CreateCollectionAsync(
	collectionName: "{collection_name}",
	vectorsConfig: new VectorParams { Size = 768, Distance = Distance.Cosine },
	optimizersConfig: new OptimizersConfigDiff { MemmapThreshold = 20000 }
);
import (
	"context"

	"github.com/qdrant/go-client/qdrant"
)

client, err := qdrant.NewClient(&qdrant.Config{
	Host: "localhost",
	Port: 6334,
})

client.CreateCollection(context.Background(), &qdrant.CreateCollection{
	CollectionName: "{collection_name}",
	VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
		Size:     768,
		Distance: qdrant.Distance_Cosine,
	}),
	OptimizersConfig: &qdrant.OptimizersConfigDiff{
		MaxSegmentSize: qdrant.PtrOf(uint64(20000)),
	},
})

设置memmap阈值参数的简单经验法则是:

  • 如果你有一个平衡的使用场景 - 将 memmap 阈值设置为与 indexing_threshold 相同(默认值为 20000)。在这种情况下,优化器不会进行任何额外的运行,并且会一次性优化所有阈值。
  • 如果你有高写入负载和低RAM - 将memmap阈值设置为低于indexing_threshold,例如10000。在这种情况下,优化器将首先将段转换为memmap存储,然后才会应用索引。

此外,您不仅可以将memmap存储用于向量,还可以用于HNSW索引。 要启用此功能,您需要在集合创建更新期间将hnsw_config.on_disk参数设置为true

PUT /collections/{collection_name}
{
    "vectors": {
      "size": 768,
      "distance": "Cosine"
    },
    "optimizers_config": {
        "memmap_threshold": 20000
    },
    "hnsw_config": {
        "on_disk": true
    }
}
from qdrant_client import QdrantClient, models

client = QdrantClient(url="http://localhost:6333")

client.create_collection(
    collection_name="{collection_name}",
    vectors_config=models.VectorParams(size=768, distance=models.Distance.COSINE),
    optimizers_config=models.OptimizersConfigDiff(memmap_threshold=20000),
    hnsw_config=models.HnswConfigDiff(on_disk=True),
)
import { QdrantClient } from "@qdrant/js-client-rest";

const client = new QdrantClient({ host: "localhost", port: 6333 });

client.createCollection("{collection_name}", {
  vectors: {
    size: 768,
    distance: "Cosine",
  },
  optimizers_config: {
    memmap_threshold: 20000,
  },
  hnsw_config: {
    on_disk: true,
  },
});
use qdrant_client::qdrant::{
    CreateCollectionBuilder, Distance, HnswConfigDiffBuilder, OptimizersConfigDiffBuilder,
    VectorParamsBuilder,
};
use qdrant_client::Qdrant;

let client = Qdrant::from_url("http://localhost:6334").build()?;

client
    .create_collection(
        CreateCollectionBuilder::new("{collection_name}")
            .vectors_config(VectorParamsBuilder::new(768, Distance::Cosine))
            .optimizers_config(OptimizersConfigDiffBuilder::default().memmap_threshold(20000))
            .hnsw_config(HnswConfigDiffBuilder::default().on_disk(true)),
    )
    .await?;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Collections.CreateCollection;
import io.qdrant.client.grpc.Collections.Distance;
import io.qdrant.client.grpc.Collections.HnswConfigDiff;
import io.qdrant.client.grpc.Collections.OptimizersConfigDiff;
import io.qdrant.client.grpc.Collections.VectorParams;
import io.qdrant.client.grpc.Collections.VectorsConfig;

QdrantClient client =
    new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());

client
    .createCollectionAsync(
        CreateCollection.newBuilder()
            .setCollectionName("{collection_name}")
            .setVectorsConfig(
                VectorsConfig.newBuilder()
                    .setParams(
                        VectorParams.newBuilder()
                            .setSize(768)
                            .setDistance(Distance.Cosine)
                            .build())
                    .build())
            .setOptimizersConfig(
                OptimizersConfigDiff.newBuilder().setMemmapThreshold(20000).build())
            .setHnswConfig(HnswConfigDiff.newBuilder().setOnDisk(true).build())
            .build())
    .get();
using Qdrant.Client;
using Qdrant.Client.Grpc;

var client = new QdrantClient("localhost", 6334);

await client.CreateCollectionAsync(
	collectionName: "{collection_name}",
	vectorsConfig: new VectorParams { Size = 768, Distance = Distance.Cosine },
	optimizersConfig: new OptimizersConfigDiff { MemmapThreshold = 20000 },
	hnswConfig: new HnswConfigDiff { OnDisk = true }
);
import (
	"context"

	"github.com/qdrant/go-client/qdrant"
)

client, err := qdrant.NewClient(&qdrant.Config{
	Host: "localhost",
	Port: 6334,
})

client.CreateCollection(context.Background(), &qdrant.CreateCollection{
	CollectionName: "{collection_name}",
	VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
		Size:     768,
		Distance: qdrant.Distance_Cosine,
	}),
	OptimizersConfig: &qdrant.OptimizersConfigDiff{
		MaxSegmentSize: qdrant.PtrOf(uint64(20000)),
	},
	HnswConfig: &qdrant.HnswConfigDiff{
		OnDisk: qdrant.PtrOf(true),
	},
})

有效载荷存储

Qdrant 支持两种类型的有效载荷存储:内存存储和磁盘存储。

内存中的有效载荷存储的组织方式与内存中的向量相同。 有效载荷数据在服务启动时加载到RAM中,而磁盘和RocksDB仅用于持久化。 这种类型的存储工作速度非常快,但可能需要大量空间来将所有数据保存在RAM中,特别是如果有效载荷附带有大值——如文本摘要甚至图像。

在负载值较大的情况下,使用OnDisk负载存储可能更为合适。 这种类型的存储将直接读取和写入负载到RocksDB,因此不需要大量的RAM来存储。 然而,缺点是访问延迟。 如果您需要基于某些负载条件查询向量 - 检查存储在磁盘上的值可能会花费太多时间。 在这种情况下,我们建议为过滤条件中使用的每个字段创建负载索引,以避免磁盘访问。 一旦创建了字段索引,Qdrant将保留索引字段的所有值在RAM中,无论负载存储类型如何。

您可以通过配置文件或在集合创建期间使用集合参数on_disk_payload来指定所需的有效负载存储类型。

版本控制

为了确保数据完整性,Qdrant 分两个阶段执行所有数据更改。 在第一步中,数据被写入预写日志(WAL),该日志对所有操作进行排序并为它们分配一个序列号。

一旦更改被添加到WAL(Write-Ahead Logging,预写式日志),即使发生断电,这些更改也不会丢失。 然后,这些更改会进入段(segments)。 每个段存储应用于它的更改的最新版本以及每个单独点的版本。 如果新更改的序列号小于该点的当前版本,更新程序将忽略该更改。 这种机制使得Qdrant在异常关闭的情况下能够安全高效地从WAL恢复存储。

这个页面有用吗?

感谢您的反馈!🙏

我们很抱歉听到这个消息。😔 你可以在GitHub上编辑这个页面,或者创建一个GitHub问题。