SSD Nodes Learn 🎉 VPS dari $5.50/bln
Panduan Matt ConnorOleh Matt Connor

Cara Jalankan Pangkalan Data Vektor pada VPS

Ketahui cara menguruskan pangkalan data vektor pada VPS anda. Bandingkan prestasi pgvector, Qdrant, dan Chroma serta cara mengira penggunaan RAM untuk indeks anda.

Kos sebenar pangkalan data vektor pada VPS

Menjalankan pangkalan data vektor pada VPS (virtual private server) menghapuskan masalah yang cuba diselesaikan oleh setiap vendor terurus. Aplikasi dan indeks anda berada pada mesin yang sama, jadi permintaan carian merentasi soket loopback dan bukannya rangkaian. Apa yang tinggal hanyalah kos yang sentiasa menjadi kos sebenar: menukar teks kepada vektor. Di sebaliknya terdapat dua lagi kos, iaitu masa untuk membina indeks dan RAM yang digunakan oleh indeks tersebut selama ia beroperasi.

Ini mengubah keputusan yang penting. Wilayah dan tempoh perjalanan pergi-balik endpoint bukan lagi kebimbangan anda. Hasil darab bilangan vektor, dimensi dan empat bait menjadi kebimbangan anda, kerana ia menentukan sama ada indeks tersebut muat dalam memori yang anda sewa setiap bulan.

Ke mana perginya milisaat pada satu pelayan

Ikuti satu pertanyaan kesamaan (similarity query) melalui tindanan (stack) yang dihoskan sendiri.

  1. Teks pertanyaan ditukar menjadi vektor oleh model embedding. Pada CPU, proses ini mengambil masa puluhan hingga ratusan milisaat untuk rentetan pendek. Pada GPU, ia hanya mengambil masa satu digit milisaat.
  2. Vektor dihantar ke storan. Melalui loopback TCP atau Unix domain socket, proses ini hanya mengambil sebahagian kecil daripada satu milisaat.
  3. Storan menyemak indeksnya dan mengembalikan baris yang paling hampir.
  4. Kod anda membaca teks yang sepadan dan menyusun prompt.

Langkah 1 biasanya merupakan angka terbesar dalam senarai tersebut. Langkah 2 adalah langkah yang menjadi persaingan antara vendor hos, dan pada satu pelayan, ia hampir tidak wujud. Jangan meneka pembahagian masa tersebut. Ukur masa kedua-dua hujung pada pelayan anda sendiri.

curl http://127.0.0.1:11434/api/embed -s -o /dev/null \
  -w 'embed: %{time_total}s\n' \
  -d '{"model": "nomic-embed-text", "input": "how do I rotate the api key"}'

Kemudian jalankan \timing on dalam psql sebelum pertanyaan carian. Jika arahan pertama mencetak embed: 0.184312s dan psql menjawab Time: 4.201 ms, maka menala indeks adalah tindakan yang salah: kependaman (latency) anda berpunca daripada model embedding. Menjalankan model embedding secara setempat dengan Ollama meletakkan langkah 1 pada CPU yang sama dengan langkah 2 dan 3, jadi kedua-dua bahagian bersaing untuk teras dan RAM yang sama. Gelung ingest dan retrieval yang dibina di sekitar storan ini diterangkan dalam panduan pipeline RAG yang dihoskan sendiri. RAG bermaksud retrieval augmented generation: anda mencari dokumen anda sendiri dan menampal padanan terbaik ke dalam prompt.

Di bawah kira-kira seratus ribu vektor, imbas kesemuanya

Imbasan menyeluruh membandingkan pertanyaan dengan setiap vektor yang disimpan. Recall adalah sempurna mengikut definisi. Ia tidak memerlukan indeks atau langkah binaan, dan ia tidak akan menjadi lapuk berbanding data anda.

Aritmetik memberitahu anda bila ia tidak lagi sesuai. Imbasan membaca n * d * 4 bait bagi setiap pertanyaan, dengan n ialah bilangan vektor dan d ialah dimensi. Pada 100,000 vektor dengan 768 dimensi, ini adalah 307 MB bagi setiap pertanyaan, yang mana CPU moden boleh menstrimnya dalam beberapa puluh milisaat. Pada 5 juta vektor, ia menjadi 15 GB bagi setiap pertanyaan, yang mana ia bukan lagi satu pertanyaan.

