Home Database pgvector: Vector Search and Embeddings in PostgreSQL
Advanced 3 min · July 13, 2026

pgvector: Vector Search and Embeddings in PostgreSQL

Learn pgvector for vector search in PostgreSQL.

N
Naren Founder & Principal Engineer

20+ years shipping high-throughput database systems. Drawn from code that ran under real load.

Follow
Production
production tested
July 13, 2026
last updated
2,165
articles · all by Naren
Before you start⏱ 15-20 min read
  • Basic knowledge of PostgreSQL (tables, indexes, queries).
  • PostgreSQL 11+ installed.
  • Superuser access to install extensions.
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • pgvector adds vector similarity search to PostgreSQL.
  • Use it for semantic search, recommendations, and AI embeddings.
  • Supports exact and approximate nearest neighbor search (IVFFlat, HNSW).
  • Integrates seamlessly with existing SQL queries.
  • Requires careful index tuning for production performance.
✦ Definition~90s read
What is pgvector?

pgvector is a PostgreSQL extension that lets you store and query vector embeddings using similarity search.

Imagine you have a library of books, but instead of searching by title or author, you want to find books that are 'similar' in meaning.
Plain-English First

Imagine you have a library of books, but instead of searching by title or author, you want to find books that are 'similar' in meaning. pgvector lets you turn each book into a list of numbers (a vector) that represents its content. Then you can ask, 'Which books are closest to this one?' and it finds them by measuring distance between those number lists.

In the age of AI, data isn't just rows and columns—it's embeddings. Whether you're building a semantic search engine, a recommendation system, or a RAG (Retrieval-Augmented Generation) pipeline, you need to store and query high-dimensional vectors efficiently. Enter pgvector: an open-source extension that turns PostgreSQL into a vector database. It allows you to store vectors alongside your relational data and perform similarity searches using exact or approximate nearest neighbor algorithms. This means you can keep your existing PostgreSQL infrastructure while adding vector capabilities, avoiding the operational complexity of a separate vector database. In this tutorial, you'll learn how to install pgvector, create vector columns, index them for performance, and run similarity queries. We'll also cover production pitfalls, debugging techniques, and a real-world incident where a missing index caused a production outage. By the end, you'll be able to integrate vector search into your PostgreSQL applications with confidence.

Installing pgvector

pgvector is available as a PostgreSQL extension. Installation varies by OS. On Ubuntu/Debian, you can install from apt: sudo apt install postgresql-16-pgvector. For other versions, compile from source: clone the repo, run make && make install. Then enable the extension in your database: CREATE EXTENSION vector;. Verify with SELECT * FROM pg_extension;. You'll need superuser privileges. After installation, you can create vector columns with the vector(n) type, where n is the number of dimensions.

install.sqlSQL
1
2
3
4
5
-- Enable the extension
CREATE EXTENSION IF NOT EXISTS vector;

-- Check installed version
SELECT extversion FROM pg_extension WHERE extname = 'vector';
Output
extversion
------------
0.7.4
(1 row)
🔥Version Compatibility
📊 Production Insight
In production, ensure all replicas also have the extension installed before creating vector columns.
🎯 Key Takeaway
Install pgvector via apt or compile from source, then enable with CREATE EXTENSION.

Creating Vector Columns and Inserting Data

Define a vector column with a fixed dimension. For example, if you use OpenAI embeddings (1536 dimensions), create vector(1536). Insert data using array notation: '[0.1, 0.2, ...]'::vector. You can also generate vectors from Python or other clients. Here's an example table for storing product embeddings.

create_table.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
-- Create table with vector column
CREATE TABLE products (
    id SERIAL PRIMARY KEY,
    name TEXT,
    description TEXT,
    embedding vector(3)  -- small dimension for demo
);

-- Insert sample data
INSERT INTO products (name, description, embedding) VALUES
('Laptop', 'A powerful laptop', '[0.1, 0.2, 0.3]'),
('Phone', 'A smartphone', '[0.4, 0.5, 0.6]'),
('Tablet', 'A tablet device', '[0.7, 0.8, 0.9]');
Output
INSERT 0 3
💡Normalize Your Vectors
📊 Production Insight
Consider using a separate table for embeddings to avoid bloating the main table if vectors are large.
🎯 Key Takeaway
Use vector(n) type with fixed dimension. Insert vectors as array literals.

