Eszközök/RAG és keresés

pgvector, értékelés: az a vektordatázis, amelyet nem kell üzemeltetned

Értékelés a pgvector 0.8.7-ről: iteratív indexszkennelés szűrt kereséshez, HNSW és IVFFlat, bináris kvantizálás 100 millió vektornál, és a CVE, amely az indexépítést javítási tétellé tette.

Típus
Vector database extension
Ár
PostgreSQL licence

··10 perc olvasás

  • Vector search
  • Postgres
  • HNSW
  • RAG
  • Quantisation
A lekérdezés felül három útra bontakozik: pontos szekvenciális szkennelés, HNSW gráfjárás és IVFFlat vizsgálat; alul egy iteratív szkennelés folytatódik, amíg a limit kitelik.

A lényeg röviden

  • A pgvector a PostgreSQL licenc alatt álló bővítmény, így a vektorok sima táblákban laknak, az ügyfélszűrők, JOIN-ek és kaszkádos törlések pedig tranzakciók maradnak.
  • Az iteratív indexszkennelés, ami 0.8.0 óta létezik, a szűrt keresés megoldása: nélküle a sorok 10%-át eltaláló szűrő alapértelmezett ef_search 40 mellett körülbelül négy sort ad vissza.
  • A AWS 367 GB-os HNSW-indexet mért 100 millió 768 dimenziós vektornál, szemben egy 38 GB-os, binárisan kvantizált indexszel, amely 1,1 óra alatt épült meg 16,1 helyett.
  • A CVE-2026-3172, amelyet 2026 februárjában a 0.8.2 javított, puffer-túlcsordulás volt a párhuzamos HNSW-indexépítésben, amely más relációk adatait szivárogtathatta ki; a javításhoz nem kell újraindexelés.
  • A korlát a memória, nem az API: ha az index nem fér már a shared_buffers-be, a válaszok a halfvec, a bináris kvantizálás, a particionálás vagy egy második rendszer.

A pgvector egy PostgreSQL-bővítmény, amely vektorokat egy sima tárolóba tesz, és SQL-lel keres azokban. Nem egy rácsavarozott lekérdezésnyelvű vektordatbázis: négy oszloptípust, hat távolságoperátort és két indextípust ad ahhoz a adatbázishoz, amelyet a legtöbb csapat amúgy is üzemeltet, a PostgreSQL licenc alatt, amely a lehető legmegengedőbb. Az itt képviselt álláspont: ez a helyes alapértelmezés szinte minden, legfeljebb néhány tízmillió vektorig terjedő előkeresési feladathoz, és aki ekkora méretnél külön vektordatázist épít, nem képességet vesz, hanem üzemeltetési terhet.

A Qdranttal, a Weavitate-val, a Chromával és a teljesen felhőben üzemeltetett vektor szolgáltatásokkal versengez, valamint minden olyan hosztolt Postgresszel, amely ma már alapból szállítja a bővítményt. Ami megszűnik, az egy teljes infrastruktúra-kategória: nincs második szolgáltatás, nincs második kommunikációs protokoll hitelesítésre, nincs második mentési ütemezés, nincs eltérés a sorok és azokat leíró beágyazások között. Ami megmarad, az a Postgres minden korlátja: egy csomópont memóriája, egy csomópont vacuumja, egy csomópont írási teljesítménye. És ezek válnak a tervezési paraméterekké abban a pillanatban, amikor az index már nem fér a RAM-ba.

Mi ez