Jadi, simpan vektor dalam SQLite dan lakukan perbandingan dalam NumPy.

import sqlite3, numpy as np

db = sqlite3.connect("docs.db")
db.execute("CREATE TABLE IF NOT EXISTS docs (id INTEGER PRIMARY KEY, body TEXT, vec BLOB)")

def add(body, vec):
    v = np.asarray(vec, dtype=np.float32)
    v /= np.linalg.norm(v)
    db.execute("INSERT INTO docs (body, vec) VALUES (?, ?)", (body, v.tobytes()))
    db.commit()

def search(query_vec, k=5):
    rows = db.execute("SELECT id, body, vec FROM docs").fetchall()
    mat = np.frombuffer(b"".join(r[2] for r in rows), dtype=np.float32).reshape(len(rows), -1)
    q = np.asarray(query_vec, dtype=np.float32)
    q /= np.linalg.norm(q)
    scores = mat @ q
    return [(rows[i][0], rows[i][1], float(scores[i])) for i in np.argsort(-scores)[:k]]

Kedua-dua belah pihak diskalakan kepada panjang unit, jadi hasil darab titik adalah similarity kosinus dan skor yang lebih tinggi bermakna padanan yang lebih dekat. Muatkan mat sekali semasa permulaan dan bukannya sekali bagi setiap pertanyaan, supaya bacaan SQLite keluar sepenuhnya daripada laluan kritikal (hot path).

Uji masa pada mesin anda sendiri sebelum anda menolaknya.

import time
t = time.perf_counter()
search(q)
print(f"{(time.perf_counter() - t) * 1000:.1f} ms")

Had yang jujur: ini adalah satu proses yang memegang keseluruhan matriks dalam RAM, dan ia tidak memberikan anda penapisan metadata serta tiada mekanisme untuk penulis serentak. Apabila salah satu daripada perkara ini menjadi sebab anda tidak berpuas hati, beralihlah. SQLite sendiri adalah storan sisi pelayan yang serius, yang mana panduan SQLite dalam pengeluaran akan membincangkannya, dan jika beban kerja sebenar anda adalah mengimbas lajur dan bukannya menghidangkan baris, maka perbandingan DuckDB dan SQLite adalah bacaan yang lebih berguna.

pgvector, apabila anda sudah menjalankan Postgres

Jika aplikasi anda sudah mempunyai pangkalan data Postgres, pgvector menambah permukaan serangan baharu yang paling minimum. Ia merupakan sambungan (extension), bukan servis. Vektor disimpan dalam jadual biasa bersebelahan dengan baris yang diterangkannya, jadi carian bertapis hanyalah klausa WHERE dan bukannya sistem kedua yang perlu diselaraskan.

Ubuntu 24.04 menyertakan pakej ini dalam komponen universe.

sudo apt update
sudo apt install -y postgresql-16-pgvector
sudo -u postgres psql -d yourdb -c 'CREATE EXTENSION vector;'

Pakej tersebut ialah pgvector 0.6.0 setakat Ogos 2026, yang jauh ketinggalan berbanding versi hulu (upstream). Imbasan indeks lelaran (iterative index scans) khususnya memerlukan versi 0.8, jadi ambil pakej tersebut daripada repositori projek PostgreSQL sendiri.

sudo apt install -y postgresql-common
sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh
sudo apt install -y postgresql-17-pgvector

Gantikan 17 dengan versi utama pelayan anda, yang dipaparkan oleh sudo -u postgres psql -tAc 'SHOW server_version'. Jika anda memasang pakej sambungan yang dibina untuk versi utama yang salah, CREATE EXTENSION akan gagal, kerana Postgres hanya mencari dalam direktori share bagi versi yang sedang berjalan:

ERROR:  could not open extension control file "/usr/share/postgresql/16/extension/vector.control": No such file or directory

Skema ini adalah SQL biasa dengan satu jenis data baharu.

CREATE TABLE chunks (
  id        bigserial PRIMARY KEY,
  doc_id    bigint NOT NULL,
  body      text   NOT NULL,
  embedding vector(768)
);

SELECT id, body FROM chunks
ORDER BY embedding <=> '[0.013, -0.021, 0.004]'
LIMIT 5;

