跳到主要内容
AI News HubLIVE
来源内容 · 翻译待补全6 分钟阅读

待翻译:pgvector vs. Dedicated Vector Databases: A Production Benchmark

文章摘要

AI 服务暂时不可用,以下为来源摘要,待恢复后补全翻译:Run this on a typical laptop and both systems will report latencies in the low single-digit to tens of milliseconds range, with the exact numbers depending heavily on your CPU, available RAM, and whatever else is running on your machine at the time.

来源Machine Learning Mastery作者: Shittu Olumide
待翻译:pgvector vs. Dedicated Vector Databases: A Production Benchmark
报告错误

纠错通道尚未开通,可先复制下方文章信息留存。

查看更正说明
直接读正文

AI 服务暂时不可用,以下为来源正文,待恢复后补全翻译。

pgvector vs. Dedicated Vector Databases: A Production Benchmark - MachineLearningMastery.com pgvector vs. Dedicated Vector Databases: A Production Benchmark - MachineLearningMastery.com In this article, you will learn how pgvector compares to dedicated vector databases in terms of real, measured performance, and how to decide which approach suits your production workload. Topics we will cover include: What pgvector and dedicated vector databases are, and how they differ architecturally. How to set up and run a hands-on benchmark comparing pgvector and Qdrant on your own machine. What production-scale benchmark data actually shows about throughput, latency, and the tradeoffs between the two approaches. A team adds pgvector to their existing Postgres database on a Tuesday afternoon, embeddings and relational data sitting in the same table, one query doing both the filter and the similarity search. Six months later, a vendor pitch lands in their inbox insisting they need to migrate to a “real” vector database before they hit a wall. No benchmark accompanies the claim in either direction, just confidence. That gap — strong opinions with no numbers behind them — is exactly what this article exists to close. What pgvector Actually Is pgvector is an open-source Postgres extension, currently at version 0.8.6 and supporting Postgres 13 and later, according to its official GitHub repository. It adds a native vector column type to Postgres, along with exact and approximate nearest-neighbor search, directly inside the database you’re likely already running. What that means in practice: a vector embedding lives in the same table as the record it describes, in the same row if you want, queried with the same SQL you already know. There’s no separate service to stand up, no second system to keep in sync, no network hop between your relational data and your vector data. pgvector supports single-precision, half-precision, binary, and sparse vector types, and five distance functions — L2, inner product, cosine, L1, and Hamming — covering essentially every distance metric a real embedding model uses. What a Dedicated Vector Database Is A dedicated vector database is a separate, purpose-built service, run as its own process with its own storage engine, designed from the ground up around vector search rather than having it added onto a general-purpose database. It’s a genuinely different architectural choice, not just a different product, since it means a new system to deploy, monitor, back up, and keep synchronized with whatever holds your actual source of truth. Here’s a plain rundown of the five most commonly reached for: Database Best for Pinecone A fully managed, zero-setup cloud database, best for low-ops enterprise scale Qdrant A high-performance Rust-based database, best for fast queries with complex JSON payload filtering Weaviate An open-source, AI-native system, best for built-in vectorization and hybrid BM25 search Milvus A distributed, GPU-accelerated database, best for massive, billion-scale enterprise workloads ChromaDB A lightweight, embedded database, best for local prototyping and Python development Prerequisites Everything in this tutorial runs entirely on your own machine — no cloud account required. You’ll need Docker and Docker Compose installed, Python 3.10 or later, roughly 8GB of free RAM, and basic comfort reading SQL and Python. The hands-on benchmark below is sized to run comfortably on a laptop. Setting Up pgvector Locally Start with a Docker Compose file that runs Postgres with pgvector already built in: 1 2 3 4 5 6 7 8 9 # docker-compose.yml services: postgres: image: pgvector/pgvector:pg17 ports: - "5432:5432" environment: - POSTGRES_PASSWORD=benchmark - POSTGRES_DB=vectorbench Run docker compose up -d, then connect and set up the extension and table: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import psycopg2 conn = psycopg2.connect( host="localhost", port=5432, dbname="vectorbench", user="postgres", password="benchmark", ) conn.autocommit = True cur = conn.cursor() # Enables the vector type and index access methods for this database cur.execute("CREATE EXTENSION IF NOT EXISTS vector;") # 1536 dimensions matches OpenAI's text-embedding-3-small, a realistic # stand-in for a genuine production embedding model cur.execute(""" CREATE TABLE IF NOT EXISTS items ( id bigserial PRIMARY KEY, embedding vector(1536) ); """) pgvector/pgvector:pg17 is the official Docker image that bundles the extension with Postgres 17, so there’s no separate compile step. CREATE EXTENSION IF NOT EXISTS vector is the one-time step that registers the vector type and its index methods in this specific database, per the pgvector documentation. Setting Up Qdrant Locally For the dedicated-database side of the comparison, run Qdrant with a single Docker command: 1 docker run -d --name qdrant-bench -p 6333:6333 -p 6334:6334 qdrant/qdrant Then connect from Python and create a matching collection: 1 2 3 4 5 6 7 8 9 from qdrant_client import QdrantClient from qdrant_client.models import Distance, VectorParams qdrant = QdrantClient(url="http://localhost:6333") qdrant.create_collection( collection_name="items", vectors_config=VectorParams(size=1536, distance=Distance.COSINE), ) Qdrant is worth picking specifically for this comparison because it’s the subject of the most detailed, methodologically transparent public benchmark against pgvector available, which this article draws on directly in a later section. VectorParams(size=1536, distance=Distance.COSINE) mirrors the same dimensionality and distance metric used in the pgvector table above, which matters — a fair comparison needs both systems measuring the same thing. Generating and Loading a Real Test Dataset With both systems running, generate a realistic set of synthetic vectors and load them into each: 1 2 3 4 5 6 7 8 9 10 11 import numpy as np NUM_VECTORS = 100_000 DIM = 1536 rng = np.random.default_rng(seed=42) vectors = rng.normal(size=(NUM_VECTORS, DIM)).astype(np.float32) # Real embedding models produce normalized vectors, so normalize # these to length 1 for a realistic cosine-distance comparison vectors /= np.linalg.norm(vectors, axis=1, keepdims=True) Load into pgvector using pgvector-python’s adapter, which lets psycopg2 accept NumPy arrays directly: 1 2 3 4 5 6 7 from pgvector.psycopg2 import register_vector from psycopg2.extras import execute_values register_vector(conn) data = [(v,) for v in vectors] execute_values(cur, "INSERT INTO items (embedding) VALUES %s", data, template="(%s)") Load the same vectors into Qdrant in batches: 1 2 3 4 5 6 7 8 9 from qdrant_client.models import PointStruct batch_size = 1000 for start in range(0, NUM_VECTORS, batch_size): batch = vectors[start:start + batch_size] points = [ PointStruct(id=start + i, vector=batch[i].tolist()) for i in range(len(batch)) ] qdrant.upsert(collection_name="items", points=points) register_vector(conn) is the key line on the pgvector side — it teaches psycopg2 how to convert a NumPy array into the format pgvector expects, so execute_values can insert raw arrays without manual string formatting. On the Qdrant side, upsert is batched deliberately at 1,000 vectors per call rather than one giant request, since Qdrant’s client, like most vector database clients, performs better with reasonably sized batches than either single inserts or one massive payload. Building the Indexes Create an HNSW index in pgvector, straight from pgvector’s own indexing documentation: 1 2 3 4 cur.execute( "CREATE INDEX ON items USING hnsw (embedding vector_cosine_ops) " "WITH (m = 16, ef_construction = 64);" ) Apply matching HNSW parameters in Qdrant, so both systems are running the same underlying algorithm with the same settings: 1 2 3 4 5 6 from qdrant_client.models import HnswConfigDiff qdrant.update_collection( collection_name="items", hnsw_config=HnswConfigDiff(m=16, ef_construct=64), ) m controls the maximum number of connections per node in the HNSW graph, and ef_construction controls how thorough the search is while building it. Both are set to 16 and 64 here — pgvector’s own defaults — deliberately mirrored on the Qdrant side. This matters more than it might seem: comparing two systems running different index parameters isn’t a benchmark, it’s two separate, incomparable results. Matching them is what makes the numbers in the next section mean anything. Running the Benchmark Here’s the actual measurement script, querying both systems with a fresh, held-out set of query vectors that weren’t part of the loaded dataset: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 import time import statistics query_vectors = rng.normal(size=(200, DIM)).astype(np.float32) query_vectors /= np.linalg.norm(query_vectors, axis=1, keepdims=True) def benchmark_pgvector(cur, query_vectors, ef_search=64, limit=10): cur.execute(f"SET hnsw.ef_search = {ef_search};") latencies = [] for qv in query_vectors: start = time.perf_counter() cur.execute( "SELECT id FROM items ORDER BY embedding %s LIMIT %s", (qv, limit), ) cur.fetchall() latencies.append((time.perf_counter() - start) * 1000) # ms return latencies def benchmark_qdrant(client, query_vectors, limit=10): latencies = [] for qv in query_vectors: start = time.perf_counter() client.query_points(collection_name="items", query=qv.tolist(), limit=limit) latencies.append((time.perf_counter() - start) * 1000) return latencies def summarize(name, latencies, total_time): latencies_sorted = sorted(latencies) p50 = statistics.median(latencies_sorted) p95 = latencies_sorted[int(len(latencies_sorted) * 0.95)] p99 = latencies_sorted[int(len(latencies_sorted) * 0.99)] qps = len(latencies) / total_time print(f"{name}: p50={p50:.2f}ms p95={p95:.2f}ms p99={p99:.2f}ms QPS={qps:.1f}") start = time.perf_counter() pg_latencies = benchmark_pgvector(cur, query_vectors) summarize("pgvector", pg_latencies, time.perf_counter() - start) start = time.perf_counter() qdrant_latencies = benchmark_qdrant(qdrant, query_vectors) summarize("Qdrant", qdrant_latencies, time.perf_counter() - start) A few things worth understanding here. The 200 query vectors are freshly generated, not pulled from the 100,000 already loaded, since querying with a vector that’s literally already in the index trivially returns itself at zero distance, which would make the benchmark meaningless. SET hnsw.ef_search = 64 controls how thorough pgvector’s search is at query time — higher values trade speed for better recall, and setting it explicitly rather than relying on the default makes the test reproducible. embedding %s is pgvector’s cosine distance operator, matching the Distance.COSINE setting used on the Qdrant collection. And client.query_points() is Qdrant’s current recommended search method, replacing the older search() call in recent versions of the client. The summarize function computes the same three percentiles — p50, p95, p99 — plus queries-per-second for both systems, so the output is directly comparable line for line. A simple bar chart mockup showing p50, p95, and p99 latency for pgvector and Qdrant (click to enlarge) What the Local Results Actually Show Run this on a typical laptop and both systems will report latencies in the low single-digit to tens of milliseconds range, with the exact numbers depending heavily on your CPU, available RAM, and whatever else is running on your machine at the time. That variability is the honest point of this section: 100,000 vectors on a laptop is a fine way to learn the mechanics of both systems and confirm your setup works correctly, but it’s genuinely too small a scale to draw a re [truncated for AI cost control]

展开要点与分析

文章情报

工程师进阶

要点

  • AI 服务暂时不可用,系统已先保留来源内容与降级元数据。
  • Run this on a typical laptop and both systems will report latencies in the low single-digit to tens of milliseconds range, with the exact numbers depending heavily on your CPU, av…

要点与分析由自动化流程生成,可能有误,请结合原始来源核实。