Advanced 28 min readModule: Module 14: Vector Databases: HNSW & IVF-PQ Indexing
Vector Database Internals: HNSW & IVF-PQ Indexing
Deconstruct production Vector Databases (Milvus, Pinecone, Qdrant, pgvector): Approximate Nearest Neighbor (ANN) search, Hierarchical Navigable Small World (HNSW multi-layer graphs), Product Quantization (PQ sub-vector compression), and SIMD-accelerated distance metrics.
What You Will Learn in This Lesson
- Why brute-force Exact Nearest Neighbor (kNN) search O(N * D) fails on millions of high-dimensional embeddings
- The Hierarchical Navigable Small World (HNSW) graph: multi-layer skip-list inspired graph navigation in O(log N)
- Inverted File Index with Product Quantization (IVF-PQ): compressing 1536-dimensional vectors by 95%
- Filtering with vector payload metadata: Pre-filtering vs Post-filtering vs Single-Stage HNSW Filter Graphs
Introduction & Core Concept
Generating 1536-dimensional OpenAI embeddings is only the first step. When a database contains 10,000,000 document vectors, calculating exact Euclidean distance or Cosine Similarity across every vector takes several seconds per query. Vector databases achieve sub-millisecond retrieval across billions of vectors using Approximate Nearest Neighbor (ANN) data structures, primarily Hierarchical Navigable Small World (HNSW) graphs.
WHY DOES THIS MATTER IN THE REAL WORLD?
High-scale RAG systems, visual search engines, and recommendation systems (Spotify, Pinterest) serve real-time semantic queries in under 5ms using HNSW and IVF-PQ indexing.
Syntax & Structure
python
// pgvector HNSW Index CreationCREATE INDEX ON document_embeddings USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 64);Simulating HNSW Multi-Layer Graph Skip-Navigation in Python
pythonpython
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152# Hierarchical Navigable Small World (HNSW) Conceptual Graph Navigationimport numpy as npclass HNSWLayerNode:def __init__(self, vector_id, vector):self.id = vector_idself.vector = vectorself.neighbors = [] # Connected edge pointersdef cosine_similarity(v1, v2):return np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2) + 1e-9)class SimpleHNSW:def __init__(self):# Layer 1 (Top sparse layer: long skips) and Layer 0 (Bottom dense layer: exact local search)self.layer1_nodes = []self.layer0_nodes = []def search_layer(self, query_vec, entry_node, candidate_pool_size=4):"""Greedy graph search on a single layer: Move to closest neighbor until local minima."""current = entry_nodebest_sim = cosine_similarity(query_vec, current.vector)while True:improved = Falsefor neighbor in current.neighbors:sim = cosine_similarity(query_vec, neighbor.vector)if sim > best_sim:best_sim = simcurrent = neighborimproved = Trueif not improved:break # Local maximum similarity reached on this layer!return current, best_sim# Create simulated nodesnode_a = HNSWLayerNode("Doc_Tech", np.array([0.9, 0.1, 0.2]))node_b = HNSWLayerNode("Doc_Science", np.array([0.8, 0.3, 0.1]))node_c = HNSWLayerNode("Doc_Cooking", np.array([0.1, 0.9, 0.8]))node_a.neighbors = [node_b]node_b.neighbors = [node_a, node_c]node_c.neighbors = [node_b]hnsw = SimpleHNSW()query = np.array([0.95, 0.05, 0.15]) # Search query close to Techbest_match, score = hnsw.search_layer(query, entry_node=node_c) # Start from distant entry nodeprint("=== HNSW Vector Database Search Engine ===")print(f"Top Semantic Match: {best_match.id} (Cosine Similarity: {score:.4f})")print("✅ Graph navigated via Small-World links to target in logarithmic O(log N) hops!")
Line-by-Line Technical Breakdown
1Product Quantization (PQ): A 1536-dimension float32 vector consumes 6,144 bytes of RAM. Product Quantization splits the vector into 64 sub-vectors of 24 dimensions, clusters each sub-vector into 256 centroids (1 byte codebook index), compressing the vector from 6KB down to 64 bytes (99% RAM savings) with hardware AVX SIMD asymmetric distance lookup.
Try It Yourself (Interactive Editor)
Modify the code in real-time and click Run to test live browser output and console logs.
Intelligent Code Runner & Live Sandbox[PYTHON]
PYTHON SOURCE EDITOR
Interactive Live CodeCommon Mistakes & How to Avoid Them
#1: Filtering vector search queries using naive post-filtering (fetching top 10 vectors then filtering by user ID), returning 0 results if all 10 belong to other users.
Post-filtering suffers from severe recall collapse. Always use vector databases that support single-stage filtered graph traversal.
Incorrect / Antipattern
// Step 1: kNN search top 10 -> Step 2: filter(user_id == '123') -> Empty list!Correct / Professional Solution
// Use Single-Stage Iterative HNSW Filtering (Qdrant / Milvus) to traverse filtered graph edgesIndustry Best Practices & Professional Standards
- Tune HNSW parameters: `M = 16-32` (connections per node) and `ef_construction = 64-128` (build index accuracy).
- Use Product Quantization (IVF-PQ) when indexing datasets exceeding 50,000,000 vectors on limited RAM.
- Normalize vectors to unit length during ingestion to turn Dot Product into fast Cosine Similarity.
Lesson Summary & Core Takeaways
- HNSW constructs multi-layer proximity graphs for sub-millisecond O(log N) vector retrieval.
- Product Quantization (PQ) compresses multi-dimensional vectors by up to 95%.
- Single-stage filtered traversal prevents recall degradation in multi-tenant RAG applications.