Implementasi pencarian vektor (vector similarity search) pada PostgreSQL menggunakan ekstensi pgvector sering menghadapi kendala skalabilitas ketika volume embedding mencapai jutaan baris. Index HNSW (Hierarchical Navigable Small World) menawarkan latensi rendah dan recall tinggi, namun memerlukan konsumsi memori (RAM) yang signifikan. Jika ukuran index melampaui alokasi shared_buffers dan RAM sistem, performa p99 akan menurun akibat lonjakan random disk I/O.
Artikel ini membahas konfigurasi teknis untuk mereduksi memory footprint index HNSW tanpa mengorbankan akurasi pencarian secara signifikan pada workload AI produksi.
Trade-off Arsitektur: HNSW vs IVFFlat
Memilih algoritma pengindeksan vektor yang tepat bergantung pada toleransi latensi, ketersediaan RAM, dan pola pembaruan data.
- HNSW: Membangun graf multi-layer langsung di memori. Menghasilkan recall tinggi (>98%) dan latensi query sangat konsisten (p99 rendah), bahkan saat data terus bertambah. Kelemahannya: ukuran index sangat besar (dapat mencapai 1,5 hingga 2 kali ukuran raw vector) dan memakan waktu komputasi build yang lama.
- IVFFlat (Inverted File Flat): Membagi ruang vektor ke dalam cluster sentroid menggunakan Voronoi cells. Ukuran index jauh lebih kecil dan waktu build cepat. Kelemahannya: recall menurun signifikan jika jumlah cluster tidak di-tune berkala, latensi query lebih lambat, dan index perlu di-rebuild secara periodik saat distribusi data bergeser.
Gunakan HNSW untuk sistem retrieval real-time seperti Retrieval-Augmented Generation (RAG) atau semantic search interaktif. Gunakan IVFFlat hanya jika batasan RAM tidak memungkinkan penempatan graf HNSW secara penuh di memori.
Tuning Parameter Index HNSW
Ukuran struktur graf HNSW dikendalikan oleh parameter m dan ef_construction saat pembuatan index, serta parameter hnsw.ef_search saat runtime.
1. Parameter Indexing: m dan ef_construction
m: Jumlah maksimum koneksi dua arah (edges) per node pada setiap layer graf. Nilai default adalah 16. Menurunkan nilai ini mengurangi ukuran memori index secara linear, namun dapat menurunkan recall pada data berdimensi tinggi.ef_construction: Ukuran dynamic candidate list saat traversal pembentukan graf. Default adalah 64. Meningkatkan nilai ini memperbaiki kualitas graf (recall lebih tinggi) dengan konsekuensi waktu build lebih lama, tanpa menambah konsumsi RAM final pada index yang sudah selesai dibuat.
-- Ekstensi pgvector
CREATE EXTENSION IF NOT EXISTS vector;
-- Tabel dokumen dengan embedding standar (FP32)
CREATE TABLE embeddings_knowledge (
id BIGSERIAL PRIMARY KEY,
content TEXT NOT NULL,
embedding vector(1536) NOT NULL
);
-- Indexing HNSW dengan parameter memori optimal
CREATE INDEX idx_embeddings_hnsw_cosine
ON embeddings_knowledge
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 128);
2. Parameter Runtime: hnsw.ef_search
Parameter ini mengontrol kedalaman pencarian kandidat tetangga terdekat saat query dieksekusi. Nilai default adalah 40. Naikkan nilai ini per transaksi jika membutuhkan recall absolut, atau turunkan untuk mempercepat p99 query latency.
-- Meningkatkan recall untuk query spesifik
SET LOCAL hnsw.ef_search = 100;
SELECT id, content, (embedding <=> '[0.012, -0.043, ...]') AS distance
FROM embeddings_knowledge
ORDER BY embedding <=> '[0.012, -0.043, ...]'
LIMIT 5;
Kompresi Memori Menggunakan halfvec (FP16)
Tipe data standar vector menyimpan tiap komponen dimensi dalam format 32-bit floating point (4 byte). Untuk embedding 1536 dimensi (seperti OpenAI text-embedding-3-small), satu baris vektor membutuhkan 6.144 byte sebelum overhead Postgres.
Mulai pgvector v0.7.0, tipe data halfvec mendukung presisi 16-bit floating point (2 byte per dimensi). Implementasi ini memotong konsumsi memori index HNSW dan payload tabel sebesar 50% dengan degradasi recall yang umumnya di bawah 0.5%.
-- Tabel menggunakan halfvec (FP16)
CREATE TABLE embeddings_compressed (
id BIGSERIAL PRIMARY KEY,
content TEXT NOT NULL,
embedding halfvec(1536) NOT NULL
);
-- Index HNSW khusus operator class halfvec
CREATE INDEX idx_halfvec_hnsw_cosine
ON embeddings_compressed
USING hnsw (embedding halfvec_cosine_ops)
WITH (m = 16, ef_construction = 128);
Untuk mengonversi data vektor FP32 yang sudah ada ke FP16 tanpa kehilangan tabel sumber, gunakan casting langsung:
INSERT INTO embeddings_compressed (id, content, embedding)
SELECT id, content, embedding::halfvec(1536)
FROM embeddings_knowledge;
Strategi Partisi Tabel: Hot vs Cold Vector
Menjaga seluruh graf HNSW dalam RAM untuk dataset puluhan juta baris menjadi tidak efisien secara biaya jika sebagian besar data merupakan arsip historis. Gunakan PostgreSQL Declarative Partitioning untuk membatasi index HNSW hanya pada data aktif.
-- Tabel partisi berbasis range waktu
CREATE TABLE documents_partitioned (
id BIGSERIAL,
created_at TIMESTAMPTZ NOT NULL,
content TEXT NOT NULL,
embedding halfvec(1536) NOT NULL,
PRIMARY KEY (id, created_at)
) PARTITION BY RANGE (created_at);
-- Partisi aktif (bulan berjalan)
CREATE TABLE documents_2026_q1 PARTITION OF documents_partitioned
FOR VALUES FROM ('2026-01-01') TO ('2026-04-01');
-- Partisi arsip
CREATE TABLE documents_historical PARTITION OF documents_partitioned
FOR VALUES FROM ('2020-01-01') TO ('2026-01-01');
-- Index HNSW hanya pada partisi aktif untuk menghemat RAM
CREATE INDEX idx_active_hnsw ON documents_2026_q1
USING hnsw (embedding halfvec_cosine_ops)
WITH (m = 16, ef_construction = 64);
-- Index IVFFlat yang hemat memori pada partisi arsip
CREATE INDEX idx_historical_ivfflat ON documents_historical
USING ivfflat (embedding halfvec_cosine_ops)
WITH (lists = 1000);
Verifikasi Performa dengan EXPLAIN (ANALYZE, BUFFERS)
Untuk memastikan index HNSW bekerja sepenuhnya di memori tanpa bottleneck disk, periksa eksekusi query menggunakan EXPLAIN (ANALYZE, BUFFERS).
EXPLAIN (ANALYZE, BUFFERS)
SELECT id, content
FROM embeddings_compressed
ORDER BY embedding <=> '[0.012, -0.043, ...]'::halfvec
LIMIT 5;
Perhatikan metrik I/O pada query plan output:
Limit (cost=42.10..58.30 rows=5 width=48) (actual time=2.140..2.148 rows=5 loops=1)
Buffers: shared hit=42 read=0
-> Index Scan using idx_halfvec_hnsw_cosine on embeddings_compressed ...
Buffers: shared hit=42 read=0
Planning Time: 0.120 ms
Execution Time: 2.180 ms
Evaluasi output buffer:
- shared hit: Blok data dan index dibaca langsung dari
shared_buffersPostgres. Ini kondisi ideal untuk latensi sub-milidetik. - read: Blok harus diambil dari disk atau filesystem cache OS. Jika nilai
readkonsisten tinggi pada pencarian berulang, ukuran index HNSW telah melampaui RAM yang tersedia, menyebabkan I/O thrashing.
Pemeriksaan Ukuran Index Fisik
Gunakan query diagnostik berikut untuk mengukur rasio ukuran tabel terhadap index HNSW:
SELECT
c.relname AS object_name,
pg_size_pretty(pg_relation_size(c.oid)) AS size
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname IN ('embeddings_compressed', 'idx_halfvec_hnsw_cosine')
AND n.nspname = 'public';
Dengan menggabungkan tipe data halfvec, tuning parameter m ke rentang 16-24, dan memisahkan partisi aktif dari historis, konsumsi memori graf HNSW dapat dipangkas hingga 50-70% dengan stabilitas performa p99 pada lingkungan beban tinggi.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!