Настройка хранилища#
В Platform V Vector DB (далее - Vector DB) все данные внутри одной коллекции разделяются на сегменты. Каждый сегмент имеет независимые хранилища векторов и полезной нагрузки, а также индексы.
Данные, хранящиеся в сегментах, обычно не пересекаются. Однако хранение одной и той же точки в разных сегментах не вызовет проблем, так как поиск содержит механизм удаления дубликатов.
Сегменты состоят из хранилищ векторов и полезной нагрузки, индексов векторов и полезной нагрузки, а также сопоставителя идентификаторов, который хранит связь между внутренними и внешними идентификаторами.
Сегмент может быть appendable или non-appendable, в зависимости от типа используемого хранилища и индекса. Можно свободно добавлять, удалять и запрашивать данные в сегменте appendable. С помощью сегмента non-appendable можно только читать и удалять данные.
Конфигурация сегментов в коллекции может различаться и быть независимой друг от друга, но хотя бы один сегмент типа «добавляемый» должен присутствовать в коллекции.
Предусловия#
Пререквизиты:
Доступ к серверу: SSH или физический доступ.
Установленный продукт: сервер должен быть запущен (кроме операций с файлами данных).
Исходный код: скрипты находятся в
/src/bin/репозитория продукта.Rust: установленный компилятор для сборки утилит.
Привилегии:
Файловая система: права на чтение/запись в
storage_path.API-ключ: обязателен ключ с правами
adminдля большинства операций.Сетевой доступ: доступность API-порта (порт по умолчанию 6333).
Ключевые требования:
Vector DB остановлен.
Наличие прав на изменение конфигурационных файлов.
Хранилище векторов#
В зависимости от требований приложения Vector DB может использовать один из вариантов хранения данных. Выбор необходимо сделать между скоростью поиска и объемом используемой оперативной памяти.
Оперативное хранилище — сохраняет все векторы в оперативной памяти, обладает самой высокой скоростью, поскольку доступ к диску требуется только для обеспечения постоянства.
Хранилище Memmap - создает виртуальное адресное пространство, связанное с файлом на диске. Файлы mmapped напрямую не загружаются в оперативную память. Вместо этого они используют кеш страниц для доступа к содержимому файла. Эта схема позволяет гибко использовать имеющуюся память. При достаточном объеме оперативной памяти она почти такая же быстрая, как оперативная память.
Хранилище полезной нагрузки#
Vector DB поддерживает два типа хранилищ полезной нагрузки: InMemory и OnDisk.
Хранилище полезной нагрузки InMemory организовано аналогично векторной памяти. Данные полезной нагрузки загружаются в оперативную память при запуске службы, тогда как диск и Gridstore используются исключительно для постоянного хранения. Этот тип хранилища работает довольно быстро, однако он может потребовать много места для хранения всех данных в оперативной памяти, особенно если полезная нагрузка связана с большими значениями — абстрактными текстовыми описаниями или даже изображениями.
Если значения полезной нагрузки велики, лучше использовать хранилище полезной нагрузки OnDisk. Этот тип хранилища будет считывать и записывать полезную нагрузку непосредственно в БД, поэтому ему не потребуется значительный объем оперативной памяти для хранения. Недостатком является задержка доступа. Если необходимо запросить векторы по условиям, основанным на полезной нагрузке — проверка значений, хранящихся на диске, может занять слишком много времени. В таком сценарии рекомендуется создать индекс полезной нагрузки для каждого поля, используемого в условиях фильтрации, чтобы избежать обращения к диску. Как только будет создан индекс полей, Vector DB сохранит все значения указанного поля в оперативной памяти независимо от типа хранилища полезной нагрузки.
Желаемый тип хранилища полезной нагрузки можно указать либо в конфигурационном файле Config.yaml, либо с помощью параметра коллекции on_disk_payload во время создания коллекции.
Версионирование#
Для обеспечения целостности данных Vector DB выполняет все изменения данных в два этапа. На первом этапе данные записываются в журнал упреждающей записи (WAL), который упорядочивает все операции и присваивает им последовательный номер.
Как только изменение было добавлено в WAL, оно не потеряется даже в случае потери питания. Затем изменения поступают в сегменты. Каждый сегмент хранит последнюю версию изменений, примененных к нему, а также версию каждой отдельной точки. Если новое изменение имеет порядковый номер меньше текущей версии точки, обновитель проигнорирует изменение. Этот механизм позволяет Vector DB безопасно и эффективно восстанавливать хранилище из WAL в случае ненормального завершения работы.
Последовательность выполнения#
Настройка хранилища Memmap#
Существует два способа настроить использование хранилища memmap (также известного как on-disk):
Установите параметр
on_diskдля векторов при создании API-коллекции:
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),
}),
})
Это создаст коллекцию со всеми векторами, сразу сохраненными в хранилище memmap. Это рекомендуемый способ, если экземпляр Vector DB работает с быстрыми дисками и большими коллекциями.
Установите опцию
memmap_threshold. Этот параметр задает пороговое значение, после которого сегмент будет преобразован в хранилище memmap.
Настройка порога Memmap#
Существует два способа настройки порога:
Установить глобальный порог в конфигурационном файле
Config.yaml. Параметр называетсяmemmap_threshold(memmap_threshold_kb).Отдельно задать порог для каждой коллекции во время ее создания или обновления.
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). В этом случае оптимизатор не выполнит никаких дополнительных запусков и оптимизирует все пороги одновременно.высокая нагрузка записи и мало оперативной памяти — установите порог memmap ниже, чем
indexing_threshold, например, 10000. В этом случае оптимизатор сначала преобразует сегменты в хранилище memmap, а затем применяет индексирование.
Кроме того, можно использовать хранилище memmap не только для векторов, но и для индекса HNSW. Для этого нужно установить параметр hnsw_config.on_disk равным true во время создания или обновления коллекции.
PUT /collections/{collection_name}
{
"vectors": {
"size": 768,
"distance": "Cosine",
"on_disk": true
},
"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, on_disk=True),
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",
on_disk: true,
},
hnsw_config: {
on_disk: true,
},
});
use qdrant_client::qdrant::{
CreateCollectionBuilder, Distance, HnswConfigDiffBuilder,
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))
.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.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)
.setOnDisk(true)
.build())
.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, OnDisk = true },
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,
OnDisk: qdrant.PtrOf(true),
}),
HnswConfig: &qdrant.HnswConfigDiff{
OnDisk: qdrant.PtrOf(true),
},
})
Результат#
Параметр |
Результат |
|---|---|
Использование памяти |
Снижение потребления RAM за счет хранения данных на диске |
Производительность |
Задержки при чтении/записи увеличиваются (по сравнению с in-memory) |
Масштабируемость |
Возможность работы с большими коллекциями без ограничений по памяти |
Гибкость |
Динамическое управление порогами Memmap для разных коллекций. |
Надежность |
Данные сохраняются на диске, что снижает риски потери информации при сбоях |