<=> ialah jarak kosinus, <-> ialah jarak L2 (Euclidean), dan <#> ialah hasil darab dalam negatif (negative inner product). Gunakan jenis yang sepadan dengan model penyemat (embedding model) anda. Jika anda memilih yang salah, tiada ralat akan berlaku, namun hasil carian anda akan menjadi kurang tepat secara senyap.

Tanpa indeks, pertanyaan tersebut merupakan carian tepat ke atas setiap baris, yang merupakan versi Postgres bagi kaedah kekerasan (brute force) di atas dan mempunyai tahap perolehan (recall) sempurna yang sama. Meningkatkan max_parallel_workers_per_gather akan menggunakan lebih banyak teras pemproses untuk tugas tersebut. Lakukan ini dahulu dan buat indeks kemudian, kerana kini anda mempunyai garis dasar perolehan untuk mengukur prestasi indeks tersebut.

Qdrant, apabila indeks melebihi saiz pangkalan data

Qdrant ialah stor vektor khusus yang ditulis dalam Rust. Ia menjadi pilihan apabila indeks cukup besar sehingga anda tidak mahu proses pembinaannya bersaing dengan Postgres aplikasi anda, atau apabila anda memerlukan penapisan payload dan kuantisasi yang tidak ditawarkan oleh pgvector.

docker run -d --name qdrant \
  -p 127.0.0.1:6333:6333 -p 127.0.0.1:6334:6334 \
  -e QDRANT__SERVICE__API_KEY="$(openssl rand -hex 32)" \
  -v "$(pwd)/qdrant_storage:/qdrant/storage:z" \
  qdrant/qdrant

Port 6333 melayani API REST (representational state transfer) dan papan pemuka pada /dashboard, manakala 6334 melayani gRPC. Dua perincian penting pada VPS awam. Dokumentasi Qdrant sendiri menyatakan bahawa servis berjalan secara lalai "tanpa penyulitan atau pengesahan", dan -p 6333:6333 daripada panduan permulaan pantas mengikat pada setiap antara muka, yang diterbitkan oleh Docker melepasi peraturan ufw kerana ia menulis peraturan penghalaannya sendiri. Ikat pada 127.0.0.1 dan tetapkan kunci API. Instans Qdrant yang boleh dicapai pada IP awam tanpa kunci adalah salinan dokumen anda yang terbuka kepada umum.

curl -s http://127.0.0.1:6333/collections -H "api-key: $QDRANT_API_KEY"

Jawapan yang sihat kelihatan seperti {"result":{"collections":[]},"status":"ok","time":0.00002}. Mendapatkan {"status":{"error":"Unauthorized"}} bermakna nama pengepala atau kunci adalah salah, dan tidak mendapat apa-apa bermakna kontena tidak berjalan atau terikat di tempat lain. Sama ada kontena tersebut sesuai untuk pelayan anda adalah soalan biasa bagi servis stateful, jadi perbandingan Docker berbanding pangkalan data hos terpakai di sini tanpa perubahan.

Chroma, dan kegunaannya

Chroma ialah laluan terpantas untuk membina demo carian (retrieval) yang berfungsi dari sifar.

pip install chromadb
chroma run --path /srv/chroma

Ia beroperasi pada port 8000, dan chromadb.HttpClient(host="localhost", port=8000) bersambung kepadanya. Chroma didatangkan dengan fungsi embedding lalai, jadi prototaip awal tidak memerlukan pelayan model yang berasingan sama sekali.

Berterus-teranglah tentang pertukaran ini. Chroma menyenangkan kerana ia menyembunyikan keputusan yang dibincangkan dalam panduan ini: metrik jarak yang mana, dan berapa banyak RAM yang akan digunakan oleh hasil tersebut. Ini adalah tepat untuk prototaip, tetapi tidak sesuai untuk sistem yang menyebabkan anda dihubungi (paged) jika berlaku gangguan. Jika data anda sudah berada dalam Postgres, memindahkannya ke Chroma akan menambah satu proses dan masalah penyelarasan untuk menyelesaikan masalah yang tidak wujud pada pgvector.

Berapakah RAM yang diperlukan oleh indeks

Mulakan dengan vektor mentah, kerana ia adalah nilai asas dan tiada pelarasan yang boleh mengubahnya.

bytes = number_of_vectors * dimensions * 4

Empat bait adalah bersamaan dengan satu float 32-bit bagi setiap dimensi. Dokumentasi perancangan kapasiti Qdrant menambah pengganda 1.5 untuk metadata dan segmen sementara yang dibuat semasa pengoptimuman:

memory_size = number_of_vectors * vector_dimension * 4 bytes * 1.5

Berikut adalah formula tersebut yang dijalankan ke atas satu juta vektor, pada dimensi yang dihasilkan oleh model embedding sebenar.

ChartRAM for 1 million vectors, by embedding dimension
The data behind this chart
[
  {
    "label": "384 dims",
    "raw_gib": 1.43,
    "with_overhead_gib": 2.15
  },
  {
    "label": "768 dims",
    "raw_gib": 2.86,
    "with_overhead_gib": 4.29
  },
  {
    "label": "1024 dims",
    "raw_gib": 3.81,
    "with_overhead_gib": 5.72
  },
  {
    "label": "1536 dims",
    "raw_gib": 5.72,
    "with_overhead_gib": 8.58
  },
  {
    "label": "3072 dims",
    "raw_gib": 11.44,
    "with_overhead_gib": 17.17
  }
]

Itu adalah output formula dalam gibibyte (GiB), bukan ukuran sebenar. Baca ia sebagai saiz ruang yang perlu anda sediakan dalam memori. Model 768-dimensi seperti nomic-embed-text untuk sejuta ketulan (chunks) memerlukan kira-kira 4.29 GiB, yang muat dalam pelan 8 GB dengan ruang yang masih berbaki untuk Postgres. Korpus yang sama pada 3072 dimensi memerlukan 17.17 GiB dan tidak akan muat.

Tuas (lever) sebenar adalah lajur pertama carta itu, bukan yang terakhir. Mengurangkan dimensi kepada separuh akan mengurangkan setiap bait seterusnya kepada separuh, selama-lamanya. Model 768-dimensi yang mendapat skor sedikit rendah pada papan pendahulu awam selalunya merupakan pilihan kejuruteraan yang lebih baik pada VPS. Jenis halfvec dalam pgvector kemudiannya menyimpan float 16-bit, yang mengurangkan bait kepada separuh sekali lagi, dan ia mengindeks melalui satu ungkapan:

CREATE INDEX ON chunks USING hnsw ((embedding::halfvec(768)) halfvec_cosine_ops);

Satu had yang perlu diketahui sebelum anda memilih model. Jenis vector dalam pgvector menerima sehingga 16,000 dimensi, tetapi indeks HNSW dan IVFFlat miliknya hanya meliputi 2,000 dimensi. Melebihi had itu, anda perlu mengindeks melalui cast halfvec, yang mencapai 4,000, atau anda tidak mengindeks langsung.

Apakah kos m dan ef_construction semasa binaan

HNSW (hierarchical navigable small world) ialah indeks yang digunakan oleh pgvector dan Qdrant. Ia merupakan graf berlapis. Setiap vektor adalah nod dengan pautan ke nod berdekatan, dan carian akan melompat sepanjang pautan tersebut ke arah pertanyaan (query) dan bukannya membaca keseluruhan data.

m ialah bilangan pautan yang dikekalkan oleh setiap nod. Dokumentasi Faiss menyatakan memori HNSW adalah (d * 4 + m * 2 * 4) bait bagi setiap vektor dan mengesyorkan agar m ditetapkan antara 4 hingga 64. Jalankan ini pada 768 dimensi merentasi sejuta vektor.

ChartCost of raising m at 768 dimensions, 1 million vectors
The data behind this chart
[
  {
    "label": "m = 8",
    "link_bytes_per_vector": 64,
    "total_gib": 2.92
  },
  {
    "label": "m = 16 (default)",
    "link_bytes_per_vector": 128,
    "total_gib": 2.98
  },
  {
    "label": "m = 32",
    "link_bytes_per_vector": 256,
    "total_gib": 3.1
  },
  {
    "label": "m = 64",
    "link_bytes_per_vector": 512,
    "total_gib": 3.34
  }
]

Perkara yang mengejutkan ialah saiz perbezaan tersebut. Menukar daripada tetapan lalai m = 16 kepada m = 64 menambah 512 bait pautan bagi setiap vektor berbanding 3072 bait data vektor, jadi jumlah keseluruhan berubah daripada 2.98 GiB kepada 3.34 GiB. Ini adalah kira-kira 12 peratus. Pada dimensi ini, m bukanlah punca penggunaan memori anda. Vektor itu sendiri yang memakan memori.

Kos sebenar m adalah pada masa binaan dan masa sisipan, kerana meletakkan nod bermaksud mencari dan memautkan sebanyak itu jiran. ef_construction ialah saiz senarai calon yang dipertimbangkan oleh pembina semasa ia meletakkan setiap nod. Meningkatkan nilainya menghasilkan graf yang lebih baik dan binaan yang lebih perlahan, namun ia tidak mengubah saiz indeks yang siap sama sekali.

SET maintenance_work_mem = '4GB';
SET max_parallel_maintenance_workers = 7;
CREATE INDEX ON chunks USING hnsw (embedding vector_cosine_ops)
  WITH (m = 16, ef_construction = 64);

maintenance_work_mem ialah tetapan yang menentukan sama ada binaan mengambil masa beberapa minit atau jam, kerana pgvector memasang graf dalam memori apabila ia muat. Apabila ia tidak lagi muat, pgvector akan memaklumkannya:

NOTICE:  hnsw graph no longer fits into maintenance_work_mem after 100000 tuples
DETAIL:  Building will take significantly more time.
HINT:  Increase maintenance_work_mem to speed up builds.

Pemberitahuan itu adalah baris paling berguna yang dicetak oleh pgvector. Ia bermaksud binaan telah beralih ke laluan yang jauh lebih perlahan, jadi batalkan proses tersebut, tingkatkan tetapan melebihi angka RAM yang anda kira di atas, dan mulakan semula. Pantau kemajuan daripada sesi kedua:

SELECT phase, round(100.0 * blocks_done / nullif(blocks_total, 0), 1) AS pct
FROM pg_stat_progress_create_index;

HNSW melaporkan initializing dan kemudian loading tuples. Binaan yang kekal pada peratusan rendah untuk masa yang lama pada cakera yang tidak sibuk adalah masalah maintenance_work_mem, bukannya pertanyaan yang tersangkut.

Dua fakta yang perlu dirancang. README pgvector menyatakan HNSW "mempunyai masa binaan yang lebih perlahan dan menggunakan lebih banyak memori" berbanding IVFFlat, dan sebagai balasan, ia memberikan pertukaran kelajuan terhadap recall yang lebih baik. Selain itu, HNSW boleh dicipta pada jadual kosong, manakala IVFFlat perlu menjalankan k-means ke atas data perwakilan terlebih dahulu, jadi membina IVFFlat pada jadual kosong akan memberikan recall yang lemah. Pada skema baharu, HNSW adalah pilihan yang boleh anda cipta di peringkat awal.

ef_search: parameter yang dilaraskan selepas binaan

m dan ef_construction dibekukan ke dalam indeks. ef_search tidak. Ia menetapkan bilangan calon yang dikekalkan oleh carian semasa ia menyusuri graf, dan anda boleh mengubahnya bagi setiap sesi atau setiap pertanyaan.

SET hnsw.ef_search = 100;
SELECT id, body FROM chunks ORDER BY embedding <=> $1 LIMIT 5;

Nilai lalai ialah 40. Tingkatkan nilai ini dan recall akan meningkat, namun latensi juga akan meningkat. Rendahkan nilai ini dan kedua-duanya akan menurun. Ini merupakan satu-satunya kawalan recall yang boleh anda ubah tanpa perlu membina semula indeks, jadi laraskan ia berdasarkan set pertanyaan tetap yang jawapan betulnya sudah anda ketahui, dan berhenti apabila recall tidak lagi menunjukkan peningkatan.

Satu perangkap. ef_search berinteraksi dengan buruk dengan klausa WHERE yang selektif, kerana indeks memberikan bilangan calon yang tetap dan penapis (filter) digunakan selepas itu. Penapis yang menolak kebanyakan baris boleh menyebabkan anda mendapat kurang daripada LIMIT hasil, sedangkan baris yang sepadan masih ada di dalam jadual. pgvector 0.8 menjawab masalah ini dengan imbasan berulang (iterative scans):

SET hnsw.iterative_scan = relaxed_order;

Indeks kemudian diimbas semula untuk mendapatkan lebih banyak calon sehingga had dipenuhi, sehingga maksimum hnsw.max_scan_tuples, yang mempunyai nilai lalai 20000. strict_order mengekalkan susunan jarak yang tepat dan memerlukan kos yang lebih tinggi. Ini adalah ciri yang tidak terdapat dalam pakej Ubuntu 0.6.0, dan baris yang hilang di bawah penapis adalah cara anda menyedari ketiadaan ciri tersebut.

Mengapa indeks perlu dimuatkan ke dalam RAM

Carian HNSW ialah proses melintasi graf. Setiap lompatan membaca nod yang disimpan di lokasi yang tidak berkaitan dengan nod sebelumnya, jadi corak capaian adalah hampir rawak dan teknik read-ahead tidak membantu. Selagi graf berada dalam RAM, setiap lompatan hanyalah rujukan memori. Apabila ia tidak lagi berada dalam RAM, satu lompatan boleh menjadi bacaan cakera, dan carian yang menyentuh beberapa ratus nod akan menjadi beberapa ratus bacaan cakera.

Dokumentasi Qdrant memberikan gambaran jelas: "jika anda menyimpan separuh daripada jumlah vektor dalam RAM, latensi carian akan meningkat kira-kira dua kali ganda." Rancang strategi anda berdasarkan kenyataan tersebut.

Apabila indeks benar-benar tidak muat, setiap pilihan adalah pertukaran (trade-off) yang perlu anda buat dengan sengaja.

  • Gunakan memory-map untuk vektor supaya sistem pengendalian menyimpan halaman yang kerap diakses (hot pages) dalam cache dan membiarkan halaman yang jarang diakses (cold pages) pada cakera. Ini memerlukan storan NVMe (non-volatile memory express) yang pantas untuk memastikan prestasi boleh diterima.
  • Lakukan kuantisasi (quantisation), dengan menyimpan setiap dimensi sebagai satu bait dan bukannya empat. Ini mengurangkan saiz bait vektor sebanyak empat kali ganda dengan kos recall yang kecil dan boleh diukur.
  • Lakukan cast kepada halfvec dalam pgvector, yang mengurangkan saiz bait kepada separuh dengan kehilangan recall yang lebih rendah berbanding kuantisasi satu bait.
  • Gunakan model yang lebih kecil untuk embedding. Ini adalah penyelesaian paling murah dan sering diabaikan oleh pengguna, kerana ia bermakna anda perlu melakukan embedding semula terhadap keseluruhan korpus.

Mod kegagalan dan rentetan yang akan anda lihat

could not open extension control file. Pakej pgvector untuk versi utama Postgres yang sedang berjalan tidak dipasang. Paparkan versi dengan sudo -u postgres psql -tAc 'SHOW server_version' dan pasang postgresql-NN-pgvector yang sepadan.

ERROR: expected 768 dimensions, not 1536. Jenis lajur dan model tidak sepadan. Anda telah menukar model embedding dan tidak melakukan embedding semula. Tiada penyelesaian separa di sini, kerana vektor daripada dua model yang berbeza tidak boleh dibandingkan sama sekali, jadi setiap baris perlu dijana semula.

Pertanyaan perlahan dan EXPLAIN menunjukkan imbasan berjujukan (sequential scan). Kelas operator indeks dan operator pertanyaan tidak sepadan. vector_cosine_ops hanya melayani <=>. Jalankan EXPLAIN ANALYZE pada pertanyaan tersebut dan cari Index Scan using ... on chunks. Jika anda melihat Seq Scan on chunks sebaliknya, bina semula indeks dengan kelas operator yang sepadan dengan operator yang anda gunakan dalam pertanyaan.

Bilangan baris kurang daripada LIMIT, dan klausa WHERE hadir. Itu adalah perangkap penapisan di atas. Tingkatkan hnsw.ef_search, atau beralih ke pgvector 0.8 dan tetapkan hnsw.iterative_scan.

Binaan indeks berakhir dengan proses hilang dan tiada ralat dalam psql. maintenance_work_mem ditetapkan kepada hampir keseluruhan mesin, sementara shared_buffers dan aplikasi anda juga memerlukan memori, berakhir dengan pembunuh kehabisan memori (out-of-memory killer) kernel. sudo dmesg -T | grep -i 'killed process' menunjukkan baris yang menamakan postgres. Rendahkan tetapan tersebut, atau bina indeks pada pelan yang lebih besar dan pulihkan dump.

Memilih satu

Jika anda sudah menjalankan Postgres dan mempunyai kurang daripada beberapa juta vektor, gunakan pgvector. Indeks tersebut terletak di sebelah data, penapisan dilakukan melalui klausa WHERE, dan sandaran sedia ada anda sudah merangkuminya. Jika indeks tersebut cukup besar sehingga memerlukan had memori sendiri, atau anda memerlukan penapisan bebanan (payload) yang berat, jalankan Qdrant di sampingnya dan terima hakikat bahawa terdapat servis kedua yang perlu diuruskan.

Di bawah kira-kira seratus ribu vektor, ukur imbasan brute-force sebelum anda memasang apa-apa. Carian menyeluruh dengan recall yang sempurna dan tanpa langkah binaan bukanlah satu kompromi pada saiz tersebut. Ia adalah jawapan yang tepat, dan memilih indeks anggaran sebaliknya bermakna anda menanggung beban penalaan dan tekanan RAM sebagai pertukaran untuk milisaat yang sebenarnya tidak anda perlukan.

FAQ

Adakah saya memerlukan pangkalan data vektor khusus, atau adakah Postgres sudah memadai?

Jika data anda sudah berada dalam Postgres, pgvector adalah mencukupi untuk tempoh yang lebih lama daripada yang disarankan oleh kebanyakan perbandingan. Ia menyimpan vektor dalam lajur biasa, jadi carian bertapis adalah klausa WHERE dan sandaran sedia ada anda merangkumi indeks tersebut. Beralihlah kepada storan khusus seperti Qdrant apabila beban kerja vektor memerlukan had memori tersendiri, atau apabila anda memerlukan penapisan muatan (payload filtering) dan kuantisasi yang tidak disediakan oleh pgvector.

Berapa banyak vektor yang boleh dimuatkan oleh satu VPS?

Kira jumlahnya dan jangan meneka, gunakan number_of_vectors * dimensions * 4 bytes * 1.5. Satu juta vektor 768-dimensi adalah kira-kira 4.3 GiB, jadi pelan 8 GB boleh memuatkannya dengan ruang yang mencukupi untuk Postgres. Satu juta vektor 3072-dimensi adalah kira-kira 17 GiB dan memerlukan pelan yang jauh lebih besar. Faktor yang paling mempengaruhi jumlah ini ialah dimensi model embedding anda, jadi pilihlah model tersebut dengan mengambil kira kos memori.

Mengapa carian vektor saya perlahan apabila indeks berada pada mesin yang sama?

Rangkaian bukanlah masalah anda pada satu mesin, jadi perhatikan dua perkara yang mungkin menjadi punca. Pertama, ukur masa panggilan embedding secara berasingan, kerana menjana vektor pertanyaan pada CPU sering kali mengambil masa yang jauh lebih lama daripada carian itu sendiri. Kedua, pastikan indeks berada dalam RAM. Carian HNSW melompat secara rawak melalui graf, jadi sebaik sahaja graf tersebut melimpah ke cakera, setiap lompatan boleh menjadi bacaan cakera, dan panduan Qdrant sendiri menyatakan bahawa mengurangkan separuh vektor yang disimpan dalam RAM akan menggandakan latensi carian.

Adakah saya perlu membina indeks HNSW?

Tidak perlu jika jumlah vektor kurang daripada kira-kira seratus ribu. Imbasan menyeluruh (exhaustive scan) membaca n * d * 4 bait setiap pertanyaan, iaitu 307 MB bagi 100,000 vektor 768-dimensi, dan CPU moden boleh menstrim data tersebut dalam masa beberapa puluh milisaat dengan ketepatan (recall) yang sempurna tanpa langkah pembinaan. Ukur imbasan pada perkakasan anda sendiri terlebih dahulu. Bina indeks apabila masa imbasan yang diukur benar-benar terlalu perlahan, bukan kerana artikel penanda aras menyarankannya.

Apakah kos sebenar meningkatkan m?

Masa pembinaan dan masa sisipan (insert), jauh lebih tinggi daripada memori. Pada 768 dimensi, menukar daripada tetapan lalai m = 16 kepada m = 64 menambah 512 bait pautan graf bagi setiap vektor berbanding 3072 bait data vektor, jadi jumlah memori meningkat kira-kira 12 peratus. Walau bagaimanapun, setiap sisipan perlu mencari dan memautkan empat kali ganda lebih banyak jiran. Laraskan ef_search terlebih dahulu, kerana ia tidak menelan sebarang kos untuk diubah dan tidak memerlukan pembinaan semula.

#vector-database#rag#pgvector#qdrant#self-hosting