Leitfaden zur Auswahl von Vektordatenbanken für KI-Anwendungen
Vergleich von Pinecone, Weaviate, Qdrant, Milvus und pgvector für KI-Anwendungen: Indexierung, Abfrageleistung, Filterung, Betriebskomplexität.

Jede KI-Anwendung, die Embeddings nutzt, braucht eine Vektordatenbank – doch die Wahl zwischen einem verwalteten Dienst, einer eigenständigen Vektordatenbank und einer Postgres-Erweiterung wirkt sich massiv auf Architektur, Betriebsaufwand und die Fähigkeit aus, Vektorsuche mit klassischen Abfragen zu kombinieren.
Der Markt ist regelrecht explodiert vor lauter Optionen. Jede geht andere Kompromisse zwischen Abfrageleistung, Filterfunktionen, Skalierbarkeit und Betriebseinfachheit ein. Die falsche Wahl erzeugt Migrationsschmerzen, die mit wachsender Embedding-Sammlung immer größer werden.
Vektorindex-Typen verstehen
Das zentrale Unterscheidungsmerkmal zwischen Vektordatenbanken ist ihre Indexierungsstrategie. Sie bestimmt Abfragegeschwindigkeit, Recall-Genauigkeit, Speicherverbrauch und Build-Zeit.
// ❌ Brute-force search — O(n) per query
function bruteForceSearch(
query: number[],
vectors: number[][],
k: number
): number[] {
const distances = vectors.map((v, i) => ({
index: i,
distance: cosineSimilarity(query, v),
}));
distances.sort((a, b) => b.distance - a.distance);
return distances.slice(0, k).map((d) => d.index);
}
// Works for < 10K vectors. Unusable at scale.// ✅ HNSW index — O(log n) per query with high recall
interface HNSWConfig {
// Number of connections per node in the graph
// Higher = better recall, more memory
M: number;
// Size of the dynamic candidate list during construction
// Higher = better index quality, slower build
efConstruction: number;
// Size of candidate list during search
// Higher = better recall, slower search
efSearch: number;
}
const balancedConfig: HNSWConfig = {
M: 16,
efConstruction: 200,
efSearch: 100,
};
const highRecallConfig: HNSWConfig = {
M: 32,
efConstruction: 400,
efSearch: 200,
// 99%+ recall, ~2x memory vs balanced
};
const lowLatencyConfig: HNSWConfig = {
M: 12,
efConstruction: 100,
efSearch: 50,
// ~95% recall, fastest queries
};
// IVF index — partition-based approach
interface IVFConfig {
// Number of partitions (clusters)
nlist: number;
// Number of partitions to search per query
nprobe: number;
}
// IVF suits large datasets where memory is constrained
// HNSW suits workloads requiring consistent low latencyHNSW-Graphen (Hierarchical Navigable Small World) dominieren, weil sie für die meisten Workloads das beste Verhältnis von Recall zu Latenz bieten. IVF (Inverted File Index) benötigt weniger Speicher, erfordert aber ein sorgfältiges Abstimmen der Anzahl an Partitionen und Probes. Produktquantisierung (PQ) komprimiert Vektoren für sehr große Datensätze auf Kosten eines gewissen Recall-Verlusts.
Vektordatenbank-Optionen im Vergleich
Jede Datenbank besetzt einen anderen Punkt im Spannungsfeld zwischen Betriebseinfachheit, Abfragefunktionen und Skalierbarkeit.
interface VectorDBComparison {
name: string;
type: "managed" | "self-hosted" | "extension";
indexTypes: string[];
filtering: "pre-filter" | "post-filter" | "hybrid";
scalarFields: boolean;
multiTenancy: "native" | "namespace" | "manual";
operationalComplexity: "low" | "medium" | "high";
}
const databases: VectorDBComparison[] = [
{
name: "Pinecone",
type: "managed",
indexTypes: ["proprietary"],
filtering: "hybrid",
scalarFields: true,
multiTenancy: "namespace",
operationalComplexity: "low",
},
{
name: "Weaviate",
type: "self-hosted",
indexTypes: ["HNSW", "flat"],
filtering: "pre-filter",
scalarFields: true,
multiTenancy: "native",
operationalComplexity: "medium",
},
{
name: "Qdrant",
type: "self-hosted",
indexTypes: ["HNSW"],
filtering: "hybrid",
scalarFields: true,
multiTenancy: "manual",
operationalComplexity: "medium",
},
{
name: "pgvector",
type: "extension",
indexTypes: ["IVFFlat", "HNSW"],
filtering: "pre-filter",
scalarFields: true,
multiTenancy: "manual",
operationalComplexity: "low",
},
];pgvector: Wenn deine Daten bereits in Postgres liegen
Für Anwendungen, bei denen die Vektorsuche nur eine von vielen Funktionen ist und Postgres bereits im Einsatz ist, erspart pgvector eine komplette zusätzliche Infrastrukturabhängigkeit.
-- Setup pgvector
CREATE EXTENSION IF NOT EXISTS vector;
-- Create table with embedding column
CREATE TABLE documents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
title TEXT NOT NULL,
content TEXT NOT NULL,
embedding vector(1536),
category TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT now(),
tenant_id UUID NOT NULL
);
-- HNSW index for cosine similarity
CREATE INDEX ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 200);
-- Combined vector + scalar query — single database
SELECT
id,
title,
1 - (embedding <=> $1::vector) AS similarity
FROM documents
WHERE tenant_id = $2
AND category = $3
AND created_at > NOW() - INTERVAL '90 days'
ORDER BY embedding <=> $1::vector
LIMIT 10;// TypeScript integration with pgvector
import { Pool } from "pg";
interface SearchResult {
id: string;
title: string;
similarity: number;
}
async function semanticSearch(
pool: Pool,
queryEmbedding: number[],
tenantId: string,
options: {
category?: string;
limit?: number;
minSimilarity?: number;
} = {}
): Promise<SearchResult[]> {
const { category, limit = 10, minSimilarity = 0.7 } =
options;
const embeddingStr = `[${queryEmbedding.join(",")}]`;
const conditions = [
"tenant_id = $2",
`1 - (embedding <=> $1::vector) >= $3`,
];
const params: unknown[] = [
embeddingStr,
tenantId,
minSimilarity,
];
if (category) {
conditions.push(`category = $${params.length + 1}`);
params.push(category);
}
const query = `
SELECT id, title,
1 - (embedding <=> $1::vector) AS similarity
FROM documents
WHERE ${conditions.join(" AND ")}
ORDER BY embedding <=> $1::vector
LIMIT ${limit}
`;
const result = await pool.query(query, params);
return result.rows;
}Der Vorteil liegt in der transaktionalen Konsistenz: Du kannst ein Dokument und sein Embedding in derselben Transaktion einfügen, Vektoren mit gewöhnlichen SQL-Joins und -Filtern abfragen und alles mit deinem bestehenden Postgres-Tooling verwalten.
Dedizierte Vektordatenbanken: Wenn die Skalierung es erfordert
Bei Millionen von Vektoren, einer geforderten Latenz unter 10 ms oder Anforderungen wie Echtzeit-Indexaktualisierungen bei hohem Durchsatz zieht eine dedizierte Vektordatenbank vorbei.
// Qdrant client example — dedicated vector DB features
import { QdrantClient } from "@qdrant/js-client-rest";
const client = new QdrantClient({
url: "http://localhost:6333",
});
// Create collection with optimized settings
await client.createCollection("documents", {
vectors: {
size: 1536,
distance: "Cosine",
},
optimizers_config: {
indexing_threshold: 20000,
memmap_threshold: 50000,
},
hnsw_config: {
m: 16,
ef_construct: 200,
full_scan_threshold: 10000,
},
});
// Search with payload filtering
const results = await client.search("documents", {
vector: queryEmbedding,
filter: {
must: [
{
key: "tenant_id",
match: { value: tenantId },
},
{
key: "category",
match: { any: ["engineering", "ai"] },
},
],
must_not: [
{
key: "status",
match: { value: "archived" },
},
],
},
limit: 10,
with_payload: true,
score_threshold: 0.7,
});Entscheidungsrahmen
Die Wahl hängt letztlich von deiner Skalierung, deinen Abfragemustern und deiner betrieblichen Kapazität ab.
interface DecisionInput {
vectorCount: number;
queryLatencyTarget: string;
existingDatabase: string;
needsJoins: boolean;
teamSize: number;
budget: string;
}
function recommendDatabase(
input: DecisionInput
): string {
// Under 1M vectors + already using Postgres + needs joins
if (
input.vectorCount < 1_000_000 &&
input.existingDatabase === "postgres" &&
input.needsJoins
) {
return "pgvector — minimal operational overhead, " +
"transactional consistency, SQL joins";
}
// Over 1M vectors or sub-10ms latency requirement
if (
input.vectorCount >= 1_000_000 ||
input.queryLatencyTarget === "sub-10ms"
) {
if (input.teamSize <= 3) {
return "Pinecone — managed service, no ops burden";
}
return "Qdrant/Weaviate — self-hosted for control " +
"and cost efficiency at scale";
}
// Default: start simple
return "pgvector — start here, migrate when you " +
"hit measured performance limits";
}Die wichtigsten Erkenntnisse
HNSW-Indizes bieten für die meisten Workloads das beste Verhältnis von Recall zu Latenz, aber das Abstimmen von Parametern wie M, efConstruction und efSearch tauscht Speicher und Build-Zeit direkt gegen Abfragequalität. pgvector erspart eine Infrastrukturabhängigkeit, wenn die Vektorsuche nur eine von vielen Funktionen ist – du erhältst transaktionale Konsistenz, SQL-Joins und dein bestehendes Postgres-Tooling. Dedizierte Vektordatenbanken wie Qdrant und Weaviate ziehen bei Millionen von Vektoren vorbei, wenn Latenzanforderungen unter 10 ms, Echtzeit-Indexaktualisierungen und native Multi-Tenancy gefragt sind. Die Filterstrategie spielt eine wichtige Rolle: Pre-Filtering wendet skalare Bedingungen vor der Vektorsuche an (präzise, kann aber die nächstgelegenen Vektoren verfehlen), Post-Filtering durchsucht erst alle Vektoren und filtert danach die Ergebnisse (kann weniger Ergebnisse liefern als angefordert), und hybride Ansätze balancieren beides aus. Starte mit pgvector, wenn du bereits Postgres nutzt und weniger als eine Million Vektoren hast – die Migration zu einer dedizierten Vektordatenbank ist unkompliziert, sobald du gemessene Leistungsgrenzen erreichst, aber eine verfrühte Migration bringt operative Komplexität ohne Nutzen.


