pgvector: Zamień PostgreSQL w wektorową bazę danych
W roku 2026 mantra społeczności stała się jasna: „Po prostu użyj Postgresa”. Snowflake przejął Crunchy Data za 250 milionów dolarów, zainwestował Databricks Neon kosztował 1 miliard, a Supabase osiągnął wycenę na poziomie 5 miliardów. PostgreSQL nie i więcej tylko relacyjna baza danych: with pgwektor, staje się wektorową bazą danych kompletny, zdolny do zarządzania osadzaniem, wyszukiwaniem podobieństw i operacjami AI bez dodawania pojedynczą usługę do Twojego stosu.
Jeśli już używasz PostgreSQL (a statystycznie tak jest), nie potrzebujesz Pinecone, Qdrant lub Weaviate, aby rozpocząć wyszukiwanie wektorowe. W tym artykule będziemy budować kompletny system wyszukiwania wektorów zaczynający od zera: od instalacji pgvector do tworzenie zoptymalizowanych indeksów, poprzez generowanie osadzania i integracji z LangChainem.
Przegląd serii
| # | Przedmiot | Centrum |
|---|---|---|
| 1 | Jesteś tutaj - pgvector | Instalacja, operatorzy, indeksowanie |
| 2 | Osadzanie w głębi | Modele, porcjowanie, wymiary |
| 3 | RAG z PostgreSQL | Rurociąg RAG od końca do końca |
| 4 | Wyszukiwanie hybrydowe | Wektory + wyszukiwanie pełnotekstowe |
| 5 | Skalowanie wektora pg | Partycjonowanie, sharding, wydajność |
| 6 | pgvector w produkcji | Monitorowanie, tworzenie kopii zapasowych, CI/CD |
Czego się nauczysz
- Co to jest pgvector i dlaczego jest najważniejszym rozszerzeniem PostgreSQL w 2026 roku
- Jak zainstalować pgvector na Linuksie, Dockerze i usługach zarządzanych (Supabase, Neon, AWS RDS)
- Jak tworzyć tabele z kolumnami wektorowymi i wstawiać osadzenia
- Trzy operatory odległości: cosinus, L2 i iloczyn wewnętrzny
- Jak przeprowadzać efektywne zapytania wyszukiwania podobieństwa
- Jak wygenerować osadzanie za pomocą Pythona (OpenAI i Transformatory zdań)
- HNSW vs IVFFlat: jaki indeks wybrać i jak go skonfigurować
- Porównanie pgvector vs dedykowane bazy danych wektorowych
- Kompletny, kompleksowy przykład z Pythonem
- Integracja z LangChain dla systemów RAG
1. Trend „Po prostu używaj Postgres” w 2026 roku
Świat AI od lat dąży do dedykowanych wektorowych baz danych: Pinecone, Weaviate, Qdrant, Milvus. Wydawało się, że każdy projekt RAG wymaga nowej usługi do zarządzania, monitorowania i płacenia. Ale w latach 2025-2026 coś się radykalnie zmieniło.
Społeczność zrozumiała, że m.in wektory nie są rodzajem bazy danych, ale typ danych. Podobnie jak PostgreSQL zarządza JSON, tablicami, geometrią i tekstami przy wyszukiwaniu pełnotekstowym radzi sobie także z wektorami wielowymiarowymi. Rozszerzenie, które sprawia, że to wszystko jest możliwe pgwektor.
dlaczego „Po prostu użyj Postgres” w 2026 r
| Czynnik | Dedykowana baza danych wektorów | PostgreSQL + pgvector |
|---|---|---|
| Infrastruktura | Nowa usługa do zarządzania | Rozszerzenie do istniejącej bazy danych |
| Koszt | Oddzielny plan (Pinecone od 70 USD/miesiąc) | Wliczone w cenę PostgreSQL |
| Spójność danych | Wymagana synchronizacja pomiędzy bazami danych | Natywne transakcje ACID |
| Zapytania | Własne API | Standardowy SQL + DOŁĄCZ, GDZIE, GRUPUJ WEDŁUG |
| Kopie zapasowe | Oddzielny system | pg_dump zawiera wszystko |
| Zespół | Wymagane konkretne umiejętności | Każdy, kto zna SQL |
Kluczową zaletą jest prostota obsługi. Dzięki pgvector Twoje osadzania znajdują się w tej samej tabeli, co Twoje dane relacyjne. Możesz DOŁĄCZYĆ wektory i metadane, użyj klauzuli WHERE do filtrowania i zachowaj spójność transakcyjną. Brak usług zewnętrznych od synchronizacja, nie trzeba się uczyć różnych interfejsów API.
2. Co to jest pgvector
pgwektor oraz rozszerzenie typu open source dla PostgreSQL, które dodaje obsługę natywny dla wektorów wielowymiarowych i wyszukiwania podobieństw. Stworzony przez Andrew Kane'a w 2021 roku i szybko stało się najczęściej pobieranym rozszerzeniem PostgreSQL do zastosowań AI/ML.
Dzięki pgvector możesz:
- Zapisz wektory (osadzenia) jako natywny typ danych w kolumnie tabeli
- Wykonuj wyszukiwanie podobieństw, korzystając z trzech różnych metryk odległości
- Twórz wyspecjalizowane indeksy (HNSW i IVFFlat) do szybkiego wyszukiwania
- Połącz wyszukiwanie wektorowe ze wszystkim, co oferuje PostgreSQL: JOIN, transakcje, wyzwalacze, zmaterializowane widoki
pgvector w liczbach (2026)
| Metryczny | Wartość |
|---|---|
| Aktualna wersja | 0.8.0+ (z iteracyjnym skanowaniem indeksu) |
| Maks. rozmiar nośnika | 16 000 |
| Typy indeksów | HNSW, IVFFlat |
| Typy wektorów | wektor, halfvec, sparsevec, bit |
| Wskaźniki odległości | Cosinus, L2 (euklidesowy), iloczyn wewnętrzny |
| Obsługiwane przez | AWS RDS, Supabase, Neon, Google Cloud SQL, Azure |
| Licencja | Licencja PostgreSQL (open source) |
Wersja 0.8.0, wydana w 2025 r., wprowadziła kluczową funkcję: gli iteracyjne skanowanie indeksu. Ta funkcja rozwiązuje problem nadmiernego filtrowania, to znaczy, gdy indeks wektorowy nie zwraca wystarczającej liczby wyników, ponieważ większość jest odrzucany przez filtry WHERE. Dzięki skanom iteracyjnym pgvector kontynuuje eksplorację indeksu dopóki nie znajdziesz wystarczającej liczby wyników pasujących do filtrów, drastycznie poprawiając jakość filtrowanych zapytań.
3. Instalowanie pgvectora
pgvector instaluje się na różne sposoby, w zależności od środowiska. Przyjrzyjmy się trzem podejściu główne: instalacja natywna na Linuksie, Dockerze i usługach zarządzanych.
3.1 Instalacja na Ubuntu/Debianie
# 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;"
W przypadku PostgreSQL 17 zamień postgresql-server-dev-16 con
postgresql-server-dev-17. pgvector obsługuje PostgreSQL od wersji 13
o 17:00.
3.2 Instalacja za pomocą Dockera
Najszybszy sposób na rozpoczęcie. Oficjalny wizerunek pgvector/pgvector
zawiera PostgreSQL z preinstalowanym pgvectorem.
# 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 Usługi zarządzane (konfiguracja zerowa)
Jeśli nie chcesz zarządzać infrastrukturą, kilka usług w chmurze oferuje PostgreSQL z pgvector jest już włączony. Jest to idealny wybór do produkcji.
Usługi zarządzane za pomocą pgvector
| Praca | pgvector Dołączony | Bezpłatny plan | Notatki |
|---|---|---|---|
| Superbaza | Zainstalowany fabrycznie | Tak (500MB) | Interfejs panelu sterowania, automatyczne interfejsy API REST |
| Neon | Zainstalowany fabrycznie | Tak (0,5 GB) | Bezserwerowe, oddziałowe bazy danych |
| AWS RDS | Aby było włączone | No | pgvector 0.8.0 na Aurorze PostgreSQL |
| Google CloudSQL | Aby było włączone | No | Obsługa pgvector 0.8.0 |
| Baza danych Azure | Aby było włączone | No | Elastyczny serwer z 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. Utwórz tabelę z wektorami
Po zainstalowaniu pgvector możesz dodawać kolumny typu vector(n) do twojego
stoły, gdzie n i wymiarowość wektora. Wartość n
Zależy to od używanego modelu osadzania: OpenAI text-embedding-3-small produkuje
1536 wektorów wymiarowych, podczas gdy text-embedding-3-large wzrasta do 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)';
Wstaw dokumenty z osadzeniem
Wstawianie działa jak normalny INSERT, przekazując wektor jako ciąg wartości oddzielone przecinkami w nawiasach kwadratowych.
-- 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, ...]');
Rozmiar wektorowy
Wymiarowość określona w definicji kolumny (vector(1536))
musi dokładnie odpowiadać rozmiarowi wstawianych elementów osadzonych. Próba
aby wstawić 768-wymiarowy wektor do kolumny vector(1536) spowoduje
błąd. Wybierz rozmiar w oparciu o szablon do osadzania, którego planujesz użyć.
Wymiary popularnych modeli osadzania
| Model | Dostawcy | Wymiary | Koszt / 1 milion tokenów |
|---|---|---|---|
| osadzanie tekstu-3-małe | OpenAI | 1536 | 0,02 USD |
| osadzanie tekstu-3-duże | OpenAI | 3072 | 0,13 USD |
| all-MiniLM-L6-v2 | Przytulanie twarzy (bezpłatny) | 384 | Bezpłatnie (lokalnie) |
| all-mpnet-base-v2 | Przytulanie twarzy (bezpłatny) | 768 | Bezpłatnie (lokalnie) |
| podróż-3 | AI podróży | 1024 | 0,06 USD |
5. Operatorzy odległości
pgvector oferuje trzy operatory do obliczania odległości między wektorami. Każdy operator odpowiada innej metryce matematycznej i ma określone przypadki użycia.
Trzej operatorzy pgvector
| Operator | Metryczny | Formuła | Sortowanie | Przypadek użycia |
|---|---|---|---|---|
<=> |
Odległość cosinusa | 1 - cosinus_podobieństwo | ASC (0 = identyczne) | Tekst, znormalizowane osadzanie |
<-> |
L2 (euklidesowy) | sqrt(suma((a-b)^2)) | ASC (0 = identyczne) | Obrazy, dane przestrzenne |
<#> |
Produkt wewnętrzny (odrzucony) | -1 * produkt_kropki | ASC (bardziej negatywne = bardziej podobne) | Znormalizowane wektory, maksymalna wydajność |
-- 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;
Którego operatora wybrać
W 90% przypadków użycia z tekstem i sztuczną inteligencją użyj <=> (odległość cosinus).
Osadzanie modeli, takich jak OpenAI, tworzy znormalizowane wektory, co sprawia, że
odległość cosinus i odpowiednik iloczynu wewnętrznego pod względem wyniku. USA <->
(L2) tylko podczas pracy z danymi przestrzennymi lub obrazami, gdzie liczy się wielkość wektora.
Produkt wewnętrzny (<#>) i nieco szybciej niż cosinus, ponieważ pozwala uniknąć
normalizacja, ale tylko na już znormalizowanych wektorach.
6. Zapytania wyszukiwania podobieństw
Wyszukiwanie podobieństw jest sercem pgvector. Podstawowe zapytanie jest proste: posortuj wyniki według odległości od wektora zapytania i uzyskaj pierwsze K. Ale w produkcji zapytania stają się bardziej wyrafinowane poprzez połączenie wyszukiwania wektorowego z filtrami SQL.
6.1 Podstawowe zapytania
-- 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 Zapytania z filtrami (prawdziwa moc pgvector)
Tutaj pgvector wyróżnia się na tle dedykowanych baz danych wektorowych. Możesz połączyć swoje wyszukiwania wektor z dowolną klauzulą SQL: WHERE, JOIN, GROUP BY, podzapytanie.
-- 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 Iteracyjne skanowanie indeksu (pgvector 0.8.0)
Jedną z najważniejszych nowych funkcji wersji 0.8.0 jest obsługa iteracyjne skanowanie indeksu. Po połączeniu wyszukiwania wektorowego z filtrami WHERE restrykcyjne, indeks może nie znaleźć wystarczającej liczby trafień w pierwszym przebiegu. W przypadku skanowania iteracyjnego pgvector kontynuuje skanowanie indeksu, aż do uzyskania zadowalającego wyniku żądany LIMIT.
-- 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)
Kiedy używać skanów iteracyjnych
- zrelaksowany_zamówienie: w większości przypadków zapewnia najlepszą równowagę pomiędzy wydajnością i jakością
- ścisła_kolejność: gdy dokładna kolejność odległości ma kluczowe znaczenie (np. ranking wyników dla użytkownika)
- wyłączony: jeśli nie masz filtrów WHERE lub gdy filtry nie są restrykcyjne
7. Generuj osadzanie za pomocą Pythona
Aby wprowadzić dane do pgvector, musisz najpierw przekształcić tekst na wektory numeryczne za pomocą model osadzania. Let's see two approaches: the OpenAI API (paid, high quality) i Transformatory zdań (bezpłatne, lokalne).
7.1 Z API 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 Z transformatorami zdań (bezpłatnymi i lokalnymi)
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 kontra transformatory zdań
| Czekam | API OpenAI | Transformatory zdań |
|---|---|---|
| Koszt | Tokeny o wartości 0,02 USD / 1 milion | Bezpłatny |
| jakość | Doskonały (najnowocześniejszy) | Dobry (wystarczający w wielu przypadkach) |
| Prywatność | Dane przesłane do OpenAI | Wszystko lokalne, żadne dane nie są przesyłane |
| Prędkość | ~100 ms na żądanie (sieć) | ~10 ms dla tekstu (z GPU) |
| Nieaktywny | Nie (wymaga internetu) | Tak (po pierwszym pobraniu) |
| Wymiary | 1536 lub 3072 | 384 lub 768 (w zależności od modelu) |
Złota zasada osadzania
Użyj tego ten sam model osadzania zarówno do indeksowania dokumentów, jak i
do wygenerowania wektora zapytania. Osadzenia wygenerowane przy użyciu różnych modeli są obecne
różnych przestrzeniach wektorowych i nie są porównywalne. Jeśli indeksujesz za pomocą
text-embedding-3-small, musisz szukać według tego samego modelu.
8. Indeksowanie: HNSW vs IVFFlat
Bez indeksu pgvector przeprowadza porównanie skanowania sekwencyjnego (brute-force). the query vector with each row of the table. Jest to w 100% dokładne, ale tak się dzieje powolny w przypadku dużych zbiorów danych. Poświęcenie indeksów przybliżonego sąsiada (ANN). minimalny procent precyzji, aby uzyskać o rząd wielkości szybsze wyszukiwania.
8.1 Indeks HNSW
HNSW (Hierarchical Navigable Small World) buduje wielopoziomowy graf połączeń pomiędzy przewoźnikami. A indeks zalecany w większości przypadków: szybkie czytanie, dobra dokładność i umożliwia wstawianie przyrostowe bez rekonstrukcji.
-- 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
Parametry HNSW: Przewodnik konfiguracji
| Parametr | Domyślny | Zakres | Efekt | Radzić |
|---|---|---|---|---|
m |
16 | 2 - 100 | Więcej połączeń = dokładniej, więcej pamięci | W większości przypadków 16-32 |
ef_construction |
64 | 4 - 1000 | Wyższy = lepszy indeks, wolniejsza kompilacja | 64-256 (jednorazowe, warte podniesienia) |
ef_search |
40 | 1 - 1000 | Wyższa = dokładniejsze i wolniejsze wyszukiwanie | 40-200 w zależności od pożądanego kompromisu |
8.2 Indeks IVFF płaski
IVFFlat (plik odwrócony z kompresją płaską) dzieli wektory tylko na klastry i przeszukuje w klastrach znajdujących się najbliżej zapytania. Jest szybszy w budowie niż HNSW, ale wymaga rekonstrukcja po dodaniu dużej ilości nowych danych. Idealny do zmiany zbiorów danych rzadko.
-- 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 Operatorzy według typu odległości
Operator określony podczas tworzenia indeksu musi być zgodny z operatorem używane w zapytaniach. Oto pełna mapa.
Operatory map i klasy indeksów
| Dystans | Operator SQL | Klasa indeksu (ups) |
|---|---|---|
| Małe rzeczy | <=> |
vector_cosine_ops |
| L2 (euklidesowy) | <-> |
vector_l2_ops |
| Produkt wewnętrzny | <#> |
vector_ip_ops |
8.4 HNSW vs IVFFlat: co wybrać
Porównanie HNSW i IVFFpłaskie
| Czekam | HNSW | IVFFlat |
|---|---|---|
| Szybkość wyszukiwania | Szybciej | Szybko |
| Dokładność (przypomnij sobie) | ~95-99% | ~90-98% |
| Czas budowania | Powolny | Szybko |
| Pamięć | Wysoki | Przeciętny |
| Wstawienia przyrostowe | Utrzymany | Wymagają REINDEXU |
| Skanowanie iteracyjne | Tak (0.8.0) | Tak (0.8.0) |
| Idealny przypadek użycia | Ogólne zastosowanie, rosnące dane | Statyczne zbiory danych, szybka kompilacja |
Praktyczne porady
W przypadku większości projektów wybierz HNSW. I bardziej wszechstronny, obsługuje przyrostowe wstawianie bez przebudowy, a wersja 0.8.0 pgvector ma znacząco poprawił swoje działanie. Używaj IVFFlat tylko wtedy, gdy czas konstrukcja indeksu i krytyczna (bardzo duże zbiory danych ładowane tylko raz) o jeśli masz rygorystyczne ograniczenia pamięci.
9. Wydajność: pgvector vs dedykowane bazy danych wektorowych
Najczęściej zadawane pytanie brzmi: „czy pgvector jest wystarczająco szybki do produkcji?”. Odpowiedź w 2026 r. i jasne si, w zdecydowanej większości przypadków użycia. Zobaczmy liczby.
9.1 Punkty odniesienia na dużą skalę
Benchmarki pgvectorscale (uzupełniające rozszerzenie pgvector opracowane przez Timescale) na 50 milionów przewoźników wykazuje imponujące wyniki:
Test porównawczy: 50 milionów wektorów przy przypominaniu na poziomie 99%.
| System | QPS (zapytania/s) | Przypomnienie sobie czegoś | Notatki |
|---|---|---|---|
| pgvectorscale | 471 | 99% | 11,4x szybszy niż Qdrant |
| Qdrant | 41 | 99% | Zoptymalizowany pod kątem czystego wyszukiwania wektorowego |
Test porównawczy średniej skali 9.2 (wektory 1M)
Test porównawczy: wektory 1M — przepustowość
| System | Zapytania/sek | Wstawki/sek | Koszt zarządzany |
|---|---|---|---|
| Szyszka | ~5000 | ~ 50 000 | Od 70 USD miesięcznie |
| Qdrant | ~4500 | ~ 45 000 | Hostowane samodzielnie lub w chmurze |
| pgwektor | ~3000 | ~ 30 000 | Wliczone w cenę PG |
Przy wektorach 1M pgvector jest nieco wolniejszy w czystej przepustowości. Ale biorąc pod uwagę że koszt jest praktycznie zerowy (i jest już uwzględniony w Twoim PostgreSQL), raport jakość-cena i bezkonkurencyjna. W przypadku większości aplikacji internetowych 3000 zapytań na sekundę są więcej niż wystarczające.
Kiedy pgvector NIE WYSTARCZA
Rozważ dedykowaną bazę danych wektorów (Qdrant, Pinecone), gdy:
- Masz już koniec 50 milionów wektorów
- Potrzebujesz opóźnień p99 poniżej i 5 ms
- Twoje obciążenie polega wyłącznie na wyszukiwaniu wektorowym (bez danych relacyjnych)
- Potrzebujesz natywnego rozproszonego fragmentowania
Do wszystkiego innego pgvector jest właściwym wyborem. System poniżej 10 milionów wektorów z PostgreSQL już na stosie nie wymaga to żadnej dodatkowej złożoności.
10. Kompletny przykład: Baza wiedzy z Pythonem
Zestawmy wszystko w praktycznym przykładzie. Zbudujemy bazę wiedzy, która: indeksuje dokumenty z osadzaniami, umożliwia wyszukiwanie podobieństw i obsługuje filtry według kategorii.
"""
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. Integracja z LangChain
Jeśli budujesz system RAG, LangChain oferuje bezpośrednią integrację z pgvector
przez paczkę langchain-postgres. Dzięki temu możesz korzystać
PostgreSQL jako magazyn wektorowy bez ręcznego pisania 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")
Uwaga na temat psycopg3
Pakiet langchain-postgres wymaga psychopg3 (nie psychopg2).
Parametry połączenia muszą używać formatu postgresql+psycopg:// zamiast postgresql+psycopg2://. Jeśli migrujesz z langchain-community,
zaktualizuj ciąg połączenia.
12. Najlepsze praktyki dla pgvector w produkcji
12.1 Rozmiar wektora
Mniejsze wektory zajmują mniej miejsca, przyspieszają indeksowanie i zmniejszają koszty.
OpenAI text-embedding-3-small (1536 dim) oferuje najlepszą wartość
jakość-rozmiar w większości przypadków. Jeśli możesz tolerować niewielką stratę
jakości, all-MiniLM-L6-v2 (384 dim) i świetny, darmowy wybór.
12.2 Wkładka partii
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 Pula połączeń
W środowisku produkcyjnym nie otwieraj nowego połączenia PostgreSQL dla każdego żądania.
Użyj puli połączeń, np PgBouncer lub zintegrowane łączenie
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 Konserwacja
-- 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 kontrolna produkcji
- Utwórz indeks HNSW za pomocą
m = 24eef_construction = 128 - Podatek
ef_searchod 40 do 200 w oparciu o kompromis między opóźnieniem a precyzją - Umiejętność
hnsw.iterative_scan = relaxed_orderjeśli używasz filtrów WHERE - Użyj wkładki wsadowej z
execute_valuesdo dużych obciążeń - Skonfiguruj pulę połączeń (PgBouncer lub psycopg2.pool)
- Harmonogram
VACUUM ANALYZEokresowy - Monitoruj zapytania za pomocą
EXPLAIN ANALYZEw celu sprawdzenia użycia indeksu - USA
halfveczamiastvectoraby zaoszczędzić 50% miejsca (jeśli precyzja float16 jest wystarczająca)
Wnioski
pgvector przekształcił PostgreSQL z prostej relacyjnej bazy danych w platformę obsługującą sztuczną inteligencję. W wersji 0.8.0 i jej iteracyjnym skanowaniu indeksów luka w stosunku do wektorowych baz danych przeznaczonych na ten cel drastycznie spadło, a w przypadku większości projektów poniżej 10 milionów przewoźników, nie ma powodu dodawać osobnej usługi do swojego stosu.
W tym artykule nauczyłeś się instalować pgvector, tworzyć tabele z kolumnami wektorowymi, przeprowadzić wyszukiwanie podobieństw za pomocą trzech operatorów odległości, wygenerować osadzania za pomocą Pythona, skonfiguruj indeksy HNSW i IVFFlat i zintegruj wszystko z LangChain. Czy Wy też widzieliście testy porównawcze potwierdzające wydajność pgvector w produkcji.
Nel następny artykuł zagłębimy się w nie osadzania: jak działają wewnętrznie, jak wybrać odpowiedni model, strategie dzielenia w przypadku długich dokumentów i optymalizacji jakości wyszukiwania. w trzeci artykuł zbudujemy taki Rurociąg RAG gotowy z PostgreSQL jako jedynym backendem.
Dodatkowe zasoby
- pgvector GitHub: github.com/pgvector/pgvector — oficjalna dokumentacja i dziennik zmian
- pgvectorscale: github.com/timescale/pgvectorscale — ulepszone rozszerzenie wydajności
- LangChain PGVector: python.langchain.com - Oficjalna integracja z Pythonem
- Wektor superbazowy: supabase.com/docs/guides/ai - przewodnik pgvector w Supabase
- Neonowa sztuczna inteligencja: neon.tech/docs/ai - pgvector na serwerze Neon







