pgvector: Transformați PostgreSQL într-o bază de date vectorială
În 2026, mantra comunității a devenit clară: „Folosește doar Postgres”. Snowflake a achiziționat Crunchy Data pentru 250 de milioane de dolari, a investit Databricks 1 miliard pe Neon, iar Supabase a ajuns la o evaluare de 5 miliarde. PostgreSQL nu și mai mult doar o bază de date relațională: cu pgvector, devine o bază de date vectorială complet, capabil să gestioneze înglobările, căutarea de similaritate și operațiunile AI fără adăugare un singur serviciu pentru stiva dvs.
Dacă utilizați deja PostgreSQL (și din punct de vedere statistic sunteți), nu aveți nevoie de Pinecone, Qdrant sau Weaviate pentru a începe cu căutarea vectorială. În acest articol vom construi un sistem complet de căutare vectorială pornind de la zero: de la instalarea pgvector la crearea de indici optimizați, prin generarea de embeddings și integrare cu LangChain.
Prezentare generală a seriei
| # | Articol | Concentrează-te |
|---|---|---|
| 1 | Sunteți aici - pgvector | Instalare, operatori, indexare |
| 2 | Înglobări în profunzime | Modele, bucăți, dimensiuni |
| 3 | RAG cu PostgreSQL | Conductă RAG de la capăt la capăt |
| 4 | Căutare hibridă | Vectori + căutare full-text |
| 5 | Scalare pgvector | Partiționare, sharding, performanță |
| 6 | pgvector în producție | Monitorizare, backup, CI/CD |
Ce vei învăța
- Ce este pgvector și de ce este cea mai importantă extensie PostgreSQL din 2026
- Cum se instalează pgvector pe Linux, Docker și servicii gestionate (Supabase, Neon, AWS RDS)
- Cum se creează tabele cu coloane vectoriale și se inserează înglobări
- Cei trei operatori la distanță: Cosinus, L2 și Inner Product
- Cum să rulați interogări eficiente de căutare de similaritate
- Cum se generează înglobări cu Python (OpenAI și Sentence Transformers)
- HNSW vs IVFFlat: ce index să alegeți și cum să îl configurați
- Benchmark pgvector vs baze de date vectoriale dedicate
- Un exemplu complet de la capăt la capăt cu Python
- Integrare cu LangChain pentru sistemele RAG
1. Tendința „Folosește doar Postgres” în 2026
De ani de zile, lumea AI a făcut eforturi către baze de date vectoriale dedicate: Pinecone, Weaviate, Qdrant, Milvus. Fiecare proiect RAG părea să necesite un nou serviciu de administrat, monitorizat și plătit. Dar în 2025-2026 ceva s-a schimbat radical.
Comunitatea a înțeles că i vectorii nu sunt un tip de bază de date, dar a tip de date. La fel cum PostgreSQL gestionează JSON, matrice, geometrii și texte cu căutarea full-text, poate gestiona și vectori cu dimensiuni mari. Extensia care face toate acestea posibile se numește pgvector.
de ce „Folosește doar Postgres” în 2026
| Factor | Baza de date vectorială dedicată | PostgreSQL + pgvector |
|---|---|---|
| Infrastructură | Serviciu nou de gestionat | Extindere în DB existent |
| Cost | Plan separat (Pinecone de la 70 USD/lună) | Inclus în costul PostgreSQL |
| Consecvența datelor | Sincronizarea între DB este necesară | Tranzacții native ACID |
| Interogări | API proprietar | SQL standard + JOIN, WHERE, GROUP BY |
| Backup-uri | Sistem separat | pg_dump include totul |
| Echipă | Sunt necesare abilități specifice | Oricine cunoaște SQL |
Avantajul cheie este simplitatea operațională. Cu pgvector, înglobările dvs ele locuiesc în același tabel cu datele dumneavoastră relaționale. Vă puteți ALĂTURA vectorilor și metadatelor, utilizați clauza WHERE pentru filtrare și aveți consistență tranzacțională. Fără servicii externe de la sincronizare, nu există API-uri diferite de învățat.
2. Ce este pgvector
pgvector și o extensie open-source pentru PostgreSQL care adaugă suport nativ pentru vectori de dimensiuni mari și căutare de similaritate. Creat de Andrew Kane în 2021 și a devenit rapid cea mai descărcată extensie PostgreSQL pentru cazurile de utilizare AI/ML.
Cu pgvector puteți:
- Salvați vectori (înglobări) ca tip de date nativ în coloana unui tabel
- Efectuați căutări de similitudine folosind trei valori de distanță diferite
- Creați indecși specializați (HNSW și IVFFlat) pentru căutări rapide
- Combină căutarea vectorială cu tot ceea ce oferă PostgreSQL: JOIN-uri, tranzacții, declanșatoare, vizualizări materializate
pgvector în Numbers (2026)
| Metric | Valoare |
|---|---|
| Versiunea curentă | 0.8.0+ (cu scanări iterative ale indexului) |
| Max. dimensiunea transportorului | 16.000 |
| Tipuri de index | HNSW, IVFFlat |
| Tipuri de vectori | vector, halfvec, sparsevec, bit |
| Valorile distanței | Cosin, L2 (euclidian), produs interior |
| Sprijinit de | AWS RDS, Supabase, Neon, Google Cloud SQL, Azure |
| Licenţă | Licență PostgreSQL (sursă deschisă) |
Versiunea 0.8.0, lansată în 2025, a introdus o caracteristică cheie: gli scanare iterativă a indexului. Această caracteristică rezolvă problema de suprafiltrare, adică atunci când un indice vectorial nu returnează suficiente rezultate deoarece majoritatea este eliminată de filtrele WHERE. Cu scanări iterative, pgvector continuă să exploreze indexul până când veți găsi suficiente rezultate care se potrivesc cu filtrele, îmbunătățind drastic calitatea interogărilor filtrate.
3. Instalarea pgvector
pgvector se instalează în moduri diferite, în funcție de mediul dvs. Să ne uităm la cele trei abordări principalele: instalare nativă pe Linux, Docker și servicii gestionate.
3.1 Instalare pe Ubuntu/Debian
# Prerequisiti: PostgreSQL 14+ già installato
sudo apt-get update
sudo apt-get install -y postgresql-server-dev-16
# Clona e compila pgvector
git clone --branch v0.8.0 https://github.com/pgvector/pgvector.git
cd pgvector
make
sudo make install
# Abilita l'estensione nel tuo database
sudo -u postgres psql -d il_tuo_database -c "CREATE EXTENSION vector;"
Pentru PostgreSQL 17, înlocuiți postgresql-server-dev-16 cu
postgresql-server-dev-17. pgvector acceptă PostgreSQL începând cu versiunea 13
la ora 17.
3.2 Instalare cu Docker
Cel mai rapid mod de a începe. Imaginea oficiala pgvector/pgvector
include PostgreSQL cu pgvector preinstalat.
# docker-compose.yml
version: '3.8'
services:
postgres:
image: pgvector/pgvector:pg17
environment:
POSTGRES_DB: vectordb
POSTGRES_USER: admin
POSTGRES_PASSWORD: secret_password
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
volumes:
pgdata:
-- init.sql: eseguito automaticamente al primo avvio
CREATE EXTENSION IF NOT EXISTS vector;
# Avvio rapido senza docker-compose
docker run -d \
--name pgvector-db \
-e POSTGRES_DB=vectordb \
-e POSTGRES_PASSWORD=secret \
-p 5432:5432 \
pgvector/pgvector:pg17
# Connetti e abilita pgvector
docker exec -it pgvector-db psql -U postgres -d vectordb \
-c "CREATE EXTENSION vector;"
3.3 Servicii gestionate (Zero Setup)
Dacă nu doriți să gestionați infrastructura, mai multe servicii cloud oferă PostgreSQL cu pgvector deja activat. Aceasta este alegerea ideală pentru producție.
Servicii gestionate cu pgvector
| Serviciu | pgvector Inclus | Plan gratuit | Note |
|---|---|---|---|
| Superbază | Preinstalat | Da (500 MB) | Interfața de utilizare a tabloului de bord, API-uri REST automate |
| Neon | Preinstalat | Da (0,5 GB) | Baze de date fără server, sucursale |
| AWS RDS | Pentru a fi activat | No | pgvector 0.8.0 pe Aurora PostgreSQL |
| Google Cloud SQL | Pentru a fi activat | No | suport pgvector 0.8.0 |
| Baza de date Azure | Pentru a fi activat | No | Server flexibil cu pgvector |
-- Su Supabase, pgvector e già disponibile.
-- Basta abilitare l'estensione:
CREATE EXTENSION IF NOT EXISTS vector;
-- Verifica l'installazione
SELECT extversion FROM pg_extension WHERE extname = 'vector';
-- Risultato: 0.8.0
4. Creați un tabel cu vectori
Cu pgvector instalat, puteți adăuga coloane de tip vector(n) la a ta
mese, unde n și dimensionalitatea vectorului. Valoarea de n
Depinde de modelul de încorporare pe care îl utilizați: OpenAI text-embedding-3-small produce
1536 de vectori dimensionali, în timp ce text-embedding-3-large merge până la 3072.
-- Abilita l'estensione (se non già fatto)
CREATE EXTENSION IF NOT EXISTS vector;
-- Tabella documenti per un sistema RAG
CREATE TABLE documents (
id BIGSERIAL PRIMARY KEY,
title VARCHAR(500) NOT NULL,
content TEXT NOT NULL,
source VARCHAR(255),
category VARCHAR(100),
embedding vector(1536),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Aggiungi un commento alla colonna per documentazione
COMMENT ON COLUMN documents.embedding IS
'Embedding generato con OpenAI text-embedding-3-small (1536 dim)';
Inserați documente cu încorporare
Inserarea funcționează ca un INSERT normal, trecând vectorul ca șir de valori separate prin virgulă între paranteze drepte.
-- Inserimento singolo
INSERT INTO documents (title, content, source, embedding)
VALUES (
'Introduzione a PostgreSQL',
'PostgreSQL e un database relazionale open-source potente e affidabile...',
'docs/postgres-intro.md',
'[0.023, -0.045, 0.067, 0.089, -0.012, ...]' -- vettore a 1536 dimensioni
);
-- Inserimento multiplo (batch)
INSERT INTO documents (title, content, source, category, embedding)
VALUES
('Capitolo 1', 'Contenuto cap 1...', 'libro.pdf', 'tutorial',
'[0.11, -0.22, 0.33, ...]'),
('Capitolo 2', 'Contenuto cap 2...', 'libro.pdf', 'tutorial',
'[0.44, -0.55, 0.66, ...]'),
('FAQ', 'Domande frequenti...', 'faq.md', 'supporto',
'[-0.12, 0.34, -0.56, ...]');
Dimensiunea Vectorului
Dimensiunea specificată în definiția coloanei (vector(1536))
trebuie să se potrivească exact cu dimensiunea înglobărilor pe care le introduceți. O încercare
pentru a insera un vector cu 768 dimensiuni într-o coloană vector(1536) va cauza
o eroare. Alegeți dimensiunea în funcție de șablonul de încorporare pe care intenționați să îl utilizați.
Dimensiunile modelelor de încorporare populare
| Model | Furnizorii | Dimensiuni | Cost / 1 M token |
|---|---|---|---|
| text-incorporare-3-mic | OpenAI | 1536 | 0,02 USD |
| text-incorporare-3-mare | OpenAI | 3072 | 0,13 USD |
| toate-MiniLM-L6-v2 | HuggingFace (gratuit) | 384 | Gratuit (local) |
| all-mpnet-base-v2 | HuggingFace (gratuit) | 768 | Gratuit (local) |
| călătorie-3 | Voyage AI | 1024 | 0,06 USD |
5. Operatori la distanță
pgvector oferă trei operatori pentru a calcula distanța dintre vectori. Fiecare operator corespunde unei metrici matematice diferite și are cazuri de utilizare specifice.
Cei trei operatori pgvector
| Operator | Metric | Formula | Triere | Caz de utilizare |
|---|---|---|---|---|
<=> |
Distanța Cosinus | 1 - cosinus_similaritate | ASC (0 = identic) | Text, încorporare normalizate |
<-> |
L2 (euclidian) | sqrt(sumă((a-b)^2)) | ASC (0 = identic) | Imagini, date spațiale |
<#> |
Produs interior (refuzat) | -1 * produs_punct | ASC (mai negativ = mai asemănător) | Vectori normalizați, performanță maximă |
-- COSINE DISTANCE <=> (il più usato per testo)
-- Risultato: 0 = identici, 2 = opposti
SELECT title, content,
embedding <=> '[0.1, 0.2, ...]' AS cosine_distance
FROM documents
ORDER BY embedding <=> '[0.1, 0.2, ...]' ASC
LIMIT 5;
-- L2 (EUCLIDEA) <->
-- Risultato: 0 = identici, valori crescenti = più lontani
SELECT title, content,
embedding <-> '[0.1, 0.2, ...]' AS l2_distance
FROM documents
ORDER BY embedding <-> '[0.1, 0.2, ...]' ASC
LIMIT 5;
-- INNER PRODUCT (NEGATO) <#>
-- Risultato: valori più negativi = più simili
SELECT title, content,
(embedding <#> '[0.1, 0.2, ...]') * -1 AS similarity
FROM documents
ORDER BY embedding <#> '[0.1, 0.2, ...]' ASC
LIMIT 5;
Ce operator să alegeți
Pentru 90% din cazurile de utilizare cu text și AI, utilizați <=> (distanta cosinus).
Încorporarea modelelor precum OpenAI produce vectori normalizați, ceea ce face
distanța cosinusului și produsul interior echivalent din punct de vedere al rezultatului. STATELE UNITE ALE AMERICII <->
(L2) numai atunci când lucrați cu date spațiale sau imagini unde amploarea vectorului contează.
Produsul interior (<#>) și puțin mai rapid decât cosinusul pentru că evită
normalizare, dar numai pe vectori deja normalizați.
6. Interogări de căutare similare
Căutarea de similaritate este inima pgvector. Interogarea de bază este simplă: sortați rezultatele prin distanța față de vectorul de interogare și obțineți primul K. Dar în producție, interogările devin mai sofisticat prin combinarea căutării vectoriale cu filtre SQL.
6.1 Interogări de bază
-- Trova i 5 documenti più simili a un vettore query
SELECT
id,
title,
content,
1 - (embedding <=> $1) AS similarity -- $1 = vettore query
FROM documents
ORDER BY embedding <=> $1
LIMIT 5;
6.2 Interogări cu filtre (puterea reală a pgvector)
Aici pgvector strălucește în comparație cu bazele de date vectoriale dedicate. Puteți combina căutarea vector cu orice clauză SQL: WHERE, JOIN, GROUP BY, subinterogare.
-- Filtra per categoria e soglia di similarità
SELECT
id, title, content,
1 - (embedding <=> $1) AS similarity
FROM documents
WHERE category = 'tutorial'
AND created_at > NOW() - INTERVAL '30 days'
ORDER BY embedding <=> $1
LIMIT 5;
-- JOIN con altre tabelle: trova documenti simili con info autore
SELECT
d.title,
d.content,
a.name AS author_name,
1 - (d.embedding <=> $1) AS similarity
FROM documents d
JOIN authors a ON d.author_id = a.id
WHERE a.verified = true
ORDER BY d.embedding <=> $1
LIMIT 10;
-- Esclusione: trova simili ma NON il documento stesso
SELECT
id, title,
1 - (embedding <=> (SELECT embedding FROM documents WHERE id = 42)) AS similarity
FROM documents
WHERE id != 42
ORDER BY embedding <=> (SELECT embedding FROM documents WHERE id = 42)
LIMIT 5;
6.3 Scanări iterative ale indexului (pgvector 0.8.0)
Una dintre cele mai importante caracteristici noi ale versiunii 0.8.0 este suportul pentru scanare iterativă a indexului. Când combinați căutarea vectorială cu filtrele WHERE restrictiv, este posibil ca indicele să nu găsească suficiente rezultate în prima trecere. Cu scanări iterative, pgvector continuă să scaneze indexul până când este satisfăcut limita solicitată.
-- Abilita iterative scan per HNSW
SET hnsw.iterative_scan = relaxed_order; -- o strict_order
-- Imposta il limite massimo di tuple da scansionare
SET hnsw.max_scan_tuples = 20000;
-- Ora le query con filtri restrittivi funzionano correttamente
SELECT id, title,
1 - (embedding <=> $1) AS similarity
FROM documents
WHERE category = 'raro' -- categoria con pochi documenti
ORDER BY embedding <=> $1
LIMIT 10;
-- Senza iterative scan: potrebbe restituire 0-3 risultati
-- Con iterative scan: restituisce sempre fino a 10 risultati
-- Modalità disponibili:
-- off = comportamento tradizionale (default)
-- strict_order = mantiene l'ordine esatto delle distanze
-- relaxed_order = ordine approssimato (migliori performance)
Când să utilizați scanările iterative
- relaxed_order: în majoritatea cazurilor, oferă cel mai bun echilibru între performanță și calitate
- strict_order: când ordinea exactă a distanțelor este critică (de exemplu, clasarea rezultatelor pentru utilizator)
- oprit: când nu ai filtre WHERE sau când filtrele nu sunt restrictive
7. Generați încorporații cu Python
Pentru a introduce date în pgvector, trebuie mai întâi să transformați textul în vectori numerici folosind un model de încorporare. Să vedem două abordări: API-ul OpenAI (plătit, de înaltă calitate) și Sentence Transformers (gratuit, local).
7.1 Cu API-ul OpenAI
import openai
import psycopg2
from psycopg2.extras import execute_values
# Configura il client OpenAI
client = openai.OpenAI(api_key="sk-...")
def get_embedding(text: str, model: str = "text-embedding-3-small") -> list[float]:
"""Genera un embedding per un testo usando OpenAI."""
response = client.embeddings.create(
input=text,
model=model
)
return response.data[0].embedding # lista di 1536 float
# Connetti a PostgreSQL
conn = psycopg2.connect(
host="localhost",
database="vectordb",
user="admin",
password="secret_password"
)
cur = conn.cursor()
# Genera e inserisci embeddings
documents = [
("Introduzione a Python", "Python e un linguaggio di programmazione versatile..."),
("PostgreSQL Avanzato", "Le CTE ricorsive permettono di eseguire query gerarchiche..."),
("Docker per Sviluppatori", "I container Docker isolano le applicazioni..."),
]
for title, content in documents:
embedding = get_embedding(content)
cur.execute(
"INSERT INTO documents (title, content, embedding) VALUES (%s, %s, %s)",
(title, content, str(embedding))
)
conn.commit()
cur.close()
conn.close()
7.2 Cu transformatoare de propoziții (gratuite și locale)
from sentence_transformers import SentenceTransformer
import psycopg2
import numpy as np
# Carica il modello (scaricato automaticamente la prima volta)
model = SentenceTransformer('all-MiniLM-L6-v2') # 384 dimensioni
# Genera embeddings per un batch di testi
texts = [
"Python e un linguaggio di programmazione versatile",
"Le CTE ricorsive permettono query gerarchiche in SQL",
"I container Docker isolano le applicazioni",
]
embeddings = model.encode(texts) # shape: (3, 384)
# Connetti e inserisci
conn = psycopg2.connect(
host="localhost", database="vectordb",
user="admin", password="secret_password"
)
cur = conn.cursor()
# NOTA: la colonna deve essere vector(384), non vector(1536)
for text, embedding in zip(texts, embeddings):
cur.execute(
"INSERT INTO documents (title, content, embedding) VALUES (%s, %s, %s)",
(text[:100], text, str(embedding.tolist()))
)
conn.commit()
cur.close()
conn.close()
OpenAI vs Sentence Transformers
| astept | OpenAI API | Transformatori de propoziții |
|---|---|---|
| Cost | 0,02 USD / 1 milion de jetoane | Gratuit |
| calitate | Excelent (de ultimă generație) | Bun (suficient pentru multe cazuri) |
| Confidențialitate | Date trimise către OpenAI | Toate locale, nu ies date |
| Viteză | ~100 ms per solicitare (rețea) | ~10 ms pentru text (cu GPU) |
| Offline | Nu (necesită internet) | Da (după prima descărcare) |
| Dimensiuni | 1536 sau 3072 | 384 sau 768 (în funcție de model) |
Regula de aur a înglobărilor
Folosește-l același model de încorporare atât pentru indexarea documentelor cât şi
pentru a genera vectorul de interogare. Înglobările generate cu modele diferite locuiesc
spații vectoriale diferite și nu sunt comparabile. Dacă indexezi cu
text-embedding-3-small, trebuie sa cauti cu acelasi model.
8. Indexare: HNSW vs IVFFlat
Fără un index, pgvector efectuează o comparație de scanare secvențială (forță brută). vectorul de interogare cu fiecare rând al tabelului. Acest lucru este 100% precis, dar devine lent cu seturi mari de date. Sacrificiul indicilor aproximativi al celui mai apropiat vecin (ANN). un procent minim de precizie pentru a obține ordine de mărime căutări mai rapide.
8.1 Indicele HNSW
HNSW (Hierarhical Navigable Small World) construiește un grafic cu mai multe niveluri de conexiuni între transportatori. Și indicele recomandat pentru majoritatea cazurilor: citire rapidă, precizie bună și acceptă inserții incrementale fără reconstrucție.
-- Indice HNSW con parametri di default
CREATE INDEX idx_documents_embedding_hnsw
ON documents
USING hnsw (embedding vector_cosine_ops);
-- Indice HNSW con parametri personalizzati
CREATE INDEX idx_documents_embedding_hnsw
ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (
m = 24, -- connessioni per nodo (default: 16, range: 2-100)
ef_construction = 128 -- candidati durante costruzione (default: 64, range: 4-1000)
);
-- Regola ef_search per le query (default: 40)
SET hnsw.ef_search = 100; -- più alto = più preciso ma più lento
Parametri HNSW: Ghid de configurare
| Parametru | Implicit | Gamă | Efect | Recomanda |
|---|---|---|---|---|
m |
16 | 2 - 100 | Mai multe conexiuni = mai precisă, mai multă memorie | 16-32 pentru majoritatea cazurilor |
ef_construction |
64 | 4 - 1000 | Mai mare = indice mai bun, construcție mai lentă | 64-256 (o singură dată, merită majorat) |
ef_search |
40 | 1 - 1000 | Mai mare = căutare mai precisă, mai lentă | 40-200 pe baza compromisului dorit |
8.2 Indexul IVFFlat
IVFFlat (Fișier inversat cu compresie plată) împarte vectorii în grupuri și numai căutări în clusterele cele mai apropiate de interogare. Este mai rapid de construit decât HNSW, dar necesită reconstrucție atunci când adăugați o mulțime de date noi. Ideal pentru schimbarea seturilor de date rar.
-- IMPORTANTE: IVFFlat richiede dati nella tabella PRIMA di creare l'indice
-- (necessità di fare clustering k-means sui dati esistenti)
-- Indice IVFFlat
CREATE INDEX idx_documents_embedding_ivf
ON documents
USING ivfflat (embedding vector_cosine_ops)
WITH (
lists = 100 -- numero di cluster (default: 100)
);
-- Regola il numero di cluster da esplorare durante la ricerca
SET ivfflat.probes = 10; -- default: 1, consigliato: sqrt(lists)
-- Regola pratica per "lists":
-- rows < 1M: lists = rows / 1000
-- rows >= 1M: lists = sqrt(rows)
8.3 Operatori după tipul de distanță
Operatorul specificat în crearea indexului trebuie să se potrivească cu operatorul folosit în interogări. Iată harta completă.
Operatori de hărți și clase de indexare
| Distanţă | operator SQL | Clasa de indexare (hopa) |
|---|---|---|
| Lucruri mărunte | <=> |
vector_cosine_ops |
| L2 (euclidian) | <-> |
vector_l2_ops |
| Produs interior | <#> |
vector_ip_ops |
8.4 HNSW vs IVFFlat: pe care să alegeți
Comparație HNSW vs IVFFlat
| astept | HNSW | IVFFlat |
|---|---|---|
| Viteza de căutare | Mai repede | Rapid |
| Precizie (rechemare) | ~95-99% | ~90-98% |
| Construiește timp | Lent | Rapid |
| Memorie | Ridicat | Medie |
| Inserții incrementale | Sprijinit | Acestea necesită REINDEX |
| Scanare iterativă | Da (0.8.0) | Da (0.8.0) |
| Caz de utilizare ideal | Utilizare generală, date în creștere | Seturi de date statice, construcție rapidă |
Sfaturi practice
Pentru majoritatea proiectelor, alege HNSW. Și mai versatil, suportă inserții incrementale fără reconstrucție, iar versiunea 0.8.0 a pgvector are și-a îmbunătățit semnificativ performanța. Utilizați IVFFlat numai dacă timpul de construcția indexului și critic (seturi de date foarte mari încărcate o singură dată) o dacă aveți constrângeri stricte de memorie.
9. Performanță: pgvector vs baze de date vectoriale dedicate
Cea mai frecventă întrebare este: „este pgvector suficient de rapid pentru producție?”. Răspunsul în 2026 și un clar si, pentru marea majoritate a cazurilor de utilizare. Să vedem cifrele.
9.1 Benchmark-uri la scară largă
Benchmark-urile pgvectorscale (extensie complementară pgvector dezvoltată de Timescale) din 50 de milioane de operatori de transport au rezultate impresionante:
Benchmark: 50M Vectori la 99% Recall
| Sistem | QPS (Interogări/sec) | Amintiți-vă | Note |
|---|---|---|---|
| pgvectorscale | 471 | 99% | De 11,4 ori mai rapid decât Qdrant |
| Qdrant | 41 | 99% | Optimizat pentru căutare pur vector |
9.2 Benchmark la scară medie (1 milion de vectori)
Benchmark: 1M Vectori - Debit
| Sistem | Interogări/sec | Inserții/sec | Cost gestionat |
|---|---|---|---|
| Cone de pin | ~5.000 | ~50.000 | De la 70 USD/lună |
| Qdrant | ~4.500 | ~45.000 | Auto-găzduit sau cloud |
| pgvector | ~3.000 | ~30.000 | Inclus în costul PG |
La 1M vectori, pgvector este puțin mai lent în debitul pur. Dar având în vedere că costul este practic zero (și deja inclus în PostgreSQL), raportul calitate-pret si imbatabil. Pentru majoritatea aplicațiilor web, 3.000 de interogări pe secundă sunt mai mult decât suficiente.
Când pgvector NU ESTE SUFICIENT
Luați în considerare o bază de date vectorială dedicată (Qdrant, Pinecone) atunci când:
- Ai peste 50 de milioane de vectori
- Aveți nevoie de latențe p99 sub i 5 ms
- Sarcina ta de lucru este exclusiv căutare vectorială (fără date relaționale)
- Aveți nevoie de fragmentare distribuită nativă
Pentru orice altceva, pgvector este alegerea potrivită. Un sistem sub 10 milioane de vectori cu PostgreSQL deja în stivă, nu are nevoie de nicio complexitate suplimentară.
10. Exemplu complet: Baza de cunoștințe cu Python
Să punem totul împreună într-un exemplu practic. Vom construi o bază de cunoștințe care: indexează documentele cu încorporare, permite căutări de similaritate și acceptă filtre după categorie.
"""
Knowledge Base con PostgreSQL + pgvector
Requisiti: pip install openai psycopg2-binary
"""
import openai
import psycopg2
from dataclasses import dataclass
# === Configurazione ===
OPENAI_API_KEY = "sk-..."
DB_CONFIG = {
"host": "localhost",
"database": "vectordb",
"user": "admin",
"password": "secret_password",
}
EMBEDDING_MODEL = "text-embedding-3-small"
EMBEDDING_DIM = 1536
@dataclass(frozen=True)
class SearchResult:
id: int
title: str
content: str
similarity: float
category: str
# === Funzioni di embedding ===
client = openai.OpenAI(api_key=OPENAI_API_KEY)
def get_embedding(text: str) -> list[float]:
"""Genera un embedding per un testo."""
response = client.embeddings.create(
input=text,
model=EMBEDDING_MODEL,
)
return response.data[0].embedding
def get_embeddings_batch(texts: list[str]) -> list[list[float]]:
"""Genera embeddings per un batch di testi (più efficiente)."""
response = client.embeddings.create(
input=texts,
model=EMBEDDING_MODEL,
)
return [item.embedding for item in response.data]
# === Setup del database ===
def setup_database() -> None:
"""Crea la tabella e gli indici."""
conn = psycopg2.connect(**DB_CONFIG)
cur = conn.cursor()
cur.execute("CREATE EXTENSION IF NOT EXISTS vector;")
cur.execute(f"""
CREATE TABLE IF NOT EXISTS kb_documents (
id BIGSERIAL PRIMARY KEY,
title VARCHAR(500) NOT NULL,
content TEXT NOT NULL,
category VARCHAR(100) DEFAULT 'general',
embedding vector({EMBEDDING_DIM}),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
""")
cur.execute("""
CREATE INDEX IF NOT EXISTS idx_kb_embedding_hnsw
ON kb_documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 24, ef_construction = 128);
""")
cur.execute("""
CREATE INDEX IF NOT EXISTS idx_kb_category
ON kb_documents (category);
""")
conn.commit()
cur.close()
conn.close()
print("Database setup completato.")
# === Inserimento documenti ===
def insert_documents(
documents: list[dict],
batch_size: int = 50,
) -> None:
"""Inserisce documenti con embeddings generati automaticamente."""
conn = psycopg2.connect(**DB_CONFIG)
cur = conn.cursor()
for i in range(0, len(documents), batch_size):
batch = documents[i : i + batch_size]
texts = [doc["content"] for doc in batch]
embeddings = get_embeddings_batch(texts)
for doc, emb in zip(batch, embeddings):
cur.execute(
"""INSERT INTO kb_documents (title, content, category, embedding)
VALUES (%s, %s, %s, %s)""",
(doc["title"], doc["content"],
doc.get("category", "general"), str(emb)),
)
print(f"Inseriti {min(i + batch_size, len(documents))}/{len(documents)} documenti")
conn.commit()
cur.close()
conn.close()
# === Ricerca per similarità ===
def search(
query: str,
limit: int = 5,
category: str | None = None,
min_similarity: float = 0.3,
) -> list[SearchResult]:
"""Cerca documenti simili alla query."""
query_embedding = get_embedding(query)
conn = psycopg2.connect(**DB_CONFIG)
cur = conn.cursor()
# Abilita iterative scan per filtri restrittivi
cur.execute("SET hnsw.iterative_scan = relaxed_order;")
if category:
cur.execute(
"""SELECT id, title, content, category,
1 - (embedding <=> %s::vector) AS similarity
FROM kb_documents
WHERE category = %s
ORDER BY embedding <=> %s::vector
LIMIT %s""",
(str(query_embedding), category,
str(query_embedding), limit),
)
else:
cur.execute(
"""SELECT id, title, content, category,
1 - (embedding <=> %s::vector) AS similarity
FROM kb_documents
ORDER BY embedding <=> %s::vector
LIMIT %s""",
(str(query_embedding), str(query_embedding), limit),
)
results = [
SearchResult(
id=row[0], title=row[1], content=row[2],
category=row[3], similarity=row[4],
)
for row in cur.fetchall()
if row[4] >= min_similarity
]
cur.close()
conn.close()
return results
# === Main ===
if __name__ == "__main__":
# 1. Setup
setup_database()
# 2. Inserisci documenti di esempio
sample_docs = [
{
"title": "Introduzione a PostgreSQL",
"content": "PostgreSQL e un RDBMS open-source con supporto ACID completo.",
"category": "database",
},
{
"title": "Indici in PostgreSQL",
"content": "I B-tree sono il tipo di indice default. HNSW e usato per vettori.",
"category": "database",
},
{
"title": "Cos'è il RAG",
"content": "RAG combina retrieval e generation per risposte basate su documenti.",
"category": "ai",
},
{
"title": "Docker per Sviluppatori",
"content": "Docker isola le applicazioni in container leggeri e portabili.",
"category": "devops",
},
{
"title": "Embeddings e Vector Search",
"content": "Gli embeddings trasformano testo in vettori per la similarity search.",
"category": "ai",
},
]
insert_documents(sample_docs)
# 3. Cerca documenti
print("\n--- Ricerca: 'come funzionano i database vettoriali' ---")
results = search("come funzionano i database vettoriali", limit=3)
for r in results:
print(f" [{r.similarity:.3f}] {r.title} ({r.category})")
print("\n--- Ricerca filtrata per categoria 'ai' ---")
results = search("tutorial su PostgreSQL", limit=3, category="ai")
for r in results:
print(f" [{r.similarity:.3f}] {r.title} ({r.category})")
11. Integrare cu LangChain
Dacă construiți un sistem RAG, LangChain oferă integrare directă cu pgvector
prin pachet langchain-postgres. Acest lucru vă permite să utilizați
PostgreSQL ca magazin de vectori fără a scrie manual SQL.
pip install langchain-postgres langchain-openai psycopg
from langchain_postgres import PGVector
from langchain_openai import OpenAIEmbeddings
from langchain_core.documents import Document
# Configura il modello di embedding
embeddings = OpenAIEmbeddings(
model="text-embedding-3-small",
openai_api_key="sk-...",
)
# NOTA: langchain-postgres usa psycopg3 (non psycopg2)
CONNECTION_STRING = "postgresql+psycopg://admin:secret@localhost:5432/vectordb"
# Crea il vector store
vector_store = PGVector(
embeddings=embeddings,
collection_name="langchain_docs",
connection=CONNECTION_STRING,
use_jsonb=True, # metadati JSONB per filtri efficienti
)
# Aggiungi documenti
docs = [
Document(
page_content="pgvector trasforma PostgreSQL in un vector database",
metadata={"source": "tutorial.md", "chapter": 1},
),
Document(
page_content="HNSW e l'indice consigliato per la maggior parte dei casi",
metadata={"source": "tutorial.md", "chapter": 2},
),
Document(
page_content="LangChain semplifica la costruzione di sistemi RAG",
metadata={"source": "guida-rag.md", "chapter": 1},
),
]
vector_store.add_documents(docs)
# Ricerca per similarità
results = vector_store.similarity_search(
"come creare un indice vettoriale",
k=3,
)
for doc in results:
print(f"[{doc.metadata['source']}] {doc.page_content[:80]}...")
# Ricerca con score
results_with_scores = vector_store.similarity_search_with_score(
"cos'è pgvector",
k=3,
)
for doc, score in results_with_scores:
print(f"[Score: {score:.4f}] {doc.page_content[:80]}...")
# Usa come retriever in una catena RAG
retriever = vector_store.as_retriever(
search_type="similarity",
search_kwargs={"k": 5},
)
# Il retriever può essere usato direttamente nelle chain LangChain
relevant_docs = retriever.invoke("installazione pgvector docker")
Notă despre psycopg3
Pachetul langchain-postgres cere psihopg3 (nu psychopg2).
Șirul de conexiune trebuie să folosească formatul postgresql+psycopg:// în loc de postgresql+psycopg2://. Dacă migrați din langchain-community,
actualizați șirul de conexiune.
12. Cele mai bune practici pentru pgvector în producție
12.1 Dimensiunea Vectorului
Vectorii mai mici ocupă mai puțin spațiu, fac indici mai rapidi și reduc costurile.
OpenAI text-embedding-3-small (1536 dim) oferă cea mai bună valoare
calitate-dimensiune pentru majoritatea cazurilor. Dacă poți tolera o ușoară pierdere
de calitate, all-MiniLM-L6-v2 (384 dim) și o alegere liberă excelentă.
12.2 Inserare lot
from psycopg2.extras import execute_values
# SBAGLIATO: un INSERT alla volta (lento)
for doc in documents:
cur.execute("INSERT INTO docs (content, embedding) VALUES (%s, %s)",
(doc.content, str(doc.embedding)))
# CORRETTO: batch insert con execute_values (10-50x più veloce)
data = [
(doc.content, str(doc.embedding))
for doc in documents
]
execute_values(
cur,
"INSERT INTO docs (content, embedding) VALUES %s",
data,
page_size=500, # righe per batch SQL
)
conn.commit()
12.3 Pooling de conexiuni
În producție, nu deschideți o nouă conexiune PostgreSQL pentru fiecare solicitare.
Utilizați un pool de conexiuni precum PgBouncer sau punerea în comun integrată a
psycopg2.
from psycopg2 import pool
# Crea un pool di connessioni (all'avvio dell'applicazione)
connection_pool = pool.ThreadedConnectionPool(
minconn=5, # connessioni minime
maxconn=20, # connessioni massime
host="localhost",
database="vectordb",
user="admin",
password="secret_password",
)
# Usa una connessione dal pool
conn = connection_pool.getconn()
try:
cur = conn.cursor()
cur.execute("SELECT ... ORDER BY embedding <=> %s LIMIT 5", ...)
results = cur.fetchall()
cur.close()
finally:
connection_pool.putconn(conn) # restituisci al pool
12.4 Întreținere
-- VACUUM per recuperare spazio dopo DELETE/UPDATE
VACUUM ANALYZE documents;
-- Ricostruisci l'indice IVFFlat dopo inserimenti massicci
REINDEX INDEX CONCURRENTLY idx_documents_embedding_ivf;
-- NOTA: HNSW non richiede REINDEX per nuovi inserimenti
-- Monitora la dimensione dell'indice
SELECT
indexname,
pg_size_pretty(pg_relation_size(indexname::regclass)) AS size
FROM pg_indexes
WHERE tablename = 'documents';
-- Verifica che l'indice venga usato
EXPLAIN ANALYZE
SELECT id, 1 - (embedding <=> '[0.1, 0.2, ...]') AS similarity
FROM documents
ORDER BY embedding <=> '[0.1, 0.2, ...]'
LIMIT 5;
-- Cerca "Index Scan using idx_..._hnsw" nell'output
Lista de verificare a producției
- Creați un index HNSW cu
m = 24eef_construction = 128 - Taxa
ef_searchîntre 40 și 200 pe baza compromisului de latență/precizie - Abilitatea
hnsw.iterative_scan = relaxed_orderdacă folosești filtrele WHERE - Utilizați inserția de lot cu
execute_valuespentru sarcini masive - Configurați un pool de conexiuni (PgBouncer sau psycopg2.pool)
- Programa
VACUUM ANALYZEperiodic - Monitorizați interogările cu
EXPLAIN ANALYZEpentru a verifica utilizarea indexului - STATELE UNITE ALE AMERICII
halfvecîn loc devectorpentru a economisi 50% spațiu (dacă precizia float16 este suficientă)
Concluzii
pgvector a transformat PostgreSQL dintr-o simplă bază de date relațională într-o platformă pregătită pentru AI. Cu versiunea 0.8.0 și scanările sale iterative de index, decalajul cu bazele de date vectoriale dedicat s-a redus drastic, iar pentru majoritatea proiectelor sub 10 milioane de transportatori, nu există niciun motiv pentru a adăuga un serviciu separat la stiva dvs.
În acest articol ați învățat cum să instalați pgvector, să creați tabele cu coloane vectoriale, efectuați căutarea de similaritate cu trei operatori de distanță, generați înglobări cu Python, configurați indici HNSW și IVFFlat și integrați totul cu LangChain. Ați văzut și dumneavoastră repere care confirmă performanța pgvector în producție.
În articolul urmator vom aprofunda în ele înglobări: cum funcționează intern, cum să alegeți modelul potrivit, strategiile de fragmentare pentru documente lungi și cum să optimizați calitatea căutării. În al treilea articol vom construi unul Conducta RAG finalizată cu PostgreSQL ca singur backend.
Resurse suplimentare
- pgvector GitHub: github.com/pgvector/pgvector - Documentație oficială și jurnal de modificări
- pgvectorscale: github.com/timescale/pgvectorscale - Extensie de performanță îmbunătățită
- LangChain PGVector: python.langchain.com - Integrare oficială Python
- Vector de superbază: supabase.com/docs/guides/ai - ghid pgvector pe Supabase
- Neon AI: neon.tech/docs/ai - pgvector pe Neon serverless