Az első kiadás, a 0.1.0, 2021. április 20-án jelent meg, a jelenlegi kiadás pedig a 2026. október 1-i 0.8.7; a repository ellenőrzéskor 23.300 csillagot és 1.300 forkot mutatott. A PostgreSQL 13 és újabb verzióit támogatja, Docker-imageként, PGXN-en, APT-n, Yumon, Homebrew-en és conda-forge-on át kapható, és egyre több hosztolt szolgáltatónál előre telepítve érkezik. Ez azért számít, mert a régi verzión ragadt felhős szolgáltatás a leggyakoribb oka annak, hogy egy csapat úgy hiszi, megvan egy biztonsági javítása, ami nincs meg.

  • Licenc: a PostgreSQL licenc, ugyanaz a megengedő szöveg, amelyet maga a PostgreSQL használ. Nincs open core, nincs kereskedelmi szint, nincs fizetős terv mögé rejtett funkció.
  • A 0.8.7-es kiadás 2026. október 1-jén; a 0.8-as széria iteratív indexszkennelést ad 2024 októbere, a 0.8.0 óta, és ez a funkció tette kiszámíthatóvá a szűrt keresést.
  • Négy típus: vector dimenciónként 4 bájttal, halfvec 2-vel, bit dimenciónként egy bittel, és sparsevec ritka vektorokhoz legfeljebb 1000 nem-nulla elemmel.
  • Két indextípus: HNSW a legjobb sebesség-találati arányért, edzési lépés nélkül, IVFFlat gyorsabb építéshez és kisebb memóriaigényhez.
  • Hat operátor, közvetlenül az ORDER BY-ban: L2 (<->), belső szorzat (<#>), koszinusz (<=>), L1 (<+>), Hamming (<~>) és Jaccard (<%>).
  • A tárolás és a hozzáférés magától a Postgrestől jön: ACID, WAL-alapú replikáció, pillanatnyi visszaállítás, JOIN-ek és soronkénti biztonság ugyanabban a táblában, mint a beágyazások.

Hogyan működik

Index nélkül a vektor-lekérdezés szekvenciális szkennelés, távolságfüggvényre rendezett ORDER BY-jal: pontos eredmény, tökéletes találati arány, és a tábla méretével növekvő költség. Ezt a játszmát egy közelítő index megváltoztatja. A HNSW többrétegű grágot épít a vektorok fölé, és azon jár kicsit gyengébb találati arányért cserébe, a költség viszont többé nem nő a táblával; az IVFFlat vektorokat listákba rendez, és csak egy részüket vizsgálja. A mindent eldöntő részlet, hogy a lekérdezéstervező csak akkor nyúl az indexhez, ha a lekérdezés így néz ki: ORDER BY embedding <=> $1 LIMIT n. Ugyanez ORDER BY 1 - (embedding <=> $1) DESC alakban a README szerint nem használ indexet.

Három végrehajtási út egyetlen vektor-lekérdezéshezA távolságoperátoros ORDER BY-t és LIMIT-et hordozó lekérdezés felül három útra bontakozik. Balra, nincs index: szekvenciális szkennelés tökéletes találati aránnyal, aminek a költsége soronként nő. Középen, HNSW: járás egy többrétegű gráfon, alapértelmezett m = 16 és ef_search = 40 mellett, ahol a WHERE feltétel csak az indexszkennelés jelöltjei után fut. Jobbra, IVFFlat: vektorok listákba rendezve, csak a legközelebbi listák vizsgálva, az adatok betöltése után építve, az ivfflat.probes állítva, gyengébb sebesség-találati arány mellett. Alul egy sáv: az iteratív indexszkennelés, ami 0.8.0 óta addig fut, amíg a szűrő sorokat ejt, strict_order vagy relaxed_order módban, a hnsw.max_scan_tuples által korlátozva, alapértelmezett 20.000.One query, three execution pathsthe ORDER BY decidesQueryORDER BY distance, LIMIT 10Exact scansequential scanperfect recallcost grows per rowno index neededHNSWmultilayer graph walkm = 16, ef_search = 40filter runs after the scanthe default indexIVFFlatlists, then probesbuild it after the loadtune ivfflat.probesfaster build, less recallIterative scan, added in 0.8.0the scan continues when the filter drops rows: strict_order keeps exact distance orderrelaxed_order trades order for recall; bounded by hnsw.max_scan_tuples, default 20,000
A szűrő csak a közelítő index munkája után fut, ezért vitatkozik egymással a szelektív WHERE és az approximatív index, amíg a szkennelésnek nem szabad továbbmennie.

A második részlet, hogy hol fut a WHERE feltétel. A közelítő index jelölteket állít elő, a szűrő pedig csak utána fut rajtuk, így a szelektivitás és a találati arány összefonódik: az alapértelmezett hnsw.ef_search 40-es értékénél és egy a sorok 10%-át eltaláló feltétnél körülbelül négy sor jön vissza. Semmi nem hibás, az index egyszerűen sosem látta az ejtett sorokat. Ez a viselkedés a leggyakoribb oka annak, hogy valaki kevesebb találatot jelez vissza, és erre létezik dokumentált megoldás, nem pedig kerülőút.

A szűrés itt nem utólagos gondolat, hanem elsőrendű probléma, és a dokumentáció négy lépést vesz végig abban a sorrendben, amelyben egy áttekintő kipróbálná őket. Hogy melyik érvényes, attól függ, mennyire szelektív a szűrő, mennyi találati arányra van valójában szükség, és hány különböző értéket vesz fel a szűrő: egy 50.000 értékes ügyfél-azonosító máshogy viselkedik, mint egy nyolc értékes országkód.

  • Először a szűrő oszlopára sima B-tree index kerüljön. Ha a feltétel a sorok kis hányadát éri el, ez pontos legközelebbi szomszédokat ad közelítő index érintése nélkül, és a README épp ezt nevezi kiindulópontnak.
  • Ha a szűrő széles marad, kapcsoljuk be az iteratív indexszkennelést a SET hnsw.iterative_scan = relaxed_order paranccsal: a gráf addig jár, amíg a LIMIT betelik, és strict_order akkor jön, amikor a távolsági sorrendnek pontosnak kell lennie.
  • Korlátozzuk a munkát. A hnsw.max_scan_tuples alapértelmezett értéke 20.000, a hnsw.scan_mem_multiplier pedig a work_mem egy szorzója, így a szelektív szűrő véges szkenneléssé romlik, nem végtelenné.
  • Sok különböző értéknél részleges index kell értékenként, vagy listaparticionálni kell a táblát. A README azt is megjegyzi, hogy az egy közelítő indexet megosztó ügyfelek egymás találati arányát is befolyásolják, ez tehát partitionálási, nem hangolási érv.

Első lépések

A teljes felület SQL, és ez az oka annak, hogy egy saját API-val dolgozó rendszer helyett ezt érdemes választani. Az alábbi kódrészlet egy éles tábla formája: rögzített dimenziójú oszlop, pontos index a szűrőre, közelítő index a vektorra, és az az egy beállítás, amely eldönti, hogy a szűrt lekérdezés röviden tér-e vissza.

CREATE EXTENSION IF NOT EXISTS vector;

-- The dimension is part of the type, so every row has to match it.
CREATE TABLE chunks (
  id         bigserial PRIMARY KEY,
  tenant_id  text       NOT NULL,
  embedding  vector(1536)
);

-- Exact index on the filter first: for a selective tenant it answers the
-- whole query and the approximate index is never consulted.
CREATE INDEX ON chunks (tenant_id);

-- Bulk load with COPY, then build the approximate index on top of the data.
CREATE INDEX ON chunks USING hnsw (embedding vector_cosine_ops)
  WITH (m = 16, ef_construction = 64);

-- A filter matching 10% of rows with ef_search = 40 returns about four rows,
-- so the scan has to be allowed to continue past its first pass.
SET hnsw.iterative_scan = relaxed_order;
SET hnsw.ef_search = 100;

SELECT id
FROM chunks
WHERE tenant_id = 'acme'
ORDER BY embedding <=> (SELECT embedding FROM chunks WHERE id = 42)
LIMIT 10;

Három döntés érdemes a védelmére az alábbi kódban. A dimenzió a típus része, így egy másik beágyazó modell által írt sor már az INSERT nél bukik, nem a lekérdezés idején. A közelítő index az adatok után épül, mert egy üres táblán létrehozott HNSW-gráfnak nincs mit összekötnie, és úgyis újraépítenék. A próbavektor pedig subselectből jön, abból a formából, amit a lekérdezéstervező elfogad; ugyanez a lekérdezés kifejezéssel az ORDER BY-ban figyelmeztetés nélkül szekvenciális szkennelésre esik vissza.

Teljesítmény

A memória az egész teljesítmény-történet. Egy vektoroszlop dimenciónként 4 bájtot és 8 bájtos fejlécet foglal, így 1536 dimenzió index nélkül körülbelül 6 KB soronként, és a AWS mérése szerint egy 100 millió 768 dimenziós vektorral dolgozó teljes pontosságú HNSW-index 367 GB, nagyjából 3,7 GB milliónként. A többi típus pontosan ezért a számért lett kitalálva.

TípusBájt dimenciónkéntIndexelhető korlátMit jár érte
vector42.000 dimaz alap; a rajta végzett pontos keresés pontos találati arányt ad
halfvec24.000 dimfeleakkora index, a AWS mérése szerint szinte nulla találati arány-veszteség
bit1/864.000 dimHamming a előjel-biteken, a találati arányhoz újrasorrendezés kell
sparsevec8 nem-nulla elemenként1.000 nem-nullaritka beágyazások, L2, koszinusz, belső szorzat és L1

A AWS a legtisztább nyilvános számokat közölte erről: VectorDBBench v0.3.4, top_k=100, Aurora PostgreSQL 18.4, pgvector 0.8.0. A 768 dimenziós LAION 100M esetén egy 128 GB-os r8g.4xlarge egy 367 GB-os, teljes pontosságú HNSW-indexet tárolt, amelyet nem tudott a gyorsítótárban tartani: 3,4 lekérdezés másodpercenként hidegen, 10 párhuzamos kapcsolat mellett, 3.336 melegen, 0,965 találati arány, 16,1 óra építés. Az újrasorrendező bináris kvantizálás 38 GB-ra és 1,1 órára vitte le az indexet, másodpercenként 13,5 hideg és 895 meleg lekérést ért el, és ezt a találati arány fizette meg: 0,931-re.

Ugyanez a mérés hordozza az ellenpéldát is, ezért a számait a módszertanukkal együtt kell olvasni. A Cohere 10M esetén, ahol a 768 dimenziós beágyazások a nulla közel csoportosulnak, a bináris kvantizálás 3000 jelölt újrasorrendezésével ért csak 0,93 találati arányt, és másodpercenként 16 lekérdezésre esett vissza 1640 ms p99 mellett, míg egy 384 GB-os gépen a teljes pontosságú HNSW 6930 lekérést adott 0,952 találati aránnyal. A kvantizálás eloszlásfüggő: validálni kell a saját beágyazásokon, vagy halfvec használni, és felezni az indexet találgatás helyett.

  • Az maintenance_work_mem értékét emelni kell HNSW-index építése előtt; a Postgres Notice-t ír, amikor a gráf már nem fér bele, és a README óva int attól, hogy a kiszolgáló memóriájáig emeljük.
  • Töltsünk COPY paranccsal, indexeljünk utána, és éles környezetben CREATE INDEX CONCURRENTLY használjunk, hogy az építés ne blokkolja az írásokat.
  • A HNSW-index VACUUM-ja eltarthat egy ideig; a dokumentált gyorsítás, hogy előbb REINDEX INDEX CONCURRENTLY, utána vacuum.
  • A vízszintes skálázást kölcsönözzük, nem építjük: a replikáció és a pillanatnyi visszaállítás a WAL-ból jön, a shardoláshoz pedig a README a Citusra, a PgDogra vagy a listaparticionálásra mutat.

Ahol megakad

A gyengeségek strukturálisak, nem befejezetlenek. Minden egy Postgres-csomóponton fut, így az index, a heap és a gyorsítótár ugyanazért a memóriáért verseng, és amelyik index nem fér bele, az előbb I/O-probléma lesz, csak utána találati arány-probléma. A közelítő keresés és a szelektív szűrők iteratív szkenneléssel is vitáznak, mert a véges szkennelés véges marad. A vacuum és az indexkarbantartás ennek az adatbázisnak a munkája, nem valaki másé. Beépített shardolás pedig nincs: a vízszintes skálázás replikákat, particionálást vagy egy bővítményt jelent.

AlternatívaÜzemeltetési formaÜzemeltetési teherHol jobb
QdrantKülön Rust-szerver vagy a gyártó felhőjeMég egy kluster javításra, mentésre, biztonságraPayload-szűrés és kvantizálás nagy volumenű találati arányra hangolva
WeaviateKülön szerver GraphQL API-val vagy a gyártó felhőjeUgyanez még egyszer, plusz saját modulkonfigurációHibrid keresés és vektorosítás egy helyen konfigurálva
ChromaBeágyazva a folyamatba vagy kis önálló szerverkéntSzinte semmi, de Postgres sincsA legrövidebb út a prototípustól a futó rendszerig

A őszinte határ: a pgvector addig nyer, amíg a vektorok a csapat amúgy is tárolt adatainak egy oszlopa, és akkor veszít, amikor egy lekérdezésnek ugyanabban a memóriában kell tartania egy nagy gráfot, egy szűrt szkennelést és az alkalmazás munkakészletének többi részét. A AWS ezt a határt 100 millió vektornál 367 GB indexben mérte meg, és kvantizálással, valamint particionálással kerülte meg. Akik ezt a cserét nem akarják magukra vállalni, négy kijáratuk van: halfvec, újrasorrendezéssel együtt futó bináris kvantizálás, ügyfél szerinti particionálás vagy egy külön rendszer, az első kettő pedig elég olcsó ahhoz, hogy a negyedikről való beszélgetés előtt ki kell próbálni.

Ítélet

A pgvector legyen az alapértelmezett válasz arra, hová kerüljenek a beágyazások, minden olyan csapatnál, amely már futtat Postgrest, és egy külön vektordatázisnak magának kell megindokolnia, miért kerüljön sorra. A bővítménynek van egy szokatlan tulajdonsága: a hibái azok a hibák, amiket a csapat már ismer egy adatbázisból — memónynyomás, karbantartási ablakok, egy csomópont írási teljesítménye. Mást csak akkor válassz, ha a méret vagy a késleltetési cél megnevezhető.

  1. Válaszd a pgvector-t, ha a vektorok olyan sorokat írnak le, amelyeket a csapat amúgy is tárol, és az ügyfélszigetelés, a kaszkádos törlés vagy a forrástáblával való JOIN tranzakciónak kell lennie.
  2. Válaszd, ha az anyag legfeljebb néhány tízmillió vektorból áll, és a szűrő elég szelektív ahhoz, hogy a szűrőoszlopon lévő B-tree vigye a lekérdezés java részét.
  3. Válaszd, ha az alternatíva egy második éles rendszer: a bővítmény örökli a már meglévő mentést, replikációt, megfigyelést és hozzáférés-kezelést, és nem ad hozzá új üzemeltetnivalót.
  4. Ne válaszd, ha egyetlen lekérdezésnek több száz gigabájtos gráfot és az alkalmazás munkakészletét is a memóriában kell tartania, hacsak a halfvec vagy a bináris kvantizálás nincs már megmérve a valós beágyazásokon.
  5. Ne válaszd, ha az igény több csomóponton át tartó folyamatos írási teljesítmény, vagy alacsony késleltetésű szűrt keresés több százmillió vektoron; az particionálás vagy kifejezetten erre épített rendszer, és a halogatás később migrációt fizettet.
Nem minden beágyazó modell olyan vektorokat ad, amelyek jól kvantizálhatók. Döntés előtt a saját adatokon kell validálni. — AWS Database Blog, 2026. augusztus 18.

Források

  1. pgvector README: típusok, indexelés, szűrés és skálázás
  2. pgvector changelog, 0.1.0-tól 0.8.7-ig
  3. PostgreSQL hír: megjelent a pgvector 0.8.2 (CVE-2026-3172)
  4. AWS: pgvector skálázása bináris kvantizálással Aurora PostgreSQL-en
  5. pgvector licenc: a PostgreSQL licenc

Gyakori kérdések

Ingyenes a pgvector használata?

Igen. A PostgreSQL licenc alatt jelenik meg, díj, open-core szétválasztás és fizetős terv nélkül, és egyre több hosztolt Postgres-szolgáltatónál előre telepítve érkezik. A lényeg a verzió: az iteratív indexszkenneléshez 0.8.0 vagy újabb kell.

Mikor érdemes HNSW-t használni IVFFlat helyett?

A HNSW jobb sebesség-találati arányt ad, üres táblán is létrehozható, mert nincs edzési lépése, viszont lassabban épül és több memóriát igényel. Az IVFFlat gyorsabban épül, de előbb adat kell hozzá; a README szerint lists = rows / 1000 egymillió sorig, afölé sqrt(rows), a probes pedig sqrt(lists)-zel kezdve.

Működik a pgvector WHERE feltétellel?

Igen, de közelítő indexnél a szűrő az indexszkennelés után fut, így egy szelektív feltétel kevesebb sort adhat vissza, mint amennyit a LIMIT kér. A dokumentált megoldások: B-tree a szűrőoszlopon, iteratív indexszkennelés a hnsw.iterative_scan kapcsolóval, részleges index értékenként és listaparticionálás sok különböző értékhez.

Hány vektort kezel a pgvector?

Egyetlen táblát a PostgreSQL 32 TB-os relációkorlátja és az határoz meg, mennyi fér a memóriába; a AWS 367 GB indexet mért 100 millió 768 dimenziós vektornál, és milliárdos nagyságrenden particionálást javasol. Efölött a README replikákat, Citust, PgDogot vagy listaparticionálást ajánl beépített shard-réteg helyett.

Pont erre van szükséged?

Írj a projektedről vagy a pozícióról – szívesen hallok felőled.