Vector database adalah tempat menyimpan vector (embedding) yang bisa mencari "yang paling dekat" dengan cepat, bahkan di jutaan baris. Bayangin perpustakaan. Kalau bukunya 4, kamu cukup melihat semuanya satu per satu. Kalau bukunya sejuta, kamu butuh katalog yang mengelompokkan buku per rak, lalu cuma memeriksa beberapa rak terdekat. Lebih cepat, tapi sesekali buku terbaik ada di rak sebelah yang gak kamu buka. Itu inti pelajaran ini: exact (periksa semua) lawan approximate (periksa sebagian).
🧪 Tebakan cepat: Toko Buku punya 4 buku, masing-masing dengan vector 1.536 angka. Perlu vector database untuk mencarinya? Tebak dulu, jawabannya ada di bagian "Kapan array di memori sudah cukup".
Kapan array di memori sudah cukup
Exact search membandingkan pertanyaan dengan semua vector, lalu mengambil yang terdekat. Hasilnya pasti benar. Kerjanya: 4 buku × 1.536 angka = 6.144 perkalian per pertanyaan, sepele bagi server mana pun. Jadi gak, Toko Buku belum perlu vector database: array di memori atau kolom JSON (seperti di pelajaran Semantic Search) sudah cukup. Itu jawaban tebakannya.
Kerja exact search naik lurus dengan jumlah baris: 10.000 buku sudah 15,4 juta perkalian per pertanyaan, sejuta buku 1,5 miliar. Di titik itu kamu butuh dua hal: penyimpanan yang ikut transaksi database-mu, dan index yang gak memeriksa semua baris.
pgvector: vector di dalam Postgres
Kalau datamu sudah di PostgreSQL, extension pgvector menambahkan tipe vector dan pencarian nearest-neighbor. Versi terbarunya 0.8.7, rilis 1 Oktober 2026 (dicek di repo GitHub pgvector).
CREATE EXTENSION vector;
ALTER TABLE books ADD COLUMN embedding vector(1536); -- text-embedding-3-small
UPDATE books SET embedding = '[0.021, -0.013, ...]' WHERE id = 4; -- 1.536 angka dari API
SELECT id, title, (embedding <#> $1) * -1 AS kemiripan
FROM books
ORDER BY embedding <#> $1
LIMIT 10;
- Vector tinggal di tabel yang sama dengan judul dan stok, jadi ikut
JOIN, transaksi, backup, dan replikasi Postgres. - Tanpa index, query ini exact: Postgres membandingkan semua baris.
- Index pgvector untuk tipe
vectormendukung paling banyak 2.000 dimensi.text-embedding-3-small(1.536) muat;text-embedding-3-large(3.072) gak, jadi pakai tipehalfvec(sampai 4.000 dimensi) atau parameterdimensionsdari API untuk memendekkannya.
Operator jarak: <->, <=>, <#>
| Operator | Artinya | Makin kecil berarti |
|---|---|---|
<-> |
jarak L2 (garis lurus) | makin dekat |
<=> |
jarak cosine = 1 − cosine similarity | makin mirip |
<#> |
inner product negatif (dot product dikali −1) | makin mirip |
Untuk vector yang panjangnya 1, seperti embedding OpenAI, ketiganya memberi urutan yang sama, karena L2² = 2 × (1 − cosine). Contoh dari Lab, untuk 3 tetangga terdekat satu pertanyaan sintetis: <-> 0,9439 / 0,9848 / 0,9871, <=> 0,4455 / 0,4850 / 0,4871, <#> −0,5545 / −0,5150 / −0,5129. Urutannya sama persis. README pgvector menyarankan inner product (<#>) untuk vector yang sudah dinormalisasi karena paling cepat. Ingat tandanya: <#> mengembalikan nilai negatif, jadi kalikan −1 untuk mendapat dot product, dan 1 - (embedding <=> $1) untuk cosine similarity.
Exact lawan approximate: HNSW dan IVFFlat
Index vector membuat pencarian approximate (ANN, approximate nearest neighbor): jauh lebih sedikit kerja, tapi kadang tetangga terdekat yang sebenarnya terlewat. Ukurannya recall@10: dari 10 tetangga yang benar (hasil exact), berapa bagian yang ketemu. pgvector punya dua index:
| Index | Cara kerja | Parameter (default) |
|---|---|---|
| HNSW | graf berlapis; recall per kecepatan lebih baik, build lebih lambat, memori lebih besar | m 16, ef_construction 64; saat query hnsw.ef_search 40 |
| IVFFlat | vector dibagi ke lists kelompok, query memeriksa probes kelompok terdekat; build cepat, memori kecil |
lists (mulai dari baris / 1000 sampai 1 juta baris, √baris di atasnya); saat query ivfflat.probes 1 (mulai dari √lists) |
CREATE INDEX ON books USING ivfflat (embedding vector_ip_ops) WITH (lists = 32);
SET ivfflat.probes = 4; -- atau, dengan HNSW: SET hnsw.ef_search = 100;
Lab mengukurnya di 2.000 vector acak 32 dimensi (sintetis, dibuat dengan seed tetap), 20 pertanyaan, IVFFlat versi Lab dengan 32 list:
| Cara | Perbandingan per pertanyaan | Recall@10 |
|---|---|---|
| Exact | 2.000 | 1,000 |
| IVFFlat, probes 1 | 93,5 | 0,535 |
| IVFFlat, probes 4 | 292,9 | 0,850 |
| IVFFlat, probes 8 | 551,2 | 0,925 |
| IVFFlat, probes 16 | 1.075,8 | 0,990 |
Makin banyak kelompok yang diperiksa, recall naik dan kerjanya juga naik. Itu tawar-menawarnya. Angka recall asli tergantung datamu, jadi ukur sendiri dengan membandingkan hasil index dan hasil exact. Buat index IVFFlat setelah tabel berisi data, karena kelompoknya dihitung dari data yang ada.
Filter metadata: stock > 0 sebelum atau sesudah ANN
Pelanggan cuma mau buku yang ada stoknya: WHERE stock > 0. README pgvector menulis bahwa dengan index approximate, filter dijalankan sesudah index dipindai. Contohnya di README: kalau kondisi cocok dengan 10% baris dan hnsw.ef_search 40, rata-rata cuma 4 baris yang tersisa. Di Lab (9% baris punya stok, 179 dari 2.000):
| Cara | Hasil dari LIMIT 10 | Recall@10 | Perbandingan |
|---|---|---|---|
| IVFFlat probes 1, filter sesudah | 5,15 | 0,235 | 93,5 |
| IVFFlat probes 1, iterative scan | 10 | 0,420 | 196,2 |
Filter dulu (index B-tree di stock), lalu exact |
10 | 1,000 | 179 |
Iterative index scan (sejak pgvector 0.8.0: SET ivfflat.iterative_scan = relaxed_order; atau SET hnsw.iterative_scan = strict_order;) terus memindai bagian index berikutnya sampai hasilnya cukup. Untuk kondisi yang cuma cocok dengan sedikit baris, README menyarankan mulai dengan index biasa di kolom filternya (CREATE INDEX ON books (stock);): Postgres menyaring dulu, lalu menghitung jarak secara exact untuk baris yang tersisa. Di Lab, itu lebih murah sekaligus lebih tepat.
Database vector khusus
Selain pgvector, ada database yang dibuat khusus untuk vector, misalnya Pinecone (layanan terkelola), serta Weaviate dan Chroma (open source). Bedanya dengan database relasional biasa: mereka dioptimalkan untuk pencarian kemiripan di vector berdimensi tinggi, dengan index seperti HNSW, sedangkan index B-tree di database relasional dibuat untuk nilai yang sama persis atau berurutan. Kalau gak mau mengelola server Postgres sendiri, layanan Postgres terkelola seperti Supabase juga menyediakan pgvector. Urutan memilih untuk Toko Buku: array di memori selama bukunya sedikit, pgvector begitu data dan vector perlu satu tempat, dan database khusus kalau kebutuhanmu memang melebihi itu. Ukur recall dan kecepatan dengan datamu sendiri sebelum pindah.
⚠️ Jebakan yang sering ketemu
- Vector database untuk 4 buku. Satu layanan lagi untuk dirawat, padahal array di memori menghitung 6.144 perkalian dalam sekejap.
- Menganggap index itu exact. Begitu ada HNSW atau IVFFlat, hasilnya approximate. Ukur recall terhadap exact search, lalu atur
probesatauef_search. - Filter sesudah ANN tanpa sadar.
WHERE stock > 0dengan index approximate bisa mengembalikan kurang dariLIMIT. Pakai iterative scan, index B-tree di kolom filter, atau partial index. - Salah tanda
<#>. Nilainya inner product negatif.ORDER BY embedding <#> $1(naik) sudah benar; untuk menampilkan skor, kalikan −1.
TL;DR: Exact search membandingkan semua vector dan selalu benar; untuk 4 buku (6.144 perkalian) array di memori sudah cukup. pgvector (0.8.7, Oktober 2026) menaruh kolom vector(1536) di Postgres; index-nya sampai 2.000 dimensi. Operator: <-> L2, <=> jarak cosine, <#> inner product negatif; untuk vector panjang 1 seperti embedding OpenAI urutannya sama, dan <#> yang paling cepat. Index approximate: HNSW (m 16, ef_construction 64, hnsw.ef_search 40) dan IVFFlat (lists, ivfflat.probes default 1); di Lab, probes 1 memberi recall 0,535 dengan 93,5 perbandingan, probes 16 recall 0,990 dengan 1.075,8, exact 2.000. Filter seperti stock > 0 dijalankan sesudah index dipindai, jadi hasilnya bisa kurang dari LIMIT: pakai iterative scan (sejak 0.8.0) atau filter dulu dengan index B-tree. Vector database khusus (Pinecone, Weaviate, Chroma) dioptimalkan untuk pencarian kemiripan vector berdimensi tinggi; pilih berdasarkan ukuran dan hasil ukur, bukan tren.