Intermedio 11 minStack

pgvector : la ricerca vettoriale in PostgreSQL

Risposta diretta

pgvector è un'estensione open source di PostgreSQL (licenza PostgreSQL, permissiva) che aggiunge l'archiviazione e la ricerca di vettori a un database che già amministri, con colonne indicizzabili fino a 2.000 dimensioni (4.000 in mezza precisione). Per un corpus locale di alcune centinaia di migliaia di frammenti, significa un servizio in meno da gestire e filtri SQL che funzionano, senza una sincronizzazione da mantenere con i tuoi dati relazionali.

pgvector è un'estensione di PostgreSQL che aggiunge l'archiviazione e la ricerca di vettori a un database che già amministri. Per la maggior parte dei progetti di ricerca documentale locale, significa un servizio in meno da tenere in esecuzione, un backup in meno da organizzare e filtri SQL che funzionano davvero — anche per i diritti di accesso. Il progetto, mantenuto sotto licenza PostgreSQL e ospitato su GitHub, contava più di 23.000 stelle a fine settembre 2026, alla versione 0.8.6, compatibile con PostgreSQL 13 e versioni successive.

Di Mohamed Meguedmi·Agg. 2026-09-28·Testato su Windows, macOS e Linux

#Argomento: non aggiungere un servizio

Un'installazione locale per la gestione dei documenti esegue già un server per il modello, una fase di codifica e un sistema di archiviazione dei documenti. Aggiungere un database vettoriale dedicato significa avere un container in più, una porta in più, un backup in più e un altro elemento che può perdere la sincronizzazione con i tuoi dati relazionali quando viene eliminato un documento.

Se PostgreSQL è già presente — e in un'applicazione aziendale lo è quasi sempre — pgvector elimina tutta questa categoria di problemi. I tuoi segmenti di testo risiedono in una tabella accanto ai documenti da cui provengono, con chiavi esterne che ne mantengono la coerenza, e una cancellazione si propaga come ti aspetti, senza dover scrivere né monitorare un'attività di pulizia separata. Il progetto aggiunge inoltre la ricerca esatta e approssimata, i vettori a precisione singola, a mezza precisione, binari e sparsi, cinque distanze (L2, prodotto scalare, coseno, L1, Hamming, Jaccard) ed eredita gratuitamente la conformità ACID, il ripristino a un punto nel tempo e le join di PostgreSQL.

#Come funziona in pratica

Il kit RAG Local

I tuoi documenti, la tua IA: un RAG locale affidabile sui tuoi PDF, sulle tue note e sulle tue email — senza inviare nulla nel cloud.

  • Spazio online a vita
  • PDF + file
  • Aggiornamenti a vita

Salvi una colonna di vettori accanto alle tue colonne abituali. Una query ordina le righe in base alla distanza dal vettore della domanda e restituisce quelle più vicine. Tre operatori di distanza coprono i casi comuni, e quello che scegli deve corrispondere alla convenzione del modello di embedding: è la causa più frequente di risultati mediocri che passano inosservati. Per i vettori normalizzati a lunghezza 1 (come quelli di OpenAI e della maggior parte dei modelli di embedding recenti), il prodotto scalare è il più veloce da calcolare e dà lo stesso ordinamento del coseno.

I pezzi del puzzle
ElementoCos'èDa monitorare
Colonna vettorialeUn array di float a dimensione fissaLa dimensione deriva dal modello di embedding e non cambia senza ricodificare tutto
Operatore di distanzaCoseno, prodotto scalare o distanza euclidea (L2)Deve corrispondere al modello
Indice HNSWIndice basato su un grafo, query rapideCostruzione lenta e con un elevato consumo di memoria; la scelta predefinita ragionevole dalla versione 0.5
Indice IVFFlatIndice basato su partizioni, poco costoso da costruireDa creare una volta che sono presenti dati rappresentativi
Nessun indiceScansione esatta di tutte le righeDel tutto fattibile fino a qualche decina di migliaia di righe

#SQL minimo per iniziare

  1. 01
    Attivare l'estensione
    CREATE EXTENSION IF NOT EXISTS vector; — un'unica istruzione, da eseguire una volta per ogni database.
  2. 02
    Aggiungere la colonna
    ALTER TABLE chunks ADD COLUMN embedding vector(1024); — la dimensione deve corrispondere esattamente a quella del modello di embedding utilizzato.
  3. 03
    Caricare i dati prima di indicizzarli
    Un caricamento in massa tramite COPY è più veloce senza un indice già esistente; crea l'indice quando è presente una quantità rappresentativa di righe.
  4. 04
    Creare l'indice
    CREATE INDEX CONCURRENTLY ON chunks USING hnsw (embedding vector_cosine_ops); — l'opzione CONCURRENTLY evita di bloccare le scritture durante la costruzione dell'indice, che richiede molto tempo su una tabella di grandi dimensioni.
  5. 05
    Interrogare
    SELECT contenu FROM chunks WHERE document_id IN (SELECT id FROM documents WHERE utilisateur_autorise($1)) ORDER BY embedding <=> $2 LIMIT 5; — filtro di autorizzazione e ordinamento per similarità nella stessa query.

#Le limitazioni di dimensione, in pratica

pgvector definisce quattro tipi di colonne, ciascuno con il proprio limite di dimensioni indicizzabili. Il tipo vector standard (precisione singola, 4 byte per elemento) è indicizzabile fino a 2 000 dimensioni, il che copre la maggior parte dei modelli di embedding open source comuni (da 384 a 1 024 dimensioni). Un modello più ampio come text-embedding-3-large di OpenAI, con 3 072 dimensioni per impostazione predefinita, supera questo limite: la soluzione documentata è ridurre la dimensionalità durante la generazione dell'embedding (l'API lo consente) oppure passare al tipo halfvec, che memorizza a mezza precisione (2 byte per elemento, metà dello spazio) ed è indicizzabile fino a 4 000 dimensioni. Il tipo bit (vettori binari, distanze di Hamming o Jaccard) arriva a 64 000 dimensioni indicizzabili, e sparsevec (vettori sparsi) a 1 000 elementi non nulli indicizzati — oltre questi limiti, PostgreSQL memorizza comunque la colonna (fino a 16 000 dimensioni per vector, halfvec e sparsevec) ma senza poterla indicizzare, tornando quindi a una scansione esatta.

→
La quantizzazione non è solo un dettaglio di archiviazione
Passare da vector a halfvec dimezza lo spazio occupato da ogni riga, consentendo di mantenere più indici in memoria e accelerando le query su larga scala senza modificare il tuo modello di embedding. La quantizzazione binaria (tipo bit) va oltre: comprime l'indice per la ricerca, poi un riordinamento basato sui vettori completi ripristina la precisione persa — il metodo documentato dal progetto stesso per mantenere un indice voluminoso interamente in memoria anziché lasciare che finisca in parte su disco.

#Il filtraggio e i diritti di accesso

Una domanda reale raramente riguarda l'intero corpus: si vogliono i passaggi di questo servizio, successivi a questa data, nei documenti che questo utente ha il diritto di leggere. In un database vettoriale dedicato, si tratta di un filtro sui metadati con la sua sintassi e i suoi casi limite. In PostgreSQL, si tratta di una clausola WHERE affiancata a un ordinamento per similarità, con un collegamento alla tua tabella degli utenti se necessario.

Un'insidia documentata dal progetto stesso merita di essere conosciuta prima di scoprirla in produzione: con un indice approssimativo (HNSW o IVFFlat), il filtro viene applicato dopo la scansione dell'indice, non prima. Se una condizione seleziona solo il 10 % delle righe e il parametro predefinito hnsw.ef_search vale 40, vengono restituite in media solo 4 righe, non le dieci richieste. La soluzione ufficiale, disponibile dalla versione 0.8.0, si chiama scansione iterativa dell'indice (SET hnsw.iterative_scan = strict_order) e riavvia automaticamente la scansione fino a trovare abbastanza risultati, anziché restituire un insieme troncato senza segnalarlo.

!
I permessi di accesso non si impostano nel prompt
Quando più persone interrogano lo stesso indice, il filtro sulle autorizzazioni impedisce al modello di citare a una persona un documento che quella persona non dovrebbe poter vedere. Nessuna istruzione nel prompt sostituisce questo filtro, ed esprimerlo in SQL sulla base del tuo modello di autorizzazione esistente è molto più sicuro che reimplementarlo.

#I limiti

La memoria su larga scala
Milioni di vettori a precisione piena occupano molto spazio; halfvec e la quantizzazione binaria riducono l'ingombro, ma i motori dedicati spingono nativamente la compressione ancora oltre. È la differenza più marcata.
Tempo di costruzione dell'indice
Costruire un indice HNSW su una tabella molto grande richiede tempo e molte risorse; in produzione, farlo con CREATE INDEX CONCURRENTLY evita di bloccare le scritture durante l'operazione.
La contesa delle risorse
Eseguire ricerche vettoriali pesanti insieme al tuo carico transazionale significa far gestire entrambi allo stesso server. Le repliche di lettura aiutano; separare i ruoli aiuta ancora di più.
La dimensione dei vettori
I vettori indicizzati hanno un limite in base al loro tipo (2.000 per vector, 4.000 per halfvec). I modelli comuni rientrano nei limiti; un modello con dimensionalità molto elevata deve essere ridotto o quantizzato.
La ricerca ibrida
PostgreSQL supporta la ricerca full-text (tsvector) e puoi combinarla con la distanza vettoriale, ma spetta a te rendere il tutto agevole da usare: devi eseguire le due query separatamente e poi fondere le classifiche, per esempio con una fusione dei ranghi reciproci (Reciprocal Rank Fusion), anziché ottenere un punteggio ibrido nativo in un'unica query.
La scalabilità orizzontale
Per andare oltre un singolo server, il percorso documentato prevede repliche PostgreSQL di sola lettura oppure uno strumento di distribuzione come Citus o PgDog — un componente in più, contrariamente all'argomento iniziale.
i
L'operazione VACUUM su un indice HNSW può essere lenta
La documentazione ufficiale lo segnala esplicitamente: la pulizia (VACUUM) di una tabella con un indice vettoriale HNSW può richiedere tempo quando il volume di dati è elevato. Per accelerarla, occorre eseguire REINDEX INDEX CONCURRENTLY prima di VACUUM anziché lasciare che l'operazione di manutenzione proceda da sola — un dettaglio operativo che pochi tutorial menzionano prima che la manutenzione notturna superi la finestra temporale prevista.

#pgvector o un database dedicato

La domanda non è quale sia oggettivamente migliore, ma quale corrisponda alla tua situazione attuale. pgvector è la scelta vincente quando PostgreSQL è già la fonte di riferimento dell'applicazione: fatturazione, account, documenti sorgente. Un motore dedicato è la scelta vincente quando il volume di vettori o il numero di richieste per unità di tempo supera ciò che un singolo server transazionale può gestire senza compromettere il resto dell'applicazione, oppure quando il team preferisce isolare il componente IA dal resto del sistema informativo per ragioni operative anziché di pura prestazione.

Scegliere in base alla situazione
SituazioneScelta
PostgreSQL già in uso, meno di qualche centinaio di migliaia di segmentipgvector, comodamente
I vettori devono restare coerenti con dati relazionalipgvector: le transazioni lo fanno gratuitamente
Filtraggio granulare basato sulle autorizzazioni esistentipgvector
Modello di embedding con più di 4.000 dimensioni senza riduzione possibileVerificare il tipo sparsevec o un database dedicato progettato per questo caso
Milioni di vettori, molte richiesteUn motore dedicato (Qdrant, Milvus)
Prototipo in un notebookUno qualsiasi, questa scelta è reversibile

#FAQ

pgvector è abbastanza veloce per il RAG?+
Per un corpus locale tipico — da alcune decine ad alcune centinaia di migliaia di segmenti — sì, con un indice HNSW, e spesso anche senza indice nella parte bassa di questo intervallo. Il recupero è raramente la fase lenta di una pipeline locale: è la generazione da parte del modello a incidere maggiormente sul tempo di risposta percepito.
pgvector o Qdrant?+
pgvector se PostgreSQL è già presente e i tuoi dati sono relazionali: meno servizi, coerenza transazionale, filtri SQL nativi e un solo backup da organizzare. Qdrant quando la scala, una quantizzazione aggressiva per ridurre l'uso della memoria o un servizio dedicato interamente progettato per la ricerca vettoriale contano più della semplicità di gestione di un unico database. Entrambi soddisfano effettivamente la stessa esigenza fino a diverse centinaia di migliaia di vettori.
Quale indice scegliere, HNSW o IVFFlat?+
HNSW è l'opzione predefinita da diverse versioni: offre migliori prestazioni nelle query, a costo di una costruzione più lenta e di un maggiore consumo di memoria. IVFFlat è meno costoso da costruire, ma deve essere creato dopo aver caricato dati rappresentativi, altrimenti le partizioni risulteranno distribuite male.
Posso filtrare per metadati?+
Sì, con normale SQL, join inclusi. È una delle migliori ragioni per sceglierlo, soprattutto per filtrare in base ai diritti di accesso. Attenzione però: con un indice approssimativo, questo filtro viene applicato dopo la scansione dell'indice, il che può restituire meno risultati del previsto senza la scansione iterativa.
Che succede se cambio il modello di embedding?+
Tutto deve essere ricodificato: la dimensione e la geometria dei vettori appartengono al modello, non al database. Si tratta di un'elaborazione in batch su GPU, non di una migrazione dello schema, e occorre ricreare la colonna se la nuova dimensione supera quella dichiarata. Prevedi una finestra di passaggio, perché il vecchio indice rimane valido finché la ricodifica non è completata.
Il mio modello di embedding ha più di 2.000 dimensioni, cosa fare?+
Il tipo vector standard indicizza fino a 2.000 dimensioni. Oltre questo limite, passa al tipo halfvec (fino a 4.000, in mezza precisione) oppure riduci la dimensionalità durante la generazione se il tuo fornitore lo consente, come permette di fare OpenAI per text-embedding-3-large. Senza indice, PostgreSQL memorizza comunque fino a 16.000 dimensioni, ma la ricerca avviene tramite una scansione esatta.
Come diagnosticare una query vettoriale lenta?+
La documentazione raccomanda di anteporre EXPLAIN (ANALYZE, BUFFERS) alla query per verificare se l'indice viene effettivamente utilizzato e quanti blocchi vengono letti. Una query di ricerca esatta senza indice trae vantaggio dall'aumento di max_parallel_workers_per_gather; una query di ricerca approssimativa lenta indica spesso un indice ancora in costruzione o memoria insufficiente per mantenerlo in cache.
Questa guida ti è stata utile?

Un feedback, un errore, una precisazione? Facci sapere, così la guida migliora per tutti.