Memory Tiers
Qdrant persists all collection data to disk. For faster search, you can also load individual structures into RAM, but keeping everything in memory isn’t always cost-effective. The per-structure memory parameter controls how each structure is cached in RAM: pinned permanently, warmed into a disk cache at startup, or left on disk until first accessed.
This page covers how to configure memory tiers, the different placement tiers available, and how to optimize for disk-based retrieval.
Configuring Memory Tiers
Each collection in Qdrant is backed by several independent structures:
- Dense vectors hold the original floating-point vectors for a collection or named vector.
- The HNSW vector index is a graph structure built over dense vectors that makes approximate nearest-neighbor search fast.
- Quantized vectors are compressed copies of the original vectors, used to speed up search and shrink memory use.
- The sparse vector index is an exact, inverted-index-style structure built over sparse vectors.
- Payloads are the JSON documents attached to each point.
- Payload indexes are per-field indexes that speed up filtering.
Each of these structures accepts a memory parameter that controls how it is cached in RAM: pinned permanently, warmed into a disk cache at startup, or left on disk until first accessed. The available tiers are:
pinned: Qdrant loads the data onto the heap and never evicts it. Requests stay fast, but the structure must fit in RAM at all times. Because it allocates data on the heap, it’s only available for structures that support a heap-backed in-RAM representation.cached: Qdrant pre-loads the data into the disk cache when it starts up, so the first request is fast. Under memory pressure, the operating system can evict this data if it decides another component’s data is used more often.cold: Qdrant doesn’t pre-load the data into RAM. Startup is faster and uses less memory, but the first access to any page requires a disk read until the OS caches it.
cold and cached both back the data with a memory-mapped file; the only difference is whether Qdrant proactively warms the operating system’s page cache on load. The OS evicts both tiers using the same criteria, so cached data gets no priority over cold data under memory pressure. Under heavy I/O load, pages evicted from either tier require disk reads to reload, which adds latency.
Limitations
- Qdrant rejects
pinnedfor dense vectors and payloads, since both only support a memory-mapped in-RAM representation (cachedorcold). - For sparse vectors, only the sparse vector index has a
memoryparameter. Qdrant doesn’t offer a RAM cache for sparse vectors, since their values aren’t read during the index search step itself, only when a point is fetched.
Default Tiers
If you don’t explicitly set memory on a structure, Qdrant defaults to the following tiers:
| Data structure | Default tier |
|---|---|
| Dense vectors | cached |
| HNSW vector index | cached |
| Quantized vectors | Depends on the placement of the original dense vectors: pinned if original vectors are cached, cold if original vectors are cold. |
| Sparse vector index | pinned |
| Payloads | cold |
| Payload indexes | pinned |
Low Memory Mode can degrade these defaults at startup under memory constraints, without changing the persisted collection configuration.
Example
This example configures a collection so the vectors are cached in RAM, the HNSW vector index is cold, the quantized vectors are pinned, and the payload is cached:
PUT /collections/{collection_name}
{
"vectors": {
"size": 768,
"distance": "Cosine",
"memory": "cached"
},
"hnsw_config": {
"memory": "cold"
},
"quantization_config": {
"scalar": {
"type": "int8",
"memory": "pinned"
}
},
"payload": {
"memory": "cached"
}
}
from qdrant_client import QdrantClient, models
client.create_collection(
collection_name="{collection_name}",
vectors_config=models.VectorParams(
size=768,
distance=models.Distance.COSINE,
memory=models.Memory.CACHED,
),
hnsw_config=models.HnswConfigDiff(memory=models.Memory.COLD),
quantization_config=models.ScalarQuantization(
scalar=models.ScalarQuantizationConfig(
type=models.ScalarType.INT8,
memory=models.Memory.PINNED,
),
),
payload=models.PayloadStorageParams(memory=models.Memory.CACHED),
)
import { QdrantClient } from "@qdrant/js-client-rest";
client.createCollection("{collection_name}", {
vectors: {
size: 768,
distance: "Cosine",
memory: "cached",
},
hnsw_config: {
memory: "cold",
},
quantization_config: {
scalar: {
type: "int8",
memory: "pinned",
},
},
payload: {
memory: "cached",
},
});
use qdrant_client::qdrant::{
CreateCollectionBuilder, Distance, HnswConfigDiffBuilder, Memory, PayloadStorageParamsBuilder,
QuantizationType, ScalarQuantizationBuilder, VectorParamsBuilder,
};
use qdrant_client::Qdrant;
client
.create_collection(
CreateCollectionBuilder::new("{collection_name}")
.vectors_config(
VectorParamsBuilder::new(768, Distance::Cosine).memory(Memory::Cached),
)
.hnsw_config(HnswConfigDiffBuilder::default().memory(Memory::Cold))
.quantization_config(
ScalarQuantizationBuilder::default()
.r#type(QuantizationType::Int8.into())
.memory(Memory::Pinned),
)
.payload(PayloadStorageParamsBuilder::default().memory(Memory::Cached)),
)
.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.Memory;
import io.qdrant.client.grpc.Collections.PayloadStorageParams;
import io.qdrant.client.grpc.Collections.QuantizationConfig;
import io.qdrant.client.grpc.Collections.QuantizationType;
import io.qdrant.client.grpc.Collections.ScalarQuantization;
import io.qdrant.client.grpc.Collections.VectorParams;
import io.qdrant.client.grpc.Collections.VectorsConfig;
client
.createCollectionAsync(
CreateCollection.newBuilder()
.setCollectionName("{collection_name}")
.setVectorsConfig(
VectorsConfig.newBuilder()
.setParams(
VectorParams.newBuilder()
.setSize(768)
.setDistance(Distance.Cosine)
.setMemory(Memory.Cached)
.build())
.build())
.setHnswConfig(HnswConfigDiff.newBuilder().setMemory(Memory.Cold).build())
.setQuantizationConfig(
QuantizationConfig.newBuilder()
.setScalar(
ScalarQuantization.newBuilder()
.setType(QuantizationType.Int8)
.setMemory(Memory.Pinned)
.build())
.build())
.setPayload(PayloadStorageParams.newBuilder().setMemory(Memory.Cached).build())
.build())
.get();
using Qdrant.Client;
using Qdrant.Client.Grpc;
await client.CreateCollectionAsync(
collectionName: "{collection_name}",
vectorsConfig: new VectorParams { Size = 768, Distance = Distance.Cosine, Memory = Memory.Cached },
hnswConfig: new HnswConfigDiff { Memory = Memory.Cold },
quantizationConfig: new QuantizationConfig
{
Scalar = new ScalarQuantization { Type = QuantizationType.Int8, Memory = Memory.Pinned }
},
onDiskPayload: false
);
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 768,
Distance: qdrant.Distance_Cosine,
Memory: qdrant.Memory_Cached.Enum(),
}),
HnswConfig: &qdrant.HnswConfigDiff{
Memory: qdrant.Memory_Cold.Enum(),
},
QuantizationConfig: qdrant.NewQuantizationScalar(
&qdrant.ScalarQuantization{
Type: qdrant.QuantizationType_Int8,
Memory: qdrant.Memory_Pinned.Enum(),
},
),
Payload: &qdrant.PayloadStorageParams{
Memory: qdrant.Memory_Cached.Enum(),
},
})
Optimizing for Disk-Based Retrieval
When structures are in the cold tier, retrieval may involve reading from disk. Use these techniques to reduce search latency despite the extra disk I/O.
Quantization
Quantization compresses vectors into a smaller representation. The quantized copy fits comfortably in RAM even when the original vectors are cold. This enables Qdrant to score most candidates against the quantized copy and only read the original vectors from disk to rescore the top results. Quantization cuts down on how much data needs to come from disk during search, at the cost of the small accuracy loss it introduces.
Keep the quantized vectors in RAM by setting memory: "pinned" in the quantization_config. Without pinning, the quantized copy may be evicted under memory pressure, forcing Qdrant to read both the quantized and original vectors from disk.
If the accuracy loss is acceptable, you can disable rescoring against the original vectors entirely by setting rescore: false in the quantization_config. This avoids any disk reads during search, and the memory tier of the original vectors no longer affects search latency.
Async I/O
Enable async_scorer in the storage configuration to let Qdrant issue disk reads concurrently during rescoring, instead of one at a time:
storage:
performance:
async_scorer: true
This uses io_uring, a Linux kernel interface for asynchronous I/O, and requires a kernel that supports it. Async I/O helps most when the original vectors are cold and quantization is enabled, since rescoring the top candidates against the on-disk originals is where sequential disk reads would otherwise add up.
Local NVMe/SSD Storage
On-disk retrieval benefits from fast, local storage. If you’re self hosting Qdrant, use NVMe or SSD drives attached directly to the machine. Avoid network-attached storage. It is too slow for the sequential reads that vector search requires.
Inline Storage
Available as of v1.16.0
Avoid putting the HNSW vector index in the cold tier. If you must store it on disk and use quantization, consider enabling inline storage. This reduces I/O operations at the cost of three to four times more disk usage.
Legacy Settings
Before version 1.19, memory placement was controlled by a different set of parameters. These parameters are deprecated. If you’re on a version older than 1.19, you can use the following tables to map the new memory parameter to the legacy parameters.
Dense Vectors
The legacy parameter is on_disk.
memory | Legacy value |
|---|---|
cached | on_disk: false |
cold | on_disk: true |
HNSW Vector Index
The legacy parameter is on_disk, set in hnsw_config.
memory | Legacy value |
|---|---|
pinned | No legacy equivalent |
cached | on_disk: false |
cold | on_disk: true |
Quantized Vectors
The legacy parameter is always_ram. always_ram: true always resolves to pinned. Otherwise, quantized vectors inherit the original vectors’ placement: pinned if the vectors are in RAM, cold if they’re on disk.
memory | Legacy value |
|---|---|
pinned | always_ram: true, or inherited from the original vectors |
cached | No legacy equivalent |
cold | Inherited from the original vectors |
Sparse Vector Index
The legacy parameter is on_disk.
memory | Legacy value |
|---|---|
pinned | on_disk: false |
cached | No legacy equivalent |
cold | on_disk: true |
Payloads
The legacy parameter is on_disk_payload, set on the collection.
memory | Legacy value |
|---|---|
cached | on_disk_payload: false |
cold | on_disk_payload: true |
Payload Indexes
The legacy parameter is on_disk, set on each field index.
memory | Legacy value |
|---|---|
pinned | on_disk: false |
cached | No legacy equivalent |
cold | on_disk: true |