VPS üzerinde vektör veritabanı kurulumu ve performans
Uygulamanız ve vektör indeksiniz aynı VPS üzerinde olduğunda ağ gecikmesi sorununu nasıl aşarsınız? pgvector, Qdrant ve Chroma karşılaştırması ile RAM hesabı rehberi.
Bir VPS üzerinde vektör veritabanı kullanmanın gerçek maliyeti
Bir VPS (sanal özel sunucu) üzerinde vektör veritabanı çalıştırmak, yönetilen servis sağlayıcıların çözüm olarak sunduğu tüm sorunları ortadan kaldırır. Uygulamanız ve indeksiniz aynı makinede yer aldığından, bir arama isteği ağ üzerinden değil, bir loopback soketi üzerinden iletilir. Geriye kalan ise her zaman asıl maliyet olan metni vektöre dönüştürme işlemidir. Bunun arkasında iki maliyet kalemi daha bulunur: indeksi oluşturma süresi ve indeksin hizmet verdiği sürece kapladığı RAM miktarı.
Bu durum, hangi kararların önemli olduğunu değiştirir. Bölge seçimi ve uç nokta gidiş-dönüş süreleri artık sizin sorununuz olmaktan çıkar. Vektör sayısı, boyutlar ve dört baytın çarpımı sizin temel sorununuz haline gelir; çünkü bu değer, indeksin her ay kiraladığınız belleğe sığıp sığmayacağını belirler.
Tek bir sunucuda milisaniyelerin gerçek dağılımı
Kendi barındırdığınız (self-hosted) bir yığında benzerlik sorgusunun izlediği yolu takip edin.
- Sorgu metni, bir embedding modeli tarafından vektöre dönüştürülür. CPU üzerinde kısa bir metin için bu işlem onlarca ila yüzlerce milisaniye sürer. GPU üzerinde ise tek haneli milisaniyelerdedir.
- Vektör, depolama birimine gönderilir. Loopback TCP veya Unix domain socket üzerinden bu işlem bir milisaniyenin çok küçük bir kısmını alır.
- Depolama birimi dizinini tarar ve en yakın satırları döndürür.
- Kodunuz eşleşen metni okur ve bir istem (prompt) oluşturur.
- adım genellikle bu listedeki en büyük süreyi oluşturur. 2. adım, barındırma hizmeti sunan firmaların rekabet ettiği kısımdır; ancak tek bir sunucuda bu süre neredeyse yok denecek kadar azdır. Süre dağılımını tahmin etmeyin. Kendi sunucunuzda her iki ucun da süresini ölçün.
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"}'Ardından arama sorgusundan önce \timing on komutunu psql içinde çalıştırın. Eğer ilk komut embed: 0.184312s çıktısını verirse ve psql, Time: 4.201 ms yanıtını dönerse, dizin ayarı yapmak yanlış bir işlemdir: gecikmenizin sebebi embedding modelidir. Embedding modelini Ollama ile yerel olarak çalıştırmak, 1. adımı 2. ve 3. adımlarla aynı CPU üzerine taşır; bu durumda her iki yarı da aynı çekirdekler ve aynı RAM için rekabet eder. Bu depolama birimi etrafında sarmalanan veri girişi ve geri getirme döngüsü, kendi kendine barındırılan RAG hattı kılavuzu içinde ele alınmıştır. RAG, "retrieval augmented generation" (geri getirme ile güçlendirilmiş üretim) anlamına gelir: kendi belgelerinizde arama yapar ve en iyi eşleşmeleri bir istemin içine yapıştırırsınız.
Yüz bin vektörün altındaki verilerde tümünü tarayın
Kapsamlı bir tarama, sorguyu depolanan her bir vektörle karşılaştırır. Geri çağırma (recall) tanım gereği mükemmeldir. Herhangi bir dizine veya oluşturma adımına ihtiyaç duymaz ve verilerinizin gerisinde kalarak güncelliğini yitiremez.
Aritmetik hesaplama, bu yöntemin ne zaman verimsizleşeceğini size söyler. Bir tarama, sorgu başına n * d * 4 bayt okur; burada n vektör sayısı, d ise boyuttur. 768 boyutlu 100.000 vektörde bu, sorgu başına 307 MB eder ve modern bir işlemci bunu birkaç on milisaniyede işleyebilir. 5 milyon vektörde ise bu değer sorgu başına 15 GB olur ki bu artık bir sorgu olarak kabul edilemez.
Bu nedenle vektörleri SQLite içinde saklayın ve karşılaştırmayı NumPy ile yapın.
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]]Her iki taraf da birim uzunluğa ölçeklendiğinden, nokta çarpımı (dot product) doğrudan kosinüs benzerliğidir ve daha yüksek bir puan daha yakın bir eşleşmeyi ifade eder. mat değerini sorgu başına değil, başlangıçta bir kez yükleyin; böylece SQLite okuma işlemi kritik yoldan tamamen çıkar.
Reddetmeden önce kendi makinenizde süre ölçümü yapın.
import time
t = time.perf_counter()
search(q)
print(f"{(time.perf_counter() - t) * 1000:.1f} ms")Dürüst sınırlar şunlardır: Bu yöntem, tüm matrisi RAM'de tutan tek bir süreçtir; meta veri filtrelemesi sunmaz ve eşzamanlı yazma işlemlerini desteklemez. Bu nedenlerden biri sizi kısıtlamaya başladığında başka bir çözüme geçin. SQLite'ın kendisi ciddi bir sunucu tarafı deposudur; üretim ortamında SQLite rehberi bunu detaylandırır. Eğer gerçek iş yükünüz satır sunmaktan ziyade sütun taramaksa, DuckDB ve SQLite karşılaştırması daha faydalı bir okuma olacaktır.
pgvector, halihazırda Postgres çalıştırıyorsanız
Uygulamanız halihazırda bir Postgres veritabanı kullanıyorsa, pgvector en az yeni yüzey alanı ekleyen çözümdür. Bu bir servis değil, bir eklentidir. Vektörler, tanımladıkları satırın yanında normal bir tabloda tutulur; bu sayede filtrelenmiş arama, senkronize tutulması gereken ikinci bir sistem yerine bir WHERE yan tümcesi haline gelir.
Ubuntu 24.04, bu eklentiyi universe bileşeninde sunar.
sudo apt update
sudo apt install -y postgresql-16-pgvector
sudo -u postgres psql -d yourdb -c 'CREATE EXTENSION vector;'Ağustos 2026 itibarıyla bu paket, ana kaynaktan oldukça geride olan pgvector 0.6.0 sürümüdür. Özellikle yinelemeli dizin taramaları 0.8 sürümüne ihtiyaç duyar, bu nedenle bu paketleri PostgreSQL projesinin kendi deposundan temin edin.
sudo apt install -y postgresql-common
sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh
sudo apt install -y postgresql-17-pgvector17 ifadesini, sudo -u postgres psql -tAc 'SHOW server_version' komutunun çıktısı olan sunucunuzun ana sürüm numarasıyla değiştirin. Yanlış ana sürüm için derlenmiş eklenti paketini yüklerseniz CREATE EXTENSION başarısız olur; çünkü Postgres yalnızca çalışan sürümün share dizinini kontrol eder:
ERROR: could not open extension control file "/usr/share/postgresql/16/extension/vector.control": No such file or directoryŞema, yeni bir veri tipi içeren standart SQL yapısındadır.
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;<=> kosinüs uzaklığı, <-> L2 (Öklid) uzaklığı ve <#> negatif iç çarpımdır. Gömme (embedding) modelinizin eğitildiği yöntemi kullanın. Yanlış olanı seçerseniz bir hata almazsınız, ancak sonuçlarınız sessizce daha kötü hale gelir.
Dizin (index) olmadığında bu sorgu, her satır üzerinde tam bir arama yapar; bu, yukarıda bahsedilen kaba kuvvet yönteminin Postgres karşılığıdır ve aynı mükemmel geri çağırma (recall) oranına sahiptir. max_parallel_workers_per_gather değerini artırmak, sorguya daha fazla çekirdek tahsis edilmesini sağlar. Bunu dizin oluşturmadan önce yapın; böylece dizinin başarısını ölçmek için bir geri çağırma referansınız olur.
Qdrant, dizin veritabanını aştığında
Qdrant, Rust ile yazılmış özel bir vektör deposudur. Dizin, oluşturulma sürecinin uygulamanızın Postgres veritabanı ile kaynak rekabetine girmesini istemeyeceğiniz kadar büyüdüğünde veya pgvector'ün sunmadığı payload filtreleme ve nicemleme (quantisation) özelliklerine ihtiyaç duyduğunuzda tercih edilir.
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/qdrant6333 numaralı port, REST (representational state transfer) API'sine ve /dashboard adresindeki kontrol paneline hizmet verir; 6334 numaralı port ise gRPC için kullanılır. Halka açık bir VPS üzerinde iki ayrıntı önem taşır. Qdrant'ın kendi dokümantasyonu, servisin varsayılan olarak "şifreleme veya kimlik doğrulama olmadan" çalıştığını belirtir. Hızlı başlangıç kılavuzundaki -p 6333:6333 komutu, servisi tüm arayüzlere bağlar; Docker, kendi yönlendirme kurallarını yazdığı için bu durum ufw kuralını aşarak portu dış dünyaya açar. Servisi 127.0.0.1 adresine bağlayın ve bir API anahtarı belirleyin. Anahtarsız bir şekilde genel IP adresi üzerinden erişilebilen bir Qdrant örneği, belgelerinizin herkese açık bir kopyası haline gelir.
curl -s http://127.0.0.1:6333/collections -H "api-key: $QDRANT_API_KEY"Sağlıklı bir yanıt {"result":{"collections":[]},"status":"ok","time":0.00002} gibi görünür. {"status":{"error":"Unauthorized"}} yanıtını almak, başlık adının veya anahtarın yanlış olduğu anlamına gelir; hiçbir yanıt alamamak ise container'ın çalışmadığını veya başka bir yere bağlandığını gösterir. Bu container'ın sunucunuz için uygun olup olmadığı, standart bir stateful servis sorusudur; bu nedenle Docker ile ana makine veritabanı karşılaştırması burada da geçerliliğini korur.
Chroma ve kullanım amacı
Chroma, boş bir ortamdan çalışan bir geri getirme (retrieval) demosu oluşturmanın en kısa yoludur.
pip install chromadb
chroma run --path /srv/chromaBu servis 8000 numaralı port üzerinde çalışır ve chromadb.HttpClient(host="localhost", port=8000) buna bağlanır. Chroma varsayılan bir gömme (embedding) fonksiyonu ile gelir, bu nedenle ilk prototip için ayrı bir model sunucusuna ihtiyaç duyulmaz.
Bu değiş tokuş konusunda dürüst olunmalıdır. Chroma, bu kılavuzun konusu olan kararları (hangi mesafe metriğinin kullanılacağı ve sonucun ne kadar RAM kaplayacağı gibi) gizlediği için kullanımı kolaydır. Bu durum bir prototip için doğru, ancak gece yarısı çağrı almanıza neden olacak bir sistem için yanlıştır. Verileriniz halihazırda Postgres üzerinde tutuluyorsa, bunları Chroma'ya taşımak, pgvector'un sahip olmadığı bir sorunu çözmek adına fazladan bir süreç ve senkronizasyon problemi yaratır.
Dizin ne kadar RAM gerektirecek
Ham vektörlerden başlayın; bunlar temel gereksinimdir ve hiçbir optimizasyon bu miktarı azaltamaz.
bytes = number_of_vectors * dimensions * 4Her boyut için 32-bit float değeri 4 bayt yer kaplar. Qdrant kapasite planlama belgeleri, meta veriler ve optimizasyon sırasında oluşturulan geçici segmentler için 1,5 çarpanı eklenmesini önerir:
memory_size = number_of_vectors * vector_dimension * 4 bytes * 1.5Aşağıdaki formül, gerçek embedding modellerinin ürettiği boyutlarda bir milyon vektör için hesaplanmıştır.
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
}
]Bunlar formülün gibibyte (GiB) cinsinden çıktılarıdır, ölçüm değeri değildir. Bunları bellekte ayırmanız gereken alanın boyutu olarak değerlendirin. Bir milyon parça üzerinde nomic-embed-text gibi 768 boyutlu bir model yaklaşık 4.29 GiB bellek gerektirir; bu da 8 GB'lık bir plana, Postgres için yer kalacak şekilde sığar. Aynı veri kümesi 3072 boyutta 17.17 GiB gerektirir ve bu plana sığmaz.
Buradaki belirleyici faktör tablonun son sütunu değil, ilk sütunudur. Boyutu yarıya indirmek, ondan sonra gelen her bayt miktarını kalıcı olarak yarıya düşürür. Genel bir liderlik tablosunda biraz daha düşük puan alan 768 boyutlu bir model, bir VPS üzerinde genellikle daha iyi bir mühendislik tercihidir. pgvector'ün halfvec türü 16-bit float değerleri saklar, bu da bayt miktarını tekrar yarıya indirir ve indeksleme bir ifade aracılığıyla yapılır:
CREATE INDEX ON chunks USING hnsw ((embedding::halfvec(768)) halfvec_cosine_ops);Model seçmeden önce bilmeniz gereken bir sınır vardır. pgvector'ün vector türü 16.000 boyuta kadar kabul eder, ancak HNSW ve IVFFlat indeksleri yalnızca 2.000 boyuta kadar destek verir. Bunun üzerindeki değerler için 4.000 boyuta kadar ulaşabilen bir halfvec cast işlemi ile indeksleme yapabilir ya da hiç indeksleme yapmayabilirsiniz.
Derleme zamanında m ve ef_construction maliyeti
HNSW (hierarchical navigable small world), hem pgvector hem de Qdrant tarafından kullanılan bir dizin yapısıdır. Bu, katmanlı bir çizge (graph) yapısıdır. Her vektör, yakınındaki vektörlere bağlantıları olan bir düğümdür; arama işlemi, tüm veriyi okumak yerine bu bağlantılar üzerinden sorguya doğru ilerler.
m, her düğümün kaç bağlantı tutacağını belirler. Faiss dokümantasyonu, HNSW bellek kullanımını vektör başına (d * 4 + m * 2 * 4) bayt olarak belirtir ve m değerinin 4 ile 64 arasında tutulmasını önerir. Bunu 768 boyutta ve bir milyon vektör üzerinde çalıştırdığınızı varsayalım.
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
}
]Buradaki faydalı sürpriz, bu farkın boyutudur. Varsayılan m = 16 değerinden m = 64 değerine geçmek, 3072 baytlık vektör verisine karşılık vektör başına 512 baytlık bağlantı verisi ekler; böylece toplam boyut 2.98 GiB değerinden 3.34 GiB değerine çıkar. Bu yaklaşık yüzde 12'lik bir artıştır. Bu boyutlarda belleğinizin tükendiği yer m değildir. Belleği tüketen asıl unsur vektörlerin kendisidir.
m değerinin asıl maliyeti derleme ve ekleme süresidir; çünkü bir düğümü yerleştirmek, o kadar sayıda komşuyu bulmak ve bağlamak anlamına gelir. ef_construction, derleyicinin her düğümü yerleştirirken değerlendirdiği aday listesinin boyutudur. Bu değeri artırmak daha iyi bir çizge yapısı ve daha yavaş bir derleme süresi sağlar, ancak tamamlanmış dizin boyutunu hiçbir şekilde değiştirmez.
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, derleme işleminin dakikalar mı yoksa saatler mi süreceğini belirleyen ayardır; çünkü pgvector, sığdığı sürece çizgeyi bellekte oluşturur. Belleğe sığmadığında pgvector şu uyarıyı verir:
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.Bu bildirim, pgvector tarafından yazdırılan en yararlı satırdır. Bu, derleme işleminin çok daha yavaş bir yola girdiğini gösterir; bu durumda işlemi iptal edin, ayarı yukarıda hesapladığınız RAM değerinin üzerine çıkarın ve yeniden başlatın. İlerlemeyi ikinci bir oturumdan izleyin:
SELECT phase, round(100.0 * blocks_done / nullif(blocks_total, 0), 1) AS pct
FROM pg_stat_progress_create_index;HNSW, initializing ve ardından loading tuples değerlerini raporlar. Meşgul olmayan bir disk üzerinde uzun süre düşük yüzde değerinde kalan bir derleme işlemi, takılmış bir sorgu değil, maintenance_work_mem sorunudur.
Planlama yaparken iki gerçeği göz önünde bulundurmak gerekir. pgvector README dosyası, HNSW'nin IVFFlat'e göre "daha yavaş derleme sürelerine sahip olduğunu ve daha fazla bellek kullandığını", buna karşılık hız ve geri çağırma (recall) dengesinde daha iyi sonuç verdiğini belirtir. Ayrıca HNSW boş bir tablo üzerinde oluşturulabilirken, IVFFlat'in önce temsilci veriler üzerinde k-means çalıştırması gerekir; bu nedenle boş bir tabloda oluşturulması düşük geri çağırma oranına yol açar. Yeni bir şemada, en başta oluşturabileceğiniz dizin türü HNSW'dir.
ef_search: derleme sonrasında ayarlanan parametre
m ve ef_construction indeks içine sabitlenmiştir. ef_search ise sabit değildir. Bu parametre, arama işlemi graf üzerinde ilerlerken kaç adet adayın tutulacağını belirler ve oturum ya da sorgu bazında değiştirilebilir.
SET hnsw.ef_search = 100;
SELECT id, body FROM chunks ORDER BY embedding <=> $1 LIMIT 5;Varsayılan değer 40'tır. Bu değeri artırdığınızda geri çağırma (recall) oranı yükselir, ancak gecikme süresi de artar. Değeri düşürdüğünüzde her ikisi de azalır. Bu, yeniden derleme yapmadan değiştirebileceğiniz tek geri çağırma kontrolüdür; bu nedenle, doğru cevaplarını bildiğiniz sabit bir sorgu kümesi üzerinde ayarlama yapın ve geri çağırma oranı iyileşmeyi durdurduğunda işlemi sonlandırın.
Bir tuzak noktası mevcuttur. ef_search, seçici bir WHERE yan tümcesi ile kötü etkileşime girer; çünkü indeks sabit sayıda aday döndürür ve filtreleme işlemi sonrasında uygulanır. Satırların çoğunu reddeden bir filtre, eşleşen satırlar tabloda dururken elinizde LIMIT değerinden daha az sonuç kalmasına neden olabilir. pgvector 0.8 sürümü, bu sorunu yinelemeli taramalar ile çözer:
SET hnsw.iterative_scan = relaxed_order;İndeks, limit karşılanana kadar daha fazla aday için yeniden taranır; bu işlem, varsayılan değeri 20000 olan hnsw.max_scan_tuples sınırına kadar devam eder. strict_order, tam mesafe sıralamasını korur ve daha maliyetlidir. Ubuntu 0.6.0 paketinde bulunmayan özellik budur ve filtre altında eksik satırlarla karşılaştığınızda bu eksikliği fark edersiniz.
Dizinin neden RAM'e sığması gerektiği
HNSW araması, bir grafik üzerinde yapılan bir gezintidir. Her sıçrama, bir öncekinden bağımsız bir konumda saklanan bir düğümü okur; bu nedenle erişim düzeni rastgeleye yakındır ve önceden okuma (read-ahead) mekanizması işe yaramaz. Grafik RAM'de olduğu sürece her sıçrama bir bellek referansıdır. Grafik RAM'de olmadığında ise bir sıçrama disk okumasına dönüşebilir ve birkaç yüz düğüme dokunan bir arama, birkaç yüz disk okumasına yol açar.
Qdrant belgeleri bu durumu şöyle özetler: "RAM'de yarı yarıya daha az vektör saklarsanız, arama gecikmesi kabaca iki katına çıkar." Planlamanızı bu cümleyi dikkate alarak yapın.
Dizinin gerçekten sığmayacağı durumlarda, her seçenek bilinçli olarak yapmanız gereken bir takas işlemidir.
- Vektörleri bellek eşlemeli (memory-map) hale getirin; böylece işletim sistemi sık kullanılan sayfaları önbelleğe alır ve seyrek kullanılanları diskte bırakır. Bunun kabul edilebilir olması için altta hızlı bir NVMe depolama birimi bulunmalıdır.
- Niceleme (quantisation) uygulayın; her boyutu dört bayt yerine bir bayt olarak saklayın. Bu, ölçülebilir küçük bir doğruluk kaybı karşılığında vektör boyutunu dörtte bire indirir.
- pgvector içinde
halfvecformatına dönüştürün; bu, tek baytlık nicelemeye göre daha az doğruluk kaybıyla bayt boyutunu yarıya indirir. - Daha küçük bir model ile gömme (embedding) işlemi yapın. Bu en ucuz çözümdür ancak insanlar tarafından genellikle göz ardı edilir, çünkü tüm veri kümesinin yeniden gömülmesini gerektirir.
Hata modları ve karşılaşacağınız dizgeler
could not open extension control file. Çalışan Postgres ana sürümü için pgvector paketi yüklü değildir. Sürümü sudo -u postgres psql -tAc 'SHOW server_version' ile yazdırın ve eşleşen postgresql-NN-pgvector paketini yükleyin.
ERROR: expected 768 dimensions, not 1536. Sütun türü ile model uyuşmuyor. Gömme (embedding) modellerini değiştirdiniz ancak verileri yeniden işlemediniz. Burada kısmi bir çözüm yoktur; iki farklı modelden gelen vektörler birbiriyle kıyaslanamaz, bu nedenle her satırın yeniden oluşturulması gerekir.
Sorgu yavaştır ve EXPLAIN bir sequential scan gösteriyor. İndeks operatör sınıfı ile sorgu operatörü eşleşmiyor. vector_cosine_ops yalnızca <=> için hizmet verir. Sorgu üzerinde EXPLAIN ANALYZE çalıştırın ve Index Scan using ... on chunks ifadesini arayın. Bunun yerine Seq Scan on chunks görüyorsanız, indeksinizi aslında sorguladığınız operatörle eşleşen operatör sınıfını kullanarak yeniden oluşturun.
LIMIT değerinden daha az satır dönüyor ve bir WHERE ifadesi mevcut. Bu, yukarıda bahsedilen filtreleme tuzağıdır. hnsw.ef_search değerini yükseltin veya pgvector 0.8 sürümüne geçerek hnsw.iterative_scan ayarını yapılandırın.
İndeks oluşturma işlemi, süreç sonlanarak bitiyor ve psql içinde hiçbir hata görünmüyor. maintenance_work_mem değerinin makine kapasitesinin büyük kısmına ayarlanması, shared_buffers ve uygulamanızın da bellek talep etmesiyle birleşince çekirdeğin out-of-memory killer mekanizması devreye girer. sudo dmesg -T | grep -i 'killed process', postgres sürecini işaret eden satırı gösterir. Ayarı düşürün veya indeksi daha yüksek kapasiteli bir planda oluşturup yedeği geri yükleyin.
Birini seçmek
Hali hazırda Postgres kullanıyorsanız ve birkaç milyondan az vektörünüz varsa, pgvector kullanın. İndeks verinin yanında yer alır, filtreleme bir WHERE ifadesidir ve mevcut yedekleme stratejiniz bunu zaten kapsar. İndeks kendi bellek sınırına ihtiyaç duyacak kadar büyükse veya yoğun yük filtrelemesine gereksinim duyuyorsanız, Qdrant'ı yanına kurun ve ikinci bir servisi yönetmeyi kabul edin.
Yaklaşık yüz bin vektörün altındaki boyutlarda, herhangi bir kurulum yapmadan önce brute-force tarama performansını ölçün. Bu boyutta, mükemmel geri çağırma (recall) oranına sahip ve herhangi bir oluşturma adımı gerektirmeyen kapsamlı bir arama bir ödünleşim değildir. Bu doğru yaklaşımdır; bunun yerine yaklaşık bir indekse yönelmek, harcamadığınız milisaniyeler karşılığında ayarlama yükü ve RAM baskısını üstlenmek anlamına gelir.
FAQ
Özel bir vektör veritabanına mı ihtiyacım var, yoksa Postgres yeterli mi?
Verileriniz halihazırda Postgres üzerinde tutuluyorsa, pgvector çoğu karşılaştırmanın önerdiğinden çok daha uzun süre yeterli olacaktır. Vektörleri standart bir sütunda sakladığı için filtrelenmiş aramalar bir WHERE ifadesiyle gerçekleştirilebilir ve mevcut yedekleme stratejiniz dizini de kapsar. Vektör iş yükünüzün kendine ait bir bellek sınırına ihtiyaç duyduğu veya pgvector tarafından sunulmayan yük filtresi (payload filtering) ve niceleme (quantisation) özelliklerine gereksinim duyduğunuz durumlarda Qdrant gibi özel bir depolama çözümüne geçiş yapın.
Bir VPS üzerinde kaç adet vektör tutulabilir?
Tahmin etmek yerine number_of_vectors * dimensions * 4 bytes * 1.5 kullanarak hesaplama yapın. Bir milyon adet 768 boyutlu vektör yaklaşık 4.3 GiB yer kaplar; dolayısıyla 8 GB RAM'e sahip bir plan, Postgres için de yeterli alan bırakarak bu veriyi barındırabilir. Bir milyon adet 3072 boyutlu vektör ise yaklaşık 17 GiB yer kaplar ve çok daha yüksek kapasiteli bir plan gerektirir. Bu durumu en çok etkileyen faktör embedding modelinizin boyutudur, bu nedenle model seçiminizi bellek maliyetini göz önünde bulundurarak yapın.
Dizin aynı makinede olmasına rağmen vektör aramam neden yavaş?
Tek bir makinede ağ kaynaklı bir sorun yaşanmaz, bu nedenle iki temel noktaya odaklanın. İlk olarak, embedding çağrısını tek başına ölçün; çünkü sorgu vektörünü CPU üzerinde oluşturmak, genellikle arama işleminin kendisinden çok daha uzun sürer. İkinci olarak, dizinin RAM üzerinde olup olmadığını kontrol edin. HNSW araması bir grafik üzerinde rastgele atlamalar yapar; bu nedenle grafik diske taştığında her atlama bir disk okuma işlemine dönüşebilir. Qdrant'ın kendi kılavuzuna göre, RAM'de tutulan vektör sayısının yarıya düşürülmesi, arama gecikmesini yaklaşık iki katına çıkarır.
HNSW dizini oluşturmalı mıyım?
Yaklaşık yüz bin vektörün altındaki veriler için buna gerek yoktur. Kapsamlı bir tarama, sorgu başına n * d * 4 bayt okur; bu da 768 boyutlu 100.000 vektör için 307 MB demektir. Modern bir CPU, bu veriyi mükemmel bir doğrulukla ve herhangi bir dizin oluşturma adımı gerektirmeden milisaniyeler içinde işleyebilir. Öncelikle kendi donanımınız üzerinde tarama süresini ölçün. Dizin oluşturma işlemini, ölçülen tarama süresi gerçekten yavaş kaldığında yapın; sadece bir kıyaslama makalesi öyle söylediği için değil.
m değerini artırmanın maliyeti nedir?
Bellekten ziyade, oluşturma ve ekleme süresi üzerinde çok daha büyük bir maliyeti vardır. 768 boyutlu verilerde, varsayılan m = 16 değerinden m = 64 değerine geçmek, vektör başına 3072 baytlık vektör verisine ek olarak 512 baytlık grafik bağlantısı ekler; bu da toplam bellek kullanımını yaklaşık yüzde 12 oranında artırır. Ancak her ekleme işlemi, dört kat daha fazla komşu bulmak ve bunları bağlamak zorundadır. Öncelikle ef_search değerini optimize edin; çünkü bu değerin değiştirilmesi herhangi bir maliyet getirmez ve dizinin yeniden oluşturulmasını gerektirmez.