pgvector supports three distance operators: L2 distance (<->), inner product (<#>), and cosine distance (<=>). Use ORDER BY with the operator and LIMIT to get nearest neighbors. Example: find products similar to a query vector.

similarity_search.sqlSQL
1
2
3
4
5
-- Find top 2 products closest to [0.2, 0.3, 0.4]
SELECT id, name, embedding <=> '[0.2, 0.3, 0.4]'::vector AS distance
FROM products
ORDER BY embedding <=> '[0.2, 0.3, 0.4]'::vector
LIMIT 2;
Output
id | name | distance
----+---------+-------------------
1 | Laptop | 0.1732050807568877
2 | Phone | 0.3464101615137755
(2 rows)
⚠ Distance vs Similarity
📊 Production Insight
In production, use parameterized queries to avoid SQL injection when passing vectors from application code.
🎯 Key Takeaway
Use <=> for cosine, <-> for L2, <#> for inner product. Always limit results for performance.

Indexing for Performance

Without an index, pgvector performs exact search (O(n)). For large datasets, use approximate nearest neighbor (ANN) indexes. pgvector offers IVFFlat (Inverted File with Flat) and HNSW (Hierarchical Navigable Small World). IVFFlat is faster to build and uses less memory; HNSW provides better recall and query speed but is more resource-intensive. Choose based on your trade-offs.

create_index.sqlSQL
1
2
3
4
5
-- IVFFlat index (requires training data)
CREATE INDEX ON products USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);

-- HNSW index (no training needed)
CREATE INDEX ON products USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 200);
Output
CREATE INDEX
🔥Index Parameters
📊 Production Insight
Rebuild IVFFlat indexes periodically if data changes significantly, as they are not dynamic.
🎯 Key Takeaway
Use IVFFlat for fast builds and lower memory; HNSW for better query performance. Always index before production.

Using pgvector with Python and ORMs

pgvector integrates with popular Python libraries like psycopg2, SQLAlchemy, and Django. For psycopg2, use the register_vector adapter. Example: store and query embeddings from Python.

python_example.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import psycopg2
from pgvector.psycopg2 import register_vector

conn = psycopg2.connect(database="test")
register_vector(conn)
cur = conn.cursor()

# Insert
cur.execute("INSERT INTO products (name, embedding) VALUES (%s, %s)",
            ("Camera", [0.2, 0.3, 0.4]))

# Query
cur.execute("SELECT id, name FROM products ORDER BY embedding <=> %s LIMIT 3",
            ([0.1, 0.2, 0.3],))
for row in cur.fetchall():
    print(row)
Output
(1, 'Laptop')
(4, 'Camera')
(2, 'Phone')
💡Use pgvector with SQLAlchemy
📊 Production Insight
For high-throughput applications, use connection pooling and batch inserts for embeddings.
🎯 Key Takeaway
Register the vector adapter in psycopg2 to handle vector arrays. Use parameterized queries.

Production Considerations and Monitoring

In production, monitor index build times, query latency, and recall. Use EXPLAIN ANALYZE to ensure indexes are used. Set ivfflat.probes for IVFFlat to control accuracy-speed trade-off (default 1, increase for better recall). For HNSW, set hnsw.ef_search per query. Also consider partitioning large tables by vector dimension or using separate tables for different embedding models.

tune_search.sqlSQL
1
2
3
4
5
6
7
8
-- Set probes for IVFFlat (session level)
SET ivfflat.probes = 10;

-- Set ef_search for HNSW (session level)
SET hnsw.ef_search = 100;

-- Verify index usage
EXPLAIN ANALYZE SELECT id, name FROM products ORDER BY embedding <=> '[0.1,0.2,0.3]'::vector LIMIT 5;
Output
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------
Limit (cost=... rows=5 width=...) (actual time=... rows=5 loops=1)
-> Index Scan using products_embedding_idx on products (cost=... rows=... width=...) (actual time=... rows=5 loops=1)
Order By: (embedding <=> '[0.1,0.2,0.3]'::vector)
Planning Time: 0.1 ms
Execution Time: 0.5 ms
⚠ Index Maintenance
📊 Production Insight
Set ivfflat.probes globally in postgresql.conf if most queries need high recall.
🎯 Key Takeaway
Tune probes/ef_search for accuracy. Use EXPLAIN ANALYZE to confirm index usage. Rebuild IVFFlat indexes periodically.

Hybrid Search: Combining Vector and Relational Filters

Often you need to filter by metadata before vector search. For example, find products similar to a query but only in a specific category. pgvector supports combining WHERE clauses with ORDER BY. However, ensure the filter is selective to avoid scanning many rows. Use composite indexes or partial indexes for performance.

hybrid_search.sqlSQL
1
2
3
4
5
6
7
8
9
-- Filter by category then order by similarity
SELECT id, name, category
FROM products
WHERE category = 'Electronics'
ORDER BY embedding <=> '[0.1,0.2,0.3]'::vector
LIMIT 5;

-- Create a partial index for better performance
CREATE INDEX ON products USING ivfflat (embedding vector_cosine_ops) WHERE category = 'Electronics';
Output
id | name | category
----+---------+-------------
1 | Laptop | Electronics
2 | Phone | Electronics
(2 rows)
💡Filter Order Matters
📊 Production Insight
For multi-tenant systems, include tenant_id in the WHERE clause and create a partial index per tenant if needed.
🎯 Key Takeaway
Combine vector search with SQL filters. Use partial indexes for common filter values.
● Production incidentPOST-MORTEMseverity: high

The Missing Index That Brought Down Search

Symptom
Users experienced timeouts on the search page. Database CPU hit 100% and queries queued up.
Assumption
The developer assumed that because the table had only 10,000 rows, no index was needed for vector search.
Root cause
The vector column had no index, so every query performed a full table scan, computing distances for all rows. As data grew to 100,000 rows, the brute-force scan became unsustainable.
Fix
Created an IVFFlat index on the vector column with appropriate lists parameter and re-ran the query.
Key lesson
  • Always index vector columns for production workloads, even with small datasets.
  • Use EXPLAIN ANALYZE to verify query plans before deploying.
  • Monitor query performance as data grows; what works at 10k rows may fail at 100k.
  • Choose the right index type (IVFFlat vs HNSW) based on latency and accuracy requirements.
Production debug guideSymptom to Action4 entries
Symptom · 01
Vector similarity query is slow (>1s)
Fix
Check if an index exists: SELECT * FROM pg_indexes WHERE tablename = 'your_table';. If missing, create an IVFFlat or HNSW index.
Symptom · 02
Query returns inaccurate results
Fix
Verify distance function (L2, cosine, inner product). Check index parameters: for IVFFlat, increase lists for better recall; for HNSW, increase m and ef_construction.
Symptom · 03
Index creation takes too long or fails
Fix
Reduce lists parameter for IVFFlat or use smaller m for HNSW. Consider building index in maintenance window.
Symptom · 04
Memory errors during index build
Fix
Increase maintenance_work_mem temporarily. For HNSW, reduce ef_construction.
★ Quick Debug Cheat SheetCommon pgvector issues and immediate fixes.
Slow vector search
Immediate action
Check for index
Commands
SELECT * FROM pg_indexes WHERE tablename='items';
CREATE INDEX ON items USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
Fix now
Create an IVFFlat index with appropriate lists.
Inaccurate results+
Immediate action
Review distance function
Commands
SELECT * FROM items ORDER BY embedding <=> '[1,2,3]' LIMIT 10;
EXPLAIN ANALYZE SELECT ...
Fix now
Switch to cosine distance if using normalized vectors.
Index build fails+
Immediate action
Increase maintenance_work_mem
Commands
SET maintenance_work_mem = '2GB';
CREATE INDEX ...
Fix now
Set higher memory and retry.
FeatureIVFFlatHNSW
Build timeFastSlow
Memory usageLowHigh
Query speedModerateFast
RecallGood (tunable)Excellent
Incremental updatesNo (requires rebuild)Yes
Parameterslists, probesm, ef_construction, ef_search
⚙ Quick Reference
7 commands from this guide
FileCommand / CodePurpose
install.sqlCREATE EXTENSION IF NOT EXISTS vector;Installing pgvector
create_table.sqlCREATE TABLE products (Creating Vector Columns and Inserting Data
similarity_search.sqlSELECT id, name, embedding <=> '[0.2, 0.3, 0.4]'::vector AS distancePerforming Similarity Search
create_index.sqlCREATE INDEX ON products USING ivfflat (embedding vector_cosine_ops) WITH (lists...Indexing for Performance
python_example.pyfrom pgvector.psycopg2 import register_vectorUsing pgvector with Python and ORMs
tune_search.sqlSET ivfflat.probes = 10;Production Considerations and Monitoring
hybrid_search.sqlSELECT id, name, categoryHybrid Search

Key takeaways

1
pgvector turns PostgreSQL into a vector database for similarity search.
2
Always index vector columns with IVFFlat or HNSW for production workloads.
3
Tune index parameters and search settings (probes, ef_search) for accuracy-speed trade-off.
4
Combine vector search with SQL filters for hybrid queries.
5
Monitor index health and rebuild IVFFlat indexes periodically.

Common mistakes to avoid

3 patterns
×

Not indexing vector columns before production

×

Using the wrong distance function

×

Ignoring index maintenance for IVFFlat

INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
What is pgvector and how does it enable vector search in PostgreSQL?
Q02SENIOR
Explain the difference between IVFFlat and HNSW indexes in pgvector.
Q03SENIOR
How would you optimize a pgvector query that is too slow?
Q01 of 03JUNIOR

What is pgvector and how does it enable vector search in PostgreSQL?

ANSWER
pgvector is an open-source PostgreSQL extension that adds a vector data type and similarity search operators. It allows storing high-dimensional vectors (e.g., embeddings) and performing nearest neighbor search using L2, inner product, or cosine distance. It supports both exact and approximate search via IVFFlat and HNSW indexes.
FAQ · 3 QUESTIONS

Frequently Asked Questions

01
What is the maximum vector dimension in pgvector?
02
Can I use pgvector with existing PostgreSQL tables?
03
How do I choose between IVFFlat and HNSW?
N
Naren Founder & Principal Engineer

20+ years shipping high-throughput database systems. Drawn from code that ran under real load.

Follow
Verified
production tested
July 13, 2026
last updated
2,165
articles · all by Naren
🔥

That's MySQL & PostgreSQL. Mark it forged?

3 min read · try the examples if you haven't

Previous
PostgreSQL High Availability: Patroni, repmgr, and Failover
16 / 20 · MySQL & PostgreSQL
Next
PostgreSQL Performance Tuning: Memory, I/O, and Vacuum