Argumen pgvector

Satu basis data untuk baris relasional, JSON, dan vektor sebagai argumen operasional memilih pgvector.

Daftar isi

pgvector menempatkan penyimpanan dan pencarian vektor di dalam PostgreSQL, sehingga baris relasional, JSON, dan embedding dapat hidup berdampingan dalam satu basis data. Topik ini menyusun argumen operasional pendekatan satu basis data, menghitung estimasi penyimpanan untuk beberapa skala data, dan menutup dengan kondisi ketika mesin vektor khusus menjadi keputusan yang lebih masuk akal.

Satu basis data untuk tiga jenis data#

pgvector adalah ekstensi PostgreSQL, yaitu perangkat lunak tambahan yang dimuat ke dalam server untuk memperluas kemampuannya. Ekstensi diaktifkan per basis data dengan pernyataan CREATE EXTENSION, sebagaimana disiapkan pada lab jalur ini. Setelah ekstensi vector aktif, kolom bertipe vector dapat dideklarasikan berdampingan dengan kolom relasional biasa tanpa skema terpisah. Contoh berikut membuat tabel dokumen untuk arsip panduan, lengkap dengan kolom vektornya:

sql Contoh: baris relasional dan kolom vektor dalam satu tabel
-- tidak-dijalankan
CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE dokumen (
    id          bigserial PRIMARY KEY,
    judul       text NOT NULL,
    kategori_id integer NOT NULL,
    isi         text,
    embedding   vector(3)
);

INSERT INTO dokumen (judul, kategori_id, isi, embedding)
VALUES ('Panduan cadangan berkala', 2, 'langkah pemulihan', '[3,1,2]');

Dimensi 3 pada contoh dipilih agar literal vektornya pendek dan mudah dibaca, sedangkan model embedding yang umum digunakan menghasilkan vektor berdimensi 384 hingga 1536. Tipe vector menerima hingga 16.000 dimensi pada satu kolom. Kolom JSONB di sampingnya menampung atribut yang bentuknya tidak seragam, sehingga tiga gaya penyimpanan yang biasanya tersebar di tiga sistem berada dalam satu skema. Penyimpanan vektornya teratur dan dapat dihitung sejak awal, yaitu elemen float32 dengan 4 byte per dimensi, sehingga ukuran kolom vektor diketahui sebelum satu baris pun dimuat. Hitungan awal ini memudahkan perencanaan kapasitas sebelum indeks dan tabel pendamping ikut ditambahkan.

Argumen operasional satu alat#

Argumen terkuat pgvector tidak terletak pada kecepatan pencariannya, melainkan pada pengurangan jumlah sistem yang harus dirawat. Baris metadata dan vektornya ditulis dalam satu transaksi yang menjamin sifat ACID, yaitu atomisitas, konsistensi, isolasi, dan durabilitas. Metadata tidak mungkin tersimpan tanpa vektornya, karena keduanya diperlakukan sebagai satu kesatuan oleh mesin transaksi Postgres. Konsistensi ini tidak menuntut pekerjaan tambahan apa pun dari sisi aplikasi.

Kedua jenis data yang berada di satu tempat juga dapat digabungkan dalam satu pernyataan SQL. Hasil pencarian vektor dapat digabungkan dengan tabel relasional lain melalui JOIN dan dibatasi oleh klausa WHERE dalam pernyataan yang sama. Diagram berikut membandingkan pengaturan satu basis data dengan pengaturan dua sistem yang terpisah.

Satu basis data PostgreSQL dibandingkan dua sistem terpisahSatu basis data PostgreSQLtabel relasionalkolom JSONBkolom vectorsatu alat untuk transaksi, backup, dan monitoringPostgreSQLbaris relasional dan JSONmesin vektorkoleksi vektorsinkronisasidua alat yang harus dijaga sinkron
Satu basis data menyimpan baris relasional, JSON, dan vektor, sedangkan pengaturan dua sistem menuntut sinkronisasi antar alat.

Pada pengaturan dua sistem, setiap penulisan dokumen harus menyentuh kedua tempat, dan keduanya harus dijaga tetap sinkron melalui langkah tambahan. Kegagalan pada salah satu sisi meninggalkan dua salinan data yang saling bertentangan. Backup, pemulihan, monitoring, dan pengaturan akses juga dijalankan dua kali pada dua alat yang berbeda. Pada pengaturan satu basis data, seluruh pekerjaan operasional itu berlaku untuk satu alat yang antarmukanya berupa SQL yang sudah dikuasai tim. Contoh pernyataan berikut mencari lima dokumen terdekat menurut vektornya sekaligus mengambil nama kategorinya dari tabel relasional:

sql Contoh: JOIN antara hasil pencarian vektor dan tabel relasional
-- tidak-dijalankan
CREATE TABLE kategori (
    id   integer PRIMARY KEY,
    nama text NOT NULL
);

SELECT d.id, d.judul, k.nama AS kategori
FROM dokumen d
JOIN kategori k ON k.id = d.kategori_id
WHERE k.nama = 'panduan'
ORDER BY d.embedding <=> '[3,1,2]'
LIMIT 5;

Skala penyimpanan yang masih nyaman#

Argumen satu basis data perlu disertai hitungan skala, karena keputusan perpindahan ke mesin khusus sebaiknya berangkat dari angka. Setiap vektor berdimensi dd menempati d×4d \times 4 byte karena disimpan sebagai elemen float32. Skrip berikut menghitung ukuran kolom vector untuk tiga keluarga dimensi model pada dua skala baris:

python estimasi-penyimpanan.py
"""Estimasi penyimpanan kolom vector pada pgvector untuk beberapa skala data."""
import numpy as np

byte_per_dimensi = 4  # elemen float32 pada tipe vector pgvector
jumlah_vektor = 1_000_000

print(f"{'dimensi':>7} {'byte/vektor':>11} {'1 juta vektor':>14} {'10 juta vektor':>15}")
for dimensi in (384, 768, 1536):
    byte_vektor = dimensi * byte_per_dimensi
    satu_juta = np.round(jumlah_vektor * byte_vektor / 10**9, 2)
    sepuluh_juta = np.round(10_000_000 * byte_vektor / 10**9, 2)
    print(f"{dimensi:>7} {byte_vektor:>11} {satu_juta:>12} GB {sepuluh_juta:>13} GB")
text Keluaran
dimensi byte/vektor  1 juta vektor  10 juta vektor
    384        1536         1.54 GB         15.36 GB
    768        3072         3.07 GB         30.72 GB
   1536        6144         6.14 GB         61.44 GB

Kolom vector untuk 1 juta dokumen berdimensi 1536 menempati sekitar 6,1 GB, dan 10 juta dokumen menempati sekitar 61,4 GB. Angka itu masih berada dalam jangkauan Postgres yang dirawat dengan baik, dan hitungan ini baru menyentuh kolom vektornya saja. Ukuran sebenarnya bertambah dengan header tiap baris, tabel pendamping, dan indeksnya, sehingga hitungan ini berperan sebagai batas bawah, bukan totalnya.

Bagian yang menentukan kenyamanan bukan hanya penyimpanan, melainkan juga latensi pencarian. Tanpa indeks, setiap query memindai seluruh kolom vektor, dan pemindaian penuh atas 1 juta vektor sudah berat untuk permintaan yang harus dijawab cepat. Indeks HNSW atau IVFFlat menurunkan biaya per query secara besar, dengan harga berupa memori tambahan untuk menyimpan struktur indeksnya. Dengan penyimpanan dan latensi yang telah dihitung, Postgres tetap nyaman untuk jutaan vektor pada satu server yang memadai.

Kapan mesin khusus menjadi lebih masuk akal#

Tiga kondisi biasanya menggeser keputusan menuju mesin vektor khusus. Pertama, skala data naik ke ratusan juta atau miliaran vektor sehingga indeks pada satu server Postgres mulai dibatasi memori dan kecepatannya. Kedua, aplikasi menggabungkan filter yang kompleks dengan tuntutan recall yang tinggi, kombinasi yang menuntut perilaku pemindaian indeks yang lebih canggih. Ketiga, target latensi yang ketat membutuhkan distribusi horizontal yang melampaui satu server Postgres.

Tiga kondisi tersebut berdiri sendiri-sendiri, tetapi pada praktiknya muncul bertahap seiring pertumbuhan data dan kebutuhan aplikasi. Perpindahan yang dilakukan sebelum kondisinya terpenuhi justru menambah pekerjaan sinkronisasi tanpa memberikan manfaat yang terukur. Ketika kondisinya terpenuhi, Qdrant dan Milvus menyediakan arsitektur yang dirancang untuk kasus tersebut, dengan biaya operasi dua sistem sebagai konsekuensinya. Keputusan perpindahan tetap sebaiknya dibuat berdasarkan pengukuran, yaitu ukuran data, tingkat recall yang dituntut aplikasi, dan biaya menjaga dua alat tetap sinkron. Selama data masih berada dalam jangkauan hitungan pada bagian sebelumnya, satu basis data dengan pgvector tetap pilihan yang paling sederhana untuk dirawat.

Kesalahan umum#

  • Menyimpan vektor sebagai teks atau array biasa, misalnya kolom text berisi ‘[3,1,2]’, membuat kolom itu tidak mendukung operator jarak pgvector dan tidak dapat diindeks. Perbaikannya, deklarasikan kolom bertipe vector dengan dimensi yang sesuai model embedding.
  • Menjadikan estimasi penyimpanan kolom vektor sebagai total kebutuhan penyimpanan mengabaikan header baris, indeks, dan tabel pendamping yang ikut menambah ukuran. Perbaikannya, perlakukan estimasi sebagai batas bawah, lalu ukur langsung pada server dengan fungsi pg_total_relation_size().
  • Berpindah ke mesin khusus sebelum kebutuhannya terukur menambah biaya operasi dua alat, sedangkan argumen perpindahan hanya berlaku pada skala yang benar-benar memerlukannya. Perbaikannya, catat angka yang memicu keputusan tersebut, lalu tinjau ulang apabila angkanya masih berada dalam jangkauan Postgres.
  • Menulis metadata melalui aplikasi dan vektornya melalui proses terpisah menciptakan jendela ketidaksesuaian antara keduanya. Perbaikannya, kirim baris relasional dan vektornya dalam satu transaksi yang sama, sebagaimana disarankan pada bagian sebelumnya.

Ringkasan#

  • pgvector menyimpan baris relasional, JSON, dan vektor dalam satu basis data, sehingga transaksi ACID, JOIN, backup, dan monitoring dijalankan pada satu alat.
  • Kolom vector menyimpan elemen float32 dengan 4 byte per dimensi, sehingga 1 juta vektor berdimensi 1536 menempati sekitar 6,1 GB untuk kolomnya.
  • Perpindahan ke mesin khusus menjadi masuk akal pada skala ratusan juta vektor atau lebih, filter kompleks dengan tuntutan recall tinggi, dan target latensi yang menuntut distribusi horizontal.
  • Keputusan perpindahan sebaiknya berangkat dari pengukuran, bukan dari kekhawatiran.

Referensi#