SSD Nodes Learn 🎉 VPS da $5.50/mese
Guide Matt ConnorDi Matt Connor

Database vettoriale su VPS: costi, RAM e scelta

App e indice sullo stesso VPS: confronta pgvector, Qdrant, Chroma e brute force. Calcola RAM, latenza e costo reale degli embedding prima di scegliere.

Il costo effettivo di un database vettoriale su un VPS

Eseguire un database vettoriale su un VPS (virtual private server) elimina il problema per cui ogni fornitore di servizi gestiti propone una soluzione. L’applicazione e l’indice risiedono sulla stessa macchina, quindi una richiesta di ricerca attraversa un socket loopback invece della rete. Resta il costo che è sempre stato quello reale: trasformare il testo in vettori. A questo si aggiungono altri due costi: il tempo necessario per creare l’indice e la RAM che l’indice occupa finché è utilizzato per servire le richieste.

Questo cambia l’importanza delle decisioni da prendere. La regione e il round trip verso l’endpoint non sono più una tua responsabilità. Lo diventano il prodotto tra numero di vettori, dimensioni e quattro byte, perché determina se l’indice entra nella memoria che noleggi ogni mese.

Dove finiscono davvero i millisecondi su un singolo server

Segui una query di similarità attraverso uno stack self-hosted.

  1. Il testo della query viene convertito in un vettore da un modello di embedding. Su CPU sono necessarie da decine a centinaia di millisecondi per una stringa breve. Su GPU si tratta di pochi millisecondi.
  2. Il vettore viene inviato allo store. Su TCP loopback o tramite un socket Unix, l'operazione richiede una frazione di millisecondo.
  3. Lo store analizza il proprio indice e restituisce le righe più vicine.
  4. Il codice legge il testo corrispondente e compone un prompt.

Il passaggio 1 è generalmente quello con il valore più alto. Il passaggio 2 è quello su cui competono i fornitori di servizi hosted e, su un singolo server, incide appena. Non basarti su stime. Misura entrambi gli estremi sul tuo server.

curl http://127.0.0.1:11434/api/embed -s -o /dev/null \
  -w 'embed: %{time_total}s\n' \
  -d '{"model": "nomic-embed-text", "input": "how do I rotate the api key"}'

Esegui quindi \timing on in psql prima della query di ricerca. Se il primo comando stampa embed: 0.184312s e psql restituisce Time: 4.201 ms, ottimizzare l'indice è l'intervento sbagliato: la latenza dipende dal modello di embedding. Eseguire localmente il modello di embedding con Ollama esegue il passaggio 1 sulla stessa CPU dei passaggi 2 e 3, quindi entrambe le parti competono per gli stessi core e la stessa RAM. Il ciclo di ingestione e recupero che usa questo store è descritto nella guida alla pipeline RAG self-hosted. RAG significa generazione aumentata dal recupero: cerchi nei tuoi documenti e inserisci i risultati migliori in un prompt.

Sotto circa centomila vettori, esegui la scansione di tutti

Una scansione esaustiva confronta la query con ogni vettore memorizzato. Il richiamo è perfetto per definizione. Non richiede un indice né una fase di build e non può diventare obsoleta rispetto ai dati.

L'aritmetica indica quando questa soluzione smette di essere adeguata. Una scansione legge n * d * 4 byte per query, dove n è il numero di vettori e d è la dimensione. Con 100,000 vettori di dimensione 768, si tratta di 307 MB per query, una quantità che una CPU moderna può elaborare in streaming in alcune decine di millisecondi. Con 5 milioni di vettori, si arriva a 15 GB per query: non è più una query praticabile.

Quindi, memorizza i vettori in SQLite ed esegui il confronto in NumPy.

import sqlite3, numpy as np

db = sqlite3.connect("docs.db")
db.execute("CREATE TABLE IF NOT EXISTS docs (id INTEGER PRIMARY KEY, body TEXT, vec BLOB)")

def add(body, vec):
    v = np.asarray(vec, dtype=np.float32)
    v /= np.linalg.norm(v)
    db.execute("INSERT INTO docs (body, vec) VALUES (?, ?)", (body, v.tobytes()))
    db.commit()

def search(query_vec, k=5):
    rows = db.execute("SELECT id, body, vec FROM docs").fetchall()
    mat = np.frombuffer(b"".join(r[2] for r in rows), dtype=np.float32).reshape(len(rows), -1)
    q = np.asarray(query_vec, dtype=np.float32)
    q /= np.linalg.norm(q)
    scores = mat @ q
    return [(rows[i][0], rows[i][1], float(scores[i])) for i in np.argsort(-scores)[:k]]

Entrambi i lati vengono ridimensionati alla lunghezza unitaria, quindi il prodotto scalare è la similarità coseno e un punteggio più alto indica una corrispondenza più vicina. Carica mat una sola volta all'avvio, invece di farlo per ogni query, così la lettura da SQLite viene completamente rimossa dal percorso critico.

Misura le prestazioni sul tuo sistema prima di scartare questa soluzione.

import time
t = time.perf_counter()
search(q)
print(f"{(time.perf_counter() - t) * 1000:.1f} ms")

I limiti reali sono questi: un singolo processo mantiene l'intera matrice in RAM e non offre né il filtraggio dei metadati né una gestione dei writer concorrenti. Se uno di questi aspetti è il motivo per cui la soluzione non è adatta, passa a un'altra soluzione. SQLite è di per sé un archivio serio per il lato server, come illustrato in la guida a SQLite in produzione, mentre se il carico di lavoro reale consiste nella scansione di colonne anziché nella gestione di righe, è più utile leggere il confronto tra DuckDB e SQLite.

pgvector, se si utilizza già Postgres

Se l'applicazione dispone già di un database Postgres, pgvector introduce il minor numero di componenti aggiuntivi. È un'estensione, non un servizio. I vettori risiedono in una tabella normale accanto alla riga che descrivono, quindi una ricerca filtrata usa una clausola WHERE invece di un secondo sistema da mantenere sincronizzato.

Ubuntu 24.04 lo include nel componente universe.

sudo apt update
sudo apt install -y postgresql-16-pgvector
sudo -u postgres psql -d yourdb -c 'CREATE EXTENSION vector;'

Ad agosto 2026, quel pacchetto contiene pgvector 0.6.0, una versione molto più vecchia di quella upstream. In particolare, le scansioni iterative degli indici richiedono la versione 0.8; per questo è necessario installarle dal repository ufficiale del progetto PostgreSQL.

sudo apt install -y postgresql-common
sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh
sudo apt install -y postgresql-17-pgvector

Sostituire 17 con la versione principale del server, visualizzata da sudo -u postgres psql -tAc 'SHOW server_version'. Se si installa il pacchetto dell'estensione compilato per una versione principale errata, CREATE EXTENSION fallisce perché Postgres cerca i componenti soltanto nella directory share della versione in esecuzione:

ERROR:  could not open extension control file "/usr/share/postgresql/16/extension/vector.control": No such file or directory

Lo schema usa SQL ordinario con un solo nuovo tipo.

CREATE TABLE chunks (
  id        bigserial PRIMARY KEY,
  doc_id    bigint NOT NULL,
  body      text   NOT NULL,
  embedding vector(768)
);

SELECT id, body FROM chunks
ORDER BY embedding <=> '[0.013, -0.021, 0.004]'
LIMIT 5;

<=> indica la distanza coseno, <-> la distanza L2 (euclidea) e <#> il prodotto interno negativo. Usare la metrica per cui è stato addestrato il modello di embedding. Se si sceglie quella errata, non si verificano errori, ma i risultati peggiorano senza segnali evidenti.

Senza un indice, la query esegue una ricerca esatta su ogni riga. È l'equivalente Postgres della ricerca brute force descritta sopra e offre lo stesso richiamo perfetto. Aumentando max_parallel_workers_per_gather si assegnano più core all'operazione. Eseguire prima questa ricerca e creare l'indice in seguito, perché in questo modo si dispone di una baseline del richiamo con cui confrontare l'indice.

Qdrant, quando l'indice supera le dimensioni del database

Qdrant è un vector store dedicato scritto in Rust. È utile quando l'indice è abbastanza grande da rendere preferibile non far competere la sua generazione con il database Postgres dell'applicazione, oppure quando servono il filtraggio dei payload e la quantizzazione che pgvector non offre.

docker run -d --name qdrant \
  -p 127.0.0.1:6333:6333 -p 127.0.0.1:6334:6334 \
  -e QDRANT__SERVICE__API_KEY="$(openssl rand -hex 32)" \
  -v "$(pwd)/qdrant_storage:/qdrant/storage:z" \
  qdrant/qdrant

La porta 6333 espone l'API REST (representational state transfer) e una dashboard all'indirizzo /dashboard; la porta 6334 espone gRPC. Su un VPS pubblico, due aspetti sono importanti. La documentazione di Qdrant specifica che il servizio viene eseguito per impostazione predefinita «senza crittografia né autenticazione» e che -p 6333:6333 del quickstart è in ascolto su tutte le interfacce. Docker pubblica quindi la porta oltre una regola ufw, perché crea proprie regole di forwarding. Configura l'ascolto su 127.0.0.1 e imposta una API key. Un'istanza Qdrant raggiungibile da un IP pubblico senza chiave rende pubblica una copia dei tuoi documenti.

curl -s http://127.0.0.1:6333/collections -H "api-key: $QDRANT_API_KEY"

Una risposta corretta ha l'aspetto {"result":{"collections":[]},"status":"ok","time":0.00002}. Se viene restituito {"status":{"error":"Unauthorized"}}, il nome dell'header o la chiave non sono corretti; se non viene restituito nulla, il container non è in esecuzione oppure è in ascolto altrove. Stabilire se quel container sia adatto al tuo server è la normale valutazione richiesta da un servizio stateful, quindi il confronto tra database Docker e database sull'host si applica anche in questo caso.

Chroma e il suo scopo

Chroma è il percorso più rapido per passare da zero a una demo di retrieval funzionante.

pip install chromadb
chroma run --path /srv/chroma

Il servizio è in ascolto sulla porta 8000 e chromadb.HttpClient(host="localhost", port=8000) vi si connette. Chroma include una funzione di embedding predefinita, quindi per un primo prototipo non serve alcun model server separato.

È importante considerare il compromesso. Chroma è pratico perché nasconde le decisioni trattate in questa guida: quale metrica di distanza usare e quanta RAM occuperanno i dati risultanti. Questo è corretto per un prototipo, ma non per il sistema di cui dovrai gestire gli incidenti. Se i dati risiedono già in Postgres, trasferirli in Chroma aggiunge un processo e un problema di sincronizzazione per risolvere un problema che pgvector non presenta.

Quanta RAM richiederà l'indice

Parti dai vettori grezzi, perché rappresentano il limite minimo e nessuna ottimizzazione può ridurne le dimensioni.

bytes = number_of_vectors * dimensions * 4

Quattro byte corrispondono a un numero a virgola mobile a 32 bit per ogni dimensione. La documentazione di pianificazione della capacità di Qdrant aggiunge un moltiplicatore di 1.5 per i metadati e per i segmenti temporanei creati durante l'ottimizzazione:

memory_size = number_of_vectors * vector_dimension * 4 bytes * 1.5

Questa è la formula applicata a un milione di vettori, con il numero di dimensioni prodotto dai modelli di embedding reali.

ChartRAM for 1 million vectors, by embedding dimension
The data behind this chart
[
  {
    "label": "384 dims",
    "raw_gib": 1.43,
    "with_overhead_gib": 2.15
  },
  {
    "label": "768 dims",
    "raw_gib": 2.86,
    "with_overhead_gib": 4.29
  },
  {
    "label": "1024 dims",
    "raw_gib": 3.81,
    "with_overhead_gib": 5.72
  },
  {
    "label": "1536 dims",
    "raw_gib": 5.72,
    "with_overhead_gib": 8.58
  },
  {
    "label": "3072 dims",
    "raw_gib": 11.44,
    "with_overhead_gib": 17.17
  }
]

Questi sono i risultati della formula in gibibyte (GiB), non una misurazione. Interpretali come lo spazio di memoria che devi lasciare disponibile. Un modello a 768 dimensioni come nomic-embed-text, applicato a un milione di chunk, richiede circa 4.29 GiB. Questa quantità rientra in un piano da 8 GB, lasciando spazio anche per Postgres. Lo stesso corpus a 3072 dimensioni richiede 17.17 GiB e non rientra.

La variabile più importante è la prima colonna della tabella, non l'ultima. Dimezzare il numero di dimensioni dimezza per sempre ogni byte utilizzato a valle. Un modello a 768 dimensioni che ottiene un punteggio leggermente peggiore in una classifica pubblica è spesso la scelta tecnica migliore su un VPS. Il tipo halfvec di pgvector memorizza quindi valori a virgola mobile a 16 bit, dimezzando ulteriormente i byte, e crea l'indice tramite un'espressione:

CREATE INDEX ON chunks USING hnsw ((embedding::halfvec(768)) halfvec_cosine_ops);

Devi conoscere un limite prima di scegliere il modello. Il tipo vector di pgvector accetta fino a 16,000 dimensioni, ma gli indici HNSW e IVFFlat supportano solo 2,000 dimensioni. Oltre questo limite devi creare l'indice su un cast a halfvec, che supporta fino a 4,000 dimensioni, oppure non creare alcun indice.

Quali sono i costi di m ed ef_construction durante la creazione

HNSW (hierarchical navigable small world) è l'indice scelto sia da pgvector sia da Qdrant. È un grafo a livelli. Ogni vettore è un nodo collegato ai nodi vicini e la ricerca segue questi collegamenti verso la query, invece di leggere tutti i vettori.

m indica quanti collegamenti conserva ogni nodo. La documentazione di Faiss esprime la memoria di HNSW come (d * 4 + m * 2 * 4) byte per vettore e consiglia di mantenere m tra 4 e 64. Esegui questo calcolo con 768 dimensioni e un milione di vettori.

ChartCost of raising m at 768 dimensions, 1 million vectors
The data behind this chart
[
  {
    "label": "m = 8",
    "link_bytes_per_vector": 64,
    "total_gib": 2.92
  },
  {
    "label": "m = 16 (default)",
    "link_bytes_per_vector": 128,
    "total_gib": 2.98
  },
  {
    "label": "m = 32",
    "link_bytes_per_vector": 256,
    "total_gib": 3.1
  },
  {
    "label": "m = 64",
    "link_bytes_per_vector": 512,
    "total_gib": 3.34
  }
]

L'aspetto più utile è la dimensione della differenza. Passare dal valore predefinito m = 16 a m = 64 aggiunge 512 byte di collegamenti per vettore, a fronte di 3072 byte di dati del vettore. Il totale passa quindi da 2.98 GiB a 3.34 GiB. Si tratta di circa il 12 percento. Con queste dimensioni, m non è il parametro che determina il consumo di memoria. Sono i vettori a occupare la maggior parte della memoria.

Il costo reale di m riguarda il tempo di creazione e di inserimento, perché per posizionare un nodo è necessario trovare e collegare quel numero di vicini. ef_construction indica la dimensione dell'elenco di candidati che il processo di creazione valuta quando posiziona ogni nodo. Aumentarlo produce un grafo migliore, ma rallenta la creazione. Non modifica in alcun modo la dimensione dell'indice completato.

SET maintenance_work_mem = '4GB';
SET max_parallel_maintenance_workers = 7;
CREATE INDEX ON chunks USING hnsw (embedding vector_cosine_ops)
  WITH (m = 16, ef_construction = 64);

maintenance_work_mem è l'impostazione che determina se la creazione richiede minuti o ore, perché pgvector costruisce il grafo in memoria quando la memoria disponibile è sufficiente. Quando non lo è più, pgvector lo segnala:

NOTICE:  hnsw graph no longer fits into maintenance_work_mem after 100000 tuples
DETAIL:  Building will take significantly more time.
HINT:  Increase maintenance_work_mem to speed up builds.

Questo avviso è la riga più utile stampata da pgvector. Indica che la creazione è passata a un percorso molto più lento. Annulla quindi l'operazione, aumenta l'impostazione oltre il valore di RAM calcolato in precedenza e avvia di nuovo la creazione. Controlla l'avanzamento da una seconda sessione:

SELECT phase, round(100.0 * blocks_done / nullif(blocks_total, 0), 1) AS pct
FROM pg_stat_progress_create_index;

HNSW indica initializing e poi loading tuples. Se la creazione resta a una percentuale bassa per molto tempo e il disco non è occupato, il problema è maintenance_work_mem, non una query bloccata.

Ci sono due aspetti da considerare nella pianificazione. Il README di pgvector indica che HNSW «ha tempi di creazione più lunghi e usa più memoria» rispetto a IVFFlat, ma offre in cambio un compromesso migliore tra velocità e recall. Inoltre, HNSW può essere creato su una tabella vuota, mentre IVFFlat deve eseguire prima k-means su dati rappresentativi. Crearlo su una tabella vuota produce quindi un recall ridotto. In uno schema nuovo, HNSW è l'indice che puoi creare in anticipo.

ef_search: il parametro da regolare dopo la build

m e ef_construction sono incorporati nell'indice. ef_search no. Questo parametro determina quanti candidati la ricerca conserva durante l'attraversamento del grafo e può essere modificato per sessione o per query.

SET hnsw.ef_search = 100;
SELECT id, body FROM chunks ORDER BY embedding <=> $1 LIMIT 5;

Il valore predefinito è 40. Aumentandolo, aumenta il recall e aumenta anche la latenza. Riducendolo, diminuiscono entrambi. È l'unico parametro del recall che puoi modificare senza ricostruire l'indice. Regolalo quindi usando un insieme fisso di query con risposte corrette già note e interrompi la regolazione quando il recall smette di migliorare.

Esiste un problema. ef_search interagisce male con una clausola WHERE selettiva, perché l'indice restituisce un numero fisso di candidati e il filtro viene applicato in seguito. Un filtro che esclude la maggior parte delle righe può lasciarti con meno di LIMIT risultati, anche se le righe corrispondenti sono presenti nella tabella. pgvector 0.8 risolve il problema con le scansioni iterative:

SET hnsw.iterative_scan = relaxed_order;

L'indice viene quindi sottoposto a nuove scansioni per recuperare altri candidati finché il limite non viene raggiunto, fino a hnsw.max_scan_tuples, il cui valore predefinito è 20000. strict_order mantiene l'ordinamento esatto in base alla distanza, ma richiede più risorse. Questa funzionalità non è disponibile nel pacchetto Ubuntu 0.6.0. Le righe mancanti quando è attivo un filtro sono il modo in cui te ne accorgi.

Perché l'indice deve rientrare nella RAM

Una ricerca HNSW consiste nell'attraversare un grafo. Ogni salto legge un nodo memorizzato in una posizione non correlata a quella del nodo precedente, quindi il modello di accesso è quasi casuale e il read-ahead non è utile. Quando il grafo risiede nella RAM, ogni salto è un accesso alla memoria. Quando non vi risiede, un salto può diventare una lettura da disco; una ricerca che accede a qualche centinaio di nodi può quindi richiedere qualche centinaio di letture.

La documentazione di Qdrant lo esprime in modo chiaro: "se si memorizza nella RAM la metà dei vettori, la latenza della ricerca raddoppia all'incirca". La pianificazione deve partire da questo presupposto.

Quando l'indice non può realmente rientrare nella RAM, ogni opzione comporta un compromesso che va scelto consapevolmente.

  • Eseguire il memory mapping dei vettori, in modo che il sistema operativo memorizzi nella cache le pagine utilizzate più spesso e lasci sul disco quelle meno utilizzate. Per ottenere prestazioni accettabili, è necessario utilizzare storage NVMe (non-volatile memory express) veloce.
  • Quantizzare i vettori, memorizzando ogni dimensione in un byte invece di quattro. In questo modo i byte occupati dai vettori si riducono di quattro volte, con una perdita di recall contenuta ma misurabile.
  • Convertire i vettori in halfvec con pgvector: i byte si dimezzano, con una perdita di recall inferiore rispetto alla quantizzazione a un byte.
  • Usare un modello più piccolo per generare gli embedding. È la correzione meno costosa e quella che spesso viene esclusa, perché richiede di generare nuovamente gli embedding dell'intero corpus.

Modalità di errore e stringhe visualizzate

could not open extension control file. Il pacchetto pgvector per la versione major in esecuzione di Postgres non è installato. Visualizza la versione con sudo -u postgres psql -tAc 'SHOW server_version' e installa il postgresql-NN-pgvector corrispondente.

ERROR: expected 768 dimensions, not 1536. Il tipo della colonna e il modello non corrispondono. Hai cambiato il modello di embedding e non hai rigenerato gli embedding. Non esiste una correzione parziale: i vettori generati da due modelli diversi non sono affatto confrontabili, quindi è necessario rigenerare ogni riga.

La query è lenta e EXPLAIN mostra una scansione sequenziale. La classe di operatori dell'indice e l'operatore della query non corrispondono. vector_cosine_ops gestisce soltanto <=>. Esegui EXPLAIN ANALYZE sulla query e cerca Index Scan using ... on chunks. Se invece visualizzi Seq Scan on chunks, ricrea l'indice con la classe di operatori corrispondente all'operatore usato effettivamente dalla query.

Meno righe di LIMIT, con una clausola WHERE presente. È il problema del filtraggio descritto sopra. Aumenta hnsw.ef_search oppure passa a pgvector 0.8 e imposta hnsw.iterative_scan.

La creazione dell'indice termina perché il processo viene arrestato, senza errori in psql. Se maintenance_work_mem è impostato su gran parte della memoria della macchina, mentre anche shared_buffers e l'applicazione richiedono memoria, il kernel attiva l'out-of-memory killer. sudo dmesg -T | grep -i 'killed process' mostra la riga che identifica postgres. Riduci il valore dell'impostazione oppure crea l'indice su un piano con più risorse e ripristina il dump.

Scegliere

Se utilizzi già Postgres e hai meno di qualche milione di vettori, usa pgvector. L'indice risiede accanto ai dati, il filtraggio è una clausola WHERE e il backup esistente include già questi dati. Se l'indice è abbastanza grande da richiedere un proprio limite di memoria, oppure ti serve un filtraggio intenso dei payload, esegui Qdrant accanto a Postgres e accetta di gestire un secondo servizio.

Al di sotto di circa centomila vettori, misura la scansione a forza bruta prima di installare qualsiasi componente. A queste dimensioni, una ricerca esaustiva con recall perfetto e senza fase di build non è un compromesso. È la scelta corretta. Usare invece un indice approssimato significa introdurre attività di tuning e pressione sulla RAM in cambio di millisecondi che non stavi spendendo.

FAQ

Mi serve un database vettoriale dedicato oppure Postgres è sufficiente?

Se i dati sono già in Postgres, pgvector è sufficiente molto più a lungo di quanto suggerisca la maggior parte dei confronti. Memorizza i vettori in una colonna ordinaria, quindi una ricerca filtrata usa una clausola WHERE e l'indice rientra nei backup esistenti. Passa a un archivio dedicato come Qdrant quando il carico vettoriale richiede un limite di memoria separato oppure quando servono il filtraggio del payload e la quantizzazione che pgvector non offre.

Quanti vettori può contenere un singolo VPS?

Calcolalo invece di procedere per stime, usando number_of_vectors * dimensions * 4 bytes * 1.5. Un milione di vettori con 768 dimensioni occupa circa 4.3 GiB, quindi un piano da 8 GB li contiene lasciando memoria disponibile per Postgres. Un milione di vettori con 3072 dimensioni occupa circa 17 GiB e richiede un piano molto più grande. Il fattore che incide maggiormente è la dimensione del modello di embedding, quindi scegli il modello considerando il relativo costo in memoria.

Perché la ricerca vettoriale è lenta quando l'indice si trova sulla stessa macchina?

Su una singola macchina la rete non è il problema, quindi controlla i due fattori rilevanti. Per prima cosa, misura separatamente la durata della chiamata per generare l'embedding, perché la generazione del vettore di query sulla CPU richiede spesso molto più tempo della ricerca. Poi verifica che l'indice sia in RAM. Una ricerca HNSW attraversa casualmente un grafo; quando il grafo eccede la memoria e viene spostato su disco, ogni passaggio può diventare una lettura da disco. Le indicazioni di Qdrant stesso mostrano che dimezzare i vettori mantenuti in RAM raddoppia all'incirca la latenza della ricerca.

Devo creare un indice HNSW?

Non sotto circa centomila vettori. Una scansione esaustiva legge n * d * 4 byte per query: con 100,000 vettori di 768 dimensioni sono 307 MB. Una CPU moderna può elaborare questo flusso in decine di millisecondi, con richiamo perfetto e senza una fase di creazione dell'indice. Misura prima la scansione sul tuo hardware. Crea l'indice quando il tempo misurato per la scansione è realmente troppo alto, non perché lo consiglia un articolo di benchmark.

Quanto mi costa effettivamente aumentare m?

Incide sul tempo di creazione e sul tempo di inserimento, molto più che sulla memoria. Con 768 dimensioni, passare dal valore predefinito m = 16 a m = 64 aggiunge 512 byte di collegamenti del grafo per vettore, rispetto ai 3072 byte dei dati vettoriali, quindi la memoria totale aumenta di circa il 12 percento. Ogni inserimento, però, deve trovare e collegare un numero di vicini quattro volte maggiore. Regola prima ef_search, perché puoi modificarlo senza costi e senza ricreare l'indice.

#vector-database#rag#pgvector#qdrant#self-hosting