Phase 08: Extensions & Modern Ecosystem¶
Chapter 32: Essential Production Extensions¶
32.1 pg_stat_statements (The DBA's Primary Radar)¶
If you manage a PostgreSQL database without pg_stat_statements, you are flying blind. It tracks execution statistics across all SQL queries, parameterizing arguments (WHERE id = ?) so identical query patterns aggregate into a single metric.
-- Enable in postgresql.conf:
shared_preload_libraries = 'pg_stat_statements'
pg_stat_statements.track = all
pg_stat_statements.max = 10000
-- Query the Top 5 most CPU-intensive queries across the entire database:
SELECT
queryid,
substring(query, 1, 50) AS short_query,
calls,
round(total_exec_time::numeric, 2) AS total_ms,
round(mean_exec_time::numeric, 2) AS avg_ms,
round((100 * total_exec_time / sum(total_exec_time) OVER ())::numeric, 2) AS pct_load,
shared_blks_hit,
shared_blks_read
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 5;
32.2 pg_trgm (Fuzzy Search & Fast LIKE '%word%')¶
A standard B-Tree index is completely useless for wildcard queries starting with %:
SELECT * FROM products WHERE name LIKE '%laptop%'; (Forces a full table Seq Scan).
The Trigram Solution:¶
pg_trgm breaks text into 3-character substrings ("trigrams"):
* "laptop" -> {" l", " la", "lap", "apt", "pto", "top", "op "}
* It builds a GIN index over these trigrams:
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE INDEX idx_products_name_trgm ON products USING gin (name gin_trgm_ops);
-- This query now uses a blazing fast GIN Index Scan:
EXPLAIN ANALYZE
SELECT * FROM products WHERE name ILIKE '%thinkpad%';
32.3 hypopg (Hypothetical Indexes)¶
Before creating a 50GB index on a production table, how do you know if the query planner will actually choose it?
hypopg creates in-memory hypothetical indexes that exist only for the duration of your session and take 0 disk space and 0 creation time:
CREATE EXTENSION hypopg;
SELECT * FROM hypopg_create_index('CREATE INDEX ON orders (customer_id, created_at)');
-- Run EXPLAIN:
EXPLAIN SELECT * FROM orders WHERE customer_id = 42 ORDER BY created_at DESC;
-- The planner will show if it would use the hypothetical index!
Chapter 33: Specialized Workloads: Time-Series & Geospatial¶
33.1 TimescaleDB (Time-Series Hypertables)¶
- The Problem: High-frequency metrics (IoT, financial ticks) bloat tables to billions of rows. B-Tree inserts degrade as indexes exceed RAM.
- The Hypertables Solution:
- Automatically chunks the table into physical partitions by time intervals (e.g., 1 day or 1 week).
- Directs writes only to the current active chunk, ensuring the index stays pinned in L2/L3 CPU cache.
- Native columnar compression achieving 90%+ disk reduction for older chunks.
33.2 PostGIS (Geospatial Dominance)¶
PostGIS turns PostgreSQL into the world's most powerful spatial engine:
* Spatial data types: GEOMETRY (flat Cartesian plane) and GEOGRAPHY (spherical Earth geoid).
* R-Tree / GiST Indexing: Indexes 2D/3D bounding boxes.
CREATE EXTENSION postgis;
-- Find all drivers within 5km of a rider:
SELECT driver_id, ST_Distance(driver_location, ST_MakePoint(-73.985, 40.748)::geography) AS distance_meters
FROM drivers
WHERE ST_DWithin(driver_location, ST_MakePoint(-73.985, 40.748)::geography, 5000)
ORDER BY distance_meters ASC;
Chapter 34: Foreign Data Wrappers & AI Vectors (pgvector)¶
34.1 postgres_fdw (Federated Multi-Database Queries)¶
Allows Postgres to mount tables located on remote PostgreSQL instances across the network and query them transparently:
CREATE EXTENSION postgres_fdw;
CREATE SERVER remote_billing FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (host 'billing-db.internal', dbname 'billing', port '5432');
CREATE USER MAPPING FOR current_user SERVER remote_billing
OPTIONS (user 'app_user', password 'secret');
IMPORT FOREIGN SCHEMA public FROM SERVER remote_billing INTO remote_billing_schema;
-- Query remote and local tables in a single JOIN:
SELECT u.name, b.invoice_total
FROM local_users u
JOIN remote_billing_schema.invoices b ON u.id = b.user_id;
34.2 pgvector (Vector Search for Generative AI & RAG)¶
Stores dense floating-point vector embeddings from OpenAI, Anthropic, or HuggingFace models:
CREATE EXTENSION vector;
CREATE TABLE documents (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
content TEXT,
embedding vector(1536) -- OpenAI text-embedding-3-small dimension
);
-- Fast Approximate Nearest Neighbor (ANN) HNSW Index:
CREATE INDEX idx_docs_embedding ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
-- Query the 5 most semantically similar documents:
SELECT id, content, (embedding <=> '[0.012, -0.045, ...]'::vector) AS cosine_distance
FROM documents
ORDER BY embedding <=> '[0.012, -0.045, ...]'::vector
LIMIT 5;
WHERE category = 'finance' AND created_at > now() - interval '30 days' ORDER BY embedding <=> query_vector LIMIT 10;
* Eliminates complex sync pipelines between your relational database and an external vector database like Pinecone.