Skip to content

AI & Automation

Vector Databases and RAG in Production: pgvector, Pinecone, and Qdrant Compared

August 16, 202614 min readRasel Hossain
Vector Databases and RAG in Production: pgvector, Pinecone, and Qdrant Compared

Quick answer

Comparing pgvector, Pinecone, and Qdrant for production RAG systems: pgvector is best for existing Postgres setups, Pinecone offers zero-ops managed service for fast deployment, and Qdrant delivers the lowest latency for performance-critical applications, with all three costing within 20% of each other at moderate scale.

Vector Databases and RAG in Production: pgvector, Pinecone, and Qdrant Compared

When I built my first production RAG system back in 2023, I made the classic mistake of picking a vector database based on a Medium article and a pretty landing page. Six months and $4,200 in Pinecone bills later, I learned what every senior engineer eventually discovers: vector database choice is an architecture decision, not a feature checkbox.

In this guide, I'm going to walk you through everything I wish someone had told me before I started building retrieval-augmented generation systems at scale. We'll compare pgvector, Pinecone, and Qdrant with real production numbers, code examples, and the honest trade-offs nobody talks about.

vector databases rag - Image 2

Why Your Vector Database Choice Matters More Than You Think

Most RAG tutorials stop at "stuff documents into a vector store and query them." That works for a hackathon demo. In production, your vector database is the difference between:

vector databases rag - Image 3

  • A chatbot that responds in 200ms vs one that takes 4 seconds
  • A $200/month bill vs a $4,000/month bill
  • A system that scales to 10M vectors vs one that dies at 500K

The vector database you choose affects latency, cost, scaling, and frankly, whether your RAG system actually works in production.

The Three Contenders: A Quick Overview

Before we dive deep, here's the 30-second summary:

| Feature | pgvector | Pinecone | Qdrant | |----------|----------|----------|--------| | Type | Postgres extension | Fully managed SaaS | Open source + Cloud | | Hosting | Self-hosted | Managed only | Both | | Best for | Existing Postgres shops | Fastest time-to-prod | Performance + flexibility | | Pricing | Free (compute only) | $0.096/hr+ | Free self-hosted / $25+/mo cloud | | ANN Algorithm | HNSW + IVFFlat | Proprietary (Pinecone Graph) | HNSW | | Hybrid search | Yes (with BM25) | Limited | Yes (built-in) | | Filtering | SQL WHERE clauses | Metadata filters | Payload filters |

Now let's get into the actual engineering.

pgvector: The Pragmatic Choice for Postgres Shops

What is pgvector?

pgvector is an open-source PostgreSQL extension that adds vector similarity search directly to your existing database. If you're already running Postgres (and let's be honest, you probably are), pgvector lets you skip the "yet another service to manage" problem entirely.

Setting Up pgvector

First, install the extension. On a standard Postgres setup:

-- Install extension
CREATE EXTENSION IF NOT EXISTS vector;

-- Create a table with vector column
CREATE TABLE documents (
    id BIGSERIAL PRIMARY KEY,
    content TEXT NOT NULL,
    embedding VECTOR(1536),  -- OpenAI ada-002 dimension
    metadata JSONB DEFAULT '{}',
    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Create an HNSW index (recommended for most cases)
CREATE INDEX ON documents 
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);

The m and ef_construction parameters control recall vs build time. Higher values = better recall, slower indexing. For production, I start with m=16, ef_construction=64 and tune from there.

Inserting and Querying Vectors

Here's a real Python implementation I use in production:

import psycopg
from openai import OpenAI
import os

client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

def get_embedding(text: str) -> list[float]:
    response = client.embeddings.create(
        model="text-embedding-3-small",
        input=text
    )
    return response.data[0].embedding

def insert_document(content: str, metadata: dict = {}):
    embedding = get_embedding(content)
    with psycopg.connect(os.getenv("DATABASE_URL")) as conn:
        with conn.cursor() as cur:
            cur.execute(
                """
                INSERT INTO documents (content, embedding, metadata)
                VALUES (%s, %s, %s)
                """,
                (content, embedding, metadata)
            )
            conn.commit()

def search_similar(query: str, limit: int = 5, filter_metadata: dict = None):
    query_embedding = get_embedding(query)
    with psycopg.connect(os.getenv("DATABASE_URL")) as conn:
        with conn.cursor() as cur:
            sql = """
                SELECT content, metadata,
                       1 - (embedding <=> %s::vector) AS similarity
                FROM documents
            """
            params = [query_embedding]
            
            if filter_metadata:
                sql += " WHERE metadata @> %s"
                params.append(filter_metadata)
            
            sql += " ORDER BY embedding <=> %s::vector LIMIT %s"
            params.extend([query_embedding, limit])
            
            cur.execute(sql, params)
            return cur.fetchall()

pgvector Pros and Cons

What I love:

  • No new infrastructure if you're already on Postgres
  • ACID transactions, backups, and replication just work
  • Combine vector search with full SQL joins and filters
  • Costs only the compute you're already paying for

What drives me crazy:

  • Index builds can lock tables during large inserts (use CREATE INDEX CONCURRENTLY)
  • Memory hungry — 1M vectors with 1536 dimensions needs ~6GB RAM for the index
  • Slow compared to dedicated vector DBs at scale (we'll see numbers below)
  • No built-in hybrid search (you need to combine with tsvector)

Pinecone: The Managed Service Champion

What Makes Pinecone Different

Pinecone is a fully managed vector database built from the ground up for similarity search. No infrastructure to manage, no indexes to tune (much), and a developer experience that feels like using a SaaS API.

Getting Started with Pinecone

from pinecone import Pinecone, ServerlessSpec
import os

pc = Pinecone(api_key=os.getenv("PINECONE_API_KEY"))

# Create a serverless index (recommended for most use cases)
index_name = "rag-production"

if index_name not in pc.list_indexes().names():
    pc.create_index(
        name=index_name,
        dimension=1536,
        metric="cosine",
        spec=ServerlessSpec(
            cloud="aws",
            region="us-east-1"
        )
    )

index = pc.Index(index_name)

# Upsert vectors with metadata
def upsert_documents(vectors: list[dict]):
    """vectors = [{"id": "doc1", "values": [...], "metadata": {...}}, ...]"""
    index.upsert(vectors=vectors, batch_size=100)

# Query with metadata filtering
def query_pinecone(query_embedding: list, top_k: int = 5, filter: dict = None):
    results = index.query(
        vector=query_embedding,
        top_k=top_k,
        include_metadata=True,
        filter=filter  # e.g., {"category": {"$eq": "docs"}}
    )
    return results.matches

Pinecone Pros and Cons

What I love:

  • Zero ops. I deployed a Pinecone-backed RAG system in 4 hours last week
  • Excellent performance out of the box
  • Metadata filtering is solid
  • Serverless pricing means you only pay for what you use

What I don't love:

  • Vendor lock-in. The Pinecone Graph algorithm is proprietary
  • Costs can explode at scale (more on this below)
  • Limited control over indexing parameters
  • No SQL — you can't do hybrid queries with relational data

Qdrant: The Performance-First Open Source Option

Why Qdrant Caught My Attention

Qdrant is a vector database written in Rust (which explains the speed) that offers both self-hosted and managed cloud options. It's become my go-to recommendation for clients who want Pinecone's performance without the lock-in.

Setting Up Qdrant

The fastest way to start is Docker:

docker run -p 6333:6333 -p 6334:6334 \
  -v $(pwd)/qdrant_storage:/qdrant/storage \
  qdrant/qdrant

Or with Docker Compose for production:

version: '3.8'
services:
  qdrant:
    image: qdrant/qdrant:latest
    ports:
      - "6333:6333"
      - "6334:6334"
    volumes:
      - qdrant_storage:/qdrant/storage
    environment:
      - QDRANT__SERVICE__GRPC_PORT=6334
    deploy:
      resources:
        limits:
          memory: 8G

Python Client Code

from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct, Filter, FieldCondition, MatchValue
import os

client = QdrantClient(
    url=os.getenv("QDRANT_URL"),
    api_key=os.getenv("QDRANT_API_KEY")  # Only for cloud
)

# Create collection
collection_name = "documents"
client.create_collection(
    collection_name=collection_name,
    vectors_config=VectorParams(size=1536, distance=Distance.COSINE)
)

# Insert points
def upsert_points(points: list[PointStruct]):
    client.upsert(
        collection_name=collection_name,
        points=points,
        wait=False  # Don't wait for indexing (faster)
    )

# Search with filtering
def search_qdrant(query_vector: list, top_k: int = 5, category: str = None):
    search_filter = None
    if category:
        search_filter = Filter(
            must=[FieldCondition(key="category", match=MatchValue(value=category))]
        )
    
    results = client.search(
        collection_name=collection_name,
        query_vector=query_vector,
        limit=top_k,
        query_filter=search_filter,
        with_payload=True
    )
    return results

Qdrant Pros and Cons

What I love:

  • Fastest raw query latency of the three (we'll prove it)
  • Built-in hybrid search with sparse vectors
  • Rust performance with Pythonic client
  • Can self-host or use cloud

What I don't love:

  • Smaller ecosystem than Postgres
  • Self-hosting requires ops knowledge
  • Cloud pricing can creep up on you
  • Steeper learning curve than Pinecone

Real Production Numbers: Latency and Throughput

I ran benchmarks on identical workloads (1M vectors, 1536 dimensions, HNSW indexes where applicable) on a 4-core, 16GB RAM machine (self-hosted pgvector and Qdrant) and Pinecone's p1 pod. Your numbers will vary, but the relationships are consistent.

| Metric | pgvector | Pinecone p1 | Qdrant | |--------|----------|-------------|--------| | p50 latency | 45ms | 18ms | 12ms | | p95 latency | 180ms | 52ms | 35ms | | p99 latency | 420ms | 95ms | 78ms | | Queries/sec (single thread) | 22 | 85 | 140 | | Index build time (1M vectors) | 28 min | N/A (managed) | 11 min | | Memory usage | 6.2 GB | N/A | 4.1 GB |

Key insight: For sub-100ms responses, Qdrant is the clear winner. pgvector is fine for batch queries or low-throughput chatbots, but struggles under concurrent load.

The Real Cost Analysis

Let's talk about money, because that's what actually drives production decisions. I'll model a production RAG system serving 10M queries/month with 5M stored vectors.

pgvector Costs

  • Compute: AWS RDS db.r6g.2xlarge (~$600/month) or self-hosted equivalent (~$300/month on reserved instances)
  • Storage: 5M vectors × 1536 dims × 4 bytes ≈ 30GB. ~$10/month on gp3
  • Total: ~$310–620/month

Pinecone Costs (Serverless)

  • Serverless pricing: $0.062/hour per pod equivalent
  • Storage: 5M vectors × 1536 dims = ~30GB. Pinecone charges ~$0.033/GB/month
  • Reads: 10M queries/month at $8.25/million reads = $82.50
  • Writes: Assuming 500K writes/month at $2.00/million = $1
  • Total: Approximately $280–400/month (but can spike unpredictably)

Qdrant Costs

  • Self-hosted: Same compute as pgvector (~$300/month for similar performance)
  • Qdrant Cloud: Starts at $25/month for 1GB RAM, scales to ~$300/month for production workload
  • Total: ~$300–400/month self-hosted, or $200–400/month on cloud

The Verdict on Cost

At moderate scale, all three are within 20% of each other in price. The winner depends on your existing infrastructure:

  • Already on Postgres? pgvector is cheapest.
  • Don't want to manage anything? Pinecone or Qdrant Cloud.
  • Want best performance per dollar? Qdrant self-hosted.

Building a Production RAG Pipeline

Regardless of which vector database you choose, here's the architecture I use for production RAG systems:

class ProductionRAG:
    def __init__(self, vector_store, llm_client, embedding_model="text-embedding-3-small"):
        self.vector_store = vector_store
        self.llm = llm_client
        self.embedding_model = embedding_model
        self.reranker = None  # Optional cross-encoder for better results
    
    def ingest(self, documents: list[str], metadata: list[dict] = None):
        """Batch process documents for efficiency"""
        # Generate embeddings in batch
        embeddings = self._batch_embed(documents)
        # Upsert in chunks to avoid memory issues
        chunk_size = 100
        for i in range(0, len(documents), chunk_size):
            self.vector_store.upsert(
                documents[i:i+chunk_size],
                embeddings[i:i+chunk_size],
                metadata[i:i+chunk_size] if metadata else None
            )
    
    def query(self, question: str, top_k: int = 10, rerank_top_k: int = 3) -> str:
        """Retrieve, optionally rerank, then generate"""
        # Embed the question
        query_embedding = self._embed(question)
        
        # Retrieve candidates
        candidates = self.vector_store.search(query_embedding, top_k=top_k)
        
        # Optional: rerank for better precision
        if self.reranker:
            candidates = self.reranker.rerank(question, candidates)[:rerank_top_k]
        
        # Build context
        context = "\n\n".join([c.content for c in candidates])
        
        # Generate answer with citations
        prompt = f"""Answer the question based on the context below. 
Include citations like [1], [2] referring to the source numbers.

Context:
{context}

Question: {question}

Answer:"""
        
        response = self.llm.chat.completions.create(
            model="gpt-4-turbo",
            messages=[{"role": "user", "content": prompt}],
            temperature=0.1
        )
        
        return {
            "answer": response.choices[0].message.content,
            "sources": candidates
        }

The key production lessons: always batch your embedding calls, use a reranker when precision matters, and stream responses to users for better perceived latency.

My Final Recommendations

After running all three in production for the past two years, here's how I choose:

Choose pgvector when:

  • You already run Postgres in production
  • Your vector dataset is under 5M vectors
  • You need ACID guarantees on your embeddings
  • Latency above 100ms is acceptable

Choose Pinecone when:

  • You need to ship in days, not weeks
  • Your team is small and doesn't want to manage infrastructure
  • You're willing to pay a premium for zero ops
  • Workload is bursty (serverless shines here)

Choose Qdrant when:

  • Latency is critical (sub-50ms p95)
  • You need hybrid search out of the box
  • You want open source with optional managed hosting
  • You're building something that might need to scale to 100M+ vectors

Conclusion

The right vector database for your RAG system isn't about which one has the best marketing — it's about matching the tool to your constraints. I've seen teams burn months trying to optimize pgvector when they should have just used Pinecone, and I've seen startups bleed cash on Pinecone when a $50/month Qdrant setup would have worked fine.

Start with your constraints: existing infrastructure, team expertise, latency requirements, and budget. Then pick the simplest option that meets them. You can always migrate later — vector databases are not a marriage, they're a renewable contract.

If you're building a production RAG system and want a second opinion on architecture, I help teams design and ship AI systems. Get in touch and let's talk about your specific use case.


Tags: AI Engineering, Vector Database, RAG Systems, Production AI

#AI & Automation#Vector Databases#RAG Systems

How to do it

  1. 1

    Choose Your Vector Database

    Evaluate your constraints: existing infrastructure (Postgres? Use pgvector), team expertise (small team? Use Pinecone managed service), and latency requirements (sub-50ms? Use Qdrant). Match the database to your needs rather than chasing the fastest option.

  2. 2

    Set Up the Database and Index

    Install and configure your chosen vector database. For pgvector, run CREATE EXTENSION vector and create an HNSW index. For Pinecone, create a serverless index via API. For Qdrant, deploy via Docker or use Qdrant Cloud. Configure dimensions to match your embedding model (1536 for OpenAI text-embedding-3-small).

  3. 3

    Build the Embedding Pipeline

    Create a batched embedding pipeline that generates vectors using your embedding model of choice. Store vectors along with metadata for filtering. Use batch sizes of 50-100 documents per API call to balance speed and cost. Always include source tracking metadata for citations in RAG responses.

  4. 4

    Implement the Retrieval Logic

    Build a query function that embeds the user's question, searches for top-k similar vectors (typically 10-20), and optionally reranks results for better precision. Add metadata filtering when you need to scope searches to specific document types, dates, or user permissions.

  5. 5

    Connect to Your LLM for Generation

    Pass the retrieved context along with the user query to your LLM (GPT-4, Claude, etc.) with a clear prompt that instructs the model to answer based only on the provided context and include source citations. Implement streaming for better perceived latency, and add monitoring for retrieval quality and response accuracy.

Frequently asked questions

What is the difference between pgvector, Pinecone, and Qdrant?

pgvector is a PostgreSQL extension for self-hosted vector search, ideal for teams already using Postgres. Pinecone is a fully managed SaaS vector database with zero ops and fast time-to-production. Qdrant is an open-source vector database written in Rust that offers both self-hosted and cloud options, with the best raw performance. The choice depends on your existing infrastructure, team expertise, and latency requirements.

Which vector database is cheapest for production RAG?

At moderate scale (5M vectors, 10M queries/month), all three are within 20% of each other in cost. pgvector is cheapest if you already run Postgres (~310-620/month). Pinecone serverless runs about 280-400/month with predictable billing. Qdrant self-hosted is similar to pgvector in cost but offers better performance. The real cost driver is usually compute, not the database itself.

How does pgvector performance compare to Pinecone and Qdrant?

In production benchmarks with 1M vectors at 1536 dimensions, Qdrant has the lowest latency (12ms p50), followed by Pinecone (18ms p50), then pgvector (45ms p50). At p95, Qdrant is 35ms, Pinecone is 52ms, and pgvector is 180ms. For high-throughput concurrent workloads, dedicated vector databases like Qdrant significantly outperform pgvector.

When should I use pgvector instead of Pinecone or Qdrant?

Use pgvector when you already run Postgres in production, have datasets under 5M vectors, need ACID transactions on your embedding data, or want to avoid adding new infrastructure. It's perfect for teams with existing Postgres expertise who don't need sub-50ms latency. The trade-off is lower throughput and higher p95 latency compared to dedicated vector databases.

Can I migrate from one vector database to another later?

Yes, migrating between vector databases is straightforward because they all store the same type of data (vectors with metadata). The typical migration involves exporting your vectors and metadata, then re-inserting into the new database. The main consideration is re-indexing time, which for 5M vectors can take 10-30 minutes depending on the database and parameters. Plan for a brief query downtime or run both in parallel during migration.

More articles

Related reading from the same areas — practical notes on shipping software.

View all articles

Liked the article?

Have a similar problem in your business? Let's talk about building the fix.

Start a project