Vector index parameters reference
How to tune Relata's HNSW + DiskANN + IVF vector indexes. Covers index creation, parameter selection, distance metrics, quantization, and search-time knobs.
Index lifecycle
A vector index lives on a typed table for every column whose name matches
_emb_* (the convention for embedding slots). The index is keyed on
(object_type, modality, model_tag, tenant_id) — multi-tenant by default.
| Phase | Trigger | Effect |
|---|---|---|
| Writing | First insert of an _emb_* field | HNSW graph grows; tombstones accumulate |
| Compacting | Tombstone ratio > 10% | Tier compaction merges; dead nodes reclaimed |
| Reading | SIMILAR TO query, /search, or HYBRID_SEARCH | Beam search + filtered refine |
| Spilling | max_live exceeded | Spill to DiskANN warm tier (object-store-backed flat segments) |
By default, the index is lazy — the first query that touches an _emb_*
field triggers build. Set RELATA_ANN_EAGER=true to build at insert time
(higher ingest cost; lower first-query latency).
HNSW parameters (index-time)
These are set at index creation (first insert) and cannot be changed without a rebuild.
| Parameter | Default | Range | Effect |
|---|---|---|---|
M | 128 | 16–256 | Out-degree per node (layer > 0). Higher = better recall, more RAM. |
M0 | 256 | 32–512 | Out-degree at layer 0 (the data layer). Typically 2× M. |
ef_construction | 350 | 100–1000 | Beam width during build. Higher = better recall, slower build. |
ml | 1/ln(M) | (computed) | Level-decay factor for random level assignment. |
Memory cost: ~3 KB × vectors at f32 / 1536-d (OpenAI). ~1 KB × vectors
at int8 / 384-d (MiniLM). For 1 M vectors at f32/1536-d: ~3 GB.
Choosing M and ef_construction
| Workload | M | ef_construction | Notes |
|---|---|---|---|
| High recall, low QPS | 256 | 500 | Best recall@10; 2× RAM |
| Balanced (default) | 128 | 350 | Good recall, fast search |
| Low latency, high QPS | 64 | 200 | Lower recall; ~half RAM |
| Binary embeddings | 32 | 100 | For 1-bit codes |
HNSW parameters (search-time)
| Parameter | Default | Range | Effect |
|---|---|---|---|
ef_search | max(k, 10) | k–1000 | Beam width at query. Higher = better recall, slower. |
limit (k) | query-supplied | 1–1000 | Top-K returned |
filter | none | allowlist | Pre-filter or post-filter (adaptive at 25% selectivity) |
Tuning ef_search
-- Tighten ef_search for fast low-recall queries:
SELECT * FROM Vec WHERE SIMILAR TO '[...]' FIELD=_emb_text LIMIT 10 EF_SEARCH=20
-- Loosen for high recall:
SELECT * FROM Vec WHERE SIMILAR TO '[...]' FIELD=_emb_text LIMIT 10 EF_SEARCH=200The default max(k, 10) is fine for most workloads. For high-recall
applications (face recognition, dedup), set ef_search = 5 * k.
Distance metrics
Today, cosine is the default and most-optimised metric.
| Metric | Use case | Cost vs cosine |
|---|---|---|
Cosine (default) | Semantic similarity (most embedding models) | 1× |
L2Squared | Face recognition, image search | ~1× |
InnerProduct (MIPS) | DPR-style retrievers | ~0.9× |
SELECT * FROM Face WHERE SIMILAR TO '[...]' FIELD=_emb_face METRIC=L2 LIMIT 10Quantization
| Tier | Bytes per dim | Recall loss | Use when |
|---|---|---|---|
f32 / full (default) | 4 | 0% | Default (RELATA_VECTOR_QUANT=full); small indexes |
| FP16 | 2 | <0.5% | 2× memory savings |
| int8 | 1 | ±0.4% | 4× memory savings; opt in via RELATA_VECTOR_QUANT=int8 |
| PQ | ~0.1 | 2–5% | Billion-scale cold tier |
| Binary hash | 1/8 | 5–15% | Deep-1-bit first-pass filter |
The HNSW hot tier defaults to full (raw f32 — RELATA_VECTOR_QUANT=full). Set RELATA_VECTOR_QUANT=int8 for a 4× memory reduction at ±0.4% recall. The IVF cold tier uses int8 with
optional PQ (32–64× compression).
Cold tier (DiskANN + IVF)
When the index exceeds RELATA_DISKANN_MAX_RESIDENT, the cold tier engages:
| Component | Purpose | Trigger |
|---|---|---|
| IVF bucket | Cluster by k-means++ centroids | Always on (cold tier) |
| Posting list | Per-centroid vector list | Paged from object store via LRU |
nprobe | Number of centroids to search | Default 32 |
| DiskANN graph | Object-store-backed HNSW for SSD traversal | ADR-185 |
# Bound RAM-resident vectors in the cold tier staging area
RELATA_VECTOR_COLD_RESIDENT_MAX=200000 # default 100 000
# Bound total HNSW-resident vectors (warns past this)
RELATA_DISKANN_MAX_RESIDENT=1000000 # 0 / unset = unboundedSearch-time knobs
/search body
{
"query": "alice",
"vector": [0.1, 0.2, ...],
"vector_field": "_emb_text",
"metric": "cosine",
"limit": 10,
"ef_search": 100,
"filter": {"tenant_id": "org-acme"}
}SQL SIMILAR TO
SELECT * FROM Vec
WHERE SIMILAR TO '[0.1, 0.2, ...]'
FIELD=_emb_text
METRIC=COSINE
LIMIT 10
EF_SEARCH=100Pre-filter vs post-filter
For selective predicates (e.g. "only vectors where tenant_id = 'org-acme'"),
Relata chooses automatically based on selectivity:
| Selectivity | Strategy | Why |
|---|---|---|
| > 25% | Post-filter | Most candidates survive; ANN is faster |
| <25% | Pre-filter (allowlist) | Few candidates; skip ANN entirely |
Performance characteristics
| Operation | Latency (1 M vectors, int8, 384-d) |
|---|---|
| Build (parallel bulk insert) | ~3 min on 16 cores |
| Point search (k=10) | <5 ms p99 |
| Filtered search (1% selectivity) | <8 ms p99 |
| Insert (single vector) | <100 µs |
| Soft delete (tombstone) | <10 µs |
| Compaction (10% tombstones) | ~5 s for 1 M vectors |
Common pitfalls
| Pitfall | Symptom | Fix |
|---|---|---|
| ef_search too low | Recall@10 <0.9 | Raise EF_SEARCH=100 or higher |
| M too low for high-dimensional embeddings | Recall plateaus | Rebuild with M=256 |
| Filtered search recall cliff | Recall@10 drops sharply at <5% selectivity | Use pre-filter explicitly |
| Index RAM exceeds budget | OOM warning at startup | Reduce max_resident or shard the type |
| Stale tombstone accumulation | Search latency drifts upward | Trigger compaction (relata compact --type <T>) |