Siddhant DevalAuthor
Senior Full-Stack Engineer·Jul 5, 2026·16 min read
Relational Databases: PostgreSQL & MySQL in Production
PostgreSQL is not just a SQL database — it is an extensible platform that handles semi-structured data, geospatial queries, and AI embeddings in a single query. This article covers ACID mechanics, MVCC, the extension ecosystem, data modeling discipline, Row-Level Security for multi-tenancy, and the scaling ceiling every production team will eventually hit.
Technical Series
Modern Database Paradigms
Part 2 of 8
Relational Databases: PostgreSQL & MySQL in Production
Selecting the right database is a foundational architectural decision; data gravity ultimately dictates the scalability and resilience of an application — choose the paradigm first, the product second. Teams that decide "PostgreSQL for everything" are often right — but not for the reasons they think. Postgres is not just a SQL database. It is an extensible platform that silently absorbs use cases that would otherwise demand three separate stores: JSONB absorbs a document database, PostGIS absorbs a geospatial tier, and pgvector absorbs a vector search layer. This article shows you how to use that extension model deliberately, how to model data correctly in a relational schema, and the exact signals that tell you Postgres can no longer grow with you.
1. ACID Mechanics — What the Guarantee Actually Covers
The broken pattern: teams interpret "ACID compliant" as "safe" and stop thinking. ACID is a contract with specific boundaries — violating any of the four properties silently undermines data integrity.
sql
| Property | What It Guarantees | What It Does NOT Guarantee |
|---|---|---|
| Atomicity | All statements in a transaction commit or none do | Application-layer logic correctness |
| Consistency | Constraints (FK, UNIQUE, CHECK) always hold after commit | Business rule correctness beyond constraints |
| Isolation | Concurrent transactions don't see each other's in-progress writes | Performance — higher isolation = lower throughput |
| Durability | Committed writes survive crash (via WAL flush to disk) | Replica lag — replicas may lag behind primary |
Performance / Safety Warning
PostgreSQL's default isolation level is
READ COMMITTED, not SERIALIZABLE. Under READ COMMITTED, a transaction can see rows committed by other transactions between its own statements — this causes non-repeatable reads. For financial calculations or inventory checks, explicitly set SET TRANSACTION ISOLATION LEVEL REPEATABLE READ.1.1 MVCC — Concurrent Reads Without Locking
sql
MVCC (Multi-Version Concurrency Control) is why Postgres achieves high read concurrency without read locks. Each transaction works against a consistent snapshot of the database as of its start time. Writers create new row versions (tuples) rather than modifying in place. The tradeoff: old tuple versions accumulate as "dead tuples" until
VACUUM reclaims them.Crucial Requirement
Long-running transactions block
VACUUM from reclaiming dead tuples. A single 4-hour analytics query can cause table bloat that degrades write performance for the entire cluster. Monitor pg_stat_activity for long-running sessions and set statement_timeout for analytics workloads.2. The PostgreSQL Extension Ecosystem
2.1 JSONB — Semi-Structured Data Without a Document Database
sql
Pro Tip & Optimization
Use structured columns for fields you query with
WHERE, ORDER BY, or GROUP BY. Use JSONB for variable, product-specific attributes that differ per record. Never store queryable data inside a JSONB blob — the GIN index helps but it cannot replace a B-tree index on a dedicated column.2.2 PostGIS — Geospatial Without a Separate Tier
sql
2.3 pgvector — AI Embeddings in Your Existing Postgres Stack
sql
Architectural Note
pgvector's HNSW index loads the entire graph into RAM. At 10M 1536-dimension vectors, that is approximately 60GB of RAM. If your vector dataset exceeds available memory, the HNSW index degrades to disk I/O — this is the signal to migrate to a dedicated vector store (Qdrant, Pinecone).
3. Data Modeling
3.1 Normalization vs. Strategic Denormalization
sql
The rule: normalize for update correctness; denormalize for read performance. Snapshot data that represents a moment in time (order price, invoice total) — never join to get it at render time.
3.2 Hybrid Schema — Structured Columns + JSONB
The correct pattern for product catalogs, CMS content, and multi-tenant SaaS applications where each entity type has a shared core and a variable extension:
sql

Expand
3.3 Schema Migration Discipline
sql
Crucial Requirement
In PostgreSQL,
ALTER TABLE ... DROP COLUMN takes an ACCESS EXCLUSIVE lock. On a table with millions of rows and active read traffic, this blocks all reads and writes until it completes. Always use the three-phase deprecation cycle in production.4. Indexing Strategy
| Index Type | Storage Engine | When to Use |
|---|---|---|
| B-tree (default) | Sorted tree | Equality (=), range (<, >), ORDER BY, most WHERE clauses |
| GIN | Inverted index | JSONB containment (@>), array overlap (&&), full-text search (tsvector) |
| GiST | Generalized search tree | Geospatial (ST_DWithin), range types (tstzrange), nearest-neighbor |
| BRIN | Block range min/max | Large tables with naturally ordered data (timestamps, sequential IDs) — tiny index, fast range scan |
| Hash | Hash table | Equality only — rarely preferred over B-tree |
sql
5. MySQL vs. PostgreSQL

Expand
| Criterion | PostgreSQL | MySQL (InnoDB) |
|---|---|---|
| ACID compliance | Full MVCC, serializable isolation | Full MVCC, serializable available |
| JSON support | JSONB — binary, indexed, queryable | JSON — text-stored, limited indexing |
| Extension ecosystem | pgvector, PostGIS, pg_search, TimescaleDB | Minimal — extensions rare |
| Replication | Streaming replication (WAL-based), logical replication | Binary log replication; Group Replication for multi-primary |
| Full-text search | tsvector + GIN — capable for moderate scale | FULLTEXT index — less flexible than Postgres |
| Connection model | Process-per-connection (PgBouncer required at scale) | Thread-per-connection (better raw concurrency) |
| License | PostgreSQL License (permissive) | GPL v2 (requires open-source compliance or commercial license) |
| Best fit | Complex queries, extensions, multi-tenant SaaS, AI workloads | High-read web apps, SaaS, CMS, WordPress, Drupal |
Pro Tip & Optimization
Choose MySQL when your team already operates it well and your workload is read-heavy with simple queries. Choose PostgreSQL when you need JSONB, PostGIS, pgvector, complex window functions, or multi-tenant Row-Level Security. Do not choose based on benchmarks — choose based on the extension your workload needs.
6. Security
6.1 Row-Level Security for Multi-Tenancy
sql
Crucial Requirement
RLS policies are enforced even for superusers unless
FORCE ROW LEVEL SECURITY is set. Always set BYPASSRLS = false on application service roles. Application-layer WHERE tenant_id = ? filtering is a defense-in-depth layer, not a replacement for RLS.6.2 Audit Trails with pg_audit
sql
7. When NOT to Use Relational
| Scenario | Why Relational Fails | Better Alternative |
|---|---|---|
| Rapidly evolving schema (early product iteration) | ALTER TABLE migrations block tables; schema rigidity slows experimentation | MongoDB — schemaless documents, no migration required |
| Write-heavy append-only at extreme volume (> 500K writes/sec) | WAL becomes the bottleneck; single-primary model cannot absorb the throughput | Cassandra — LSM-tree append, multi-primary writes |
| Graph traversal with 3+ relationship hops | Recursive CTEs scale as O(n³) — impractical beyond millions of rows | Neo4j — native graph storage, Cypher in milliseconds |
| Sub-millisecond key lookups (session, rate limit) | Network + query parse overhead adds ~1–5ms minimum | Redis — in-memory O(1) hash lookups |
| Horizontal scale across regions (petabyte-scale, auto-sharding) | Single primary model; manual sharding is complex and failure-prone | CockroachDB — automatic Raft-based horizontal sharding |
8. The Scaling Ceiling
The correct escalation path when Postgres performance degrades:
Architectural Note
Most teams reach Step 1 and stop. PgBouncer in transaction mode (not session mode) is the single highest-leverage optimization available for Postgres at scale — it multiplies the effective connection capacity by 10–50× without changing any application code.
Summary
| Concept | Rule |
|---|---|
| PostgreSQL extension model | PostgreSQL's extension model (JSONB, PostGIS, pgvector) absorbs use cases that would otherwise require 3 separate databases. |
| MVCC and concurrency | MVCC enables high read concurrency without locking — understanding it prevents incorrect 'Postgres doesn't scale' conclusions. |
| Multi-tenant security | Row-Level Security is the correct mechanism for multi-tenant isolation; application-layer filtering is a security anti-pattern. |
| Scaling ceiling | PostgreSQL's scaling ceiling is connection-count, not query performance — deploy PgBouncer before reaching for sharding. |
| When to switch | Relational databases are wrong for rapidly evolving schemas, 3+ hop graph queries, and write-throughput workloads exceeding what a single primary can absorb. |
What's Next
In Part 3, we cover document and NoSQL databases — MongoDB's embedding vs. referencing decision, DynamoDB's single-table design, schema versioning patterns, and the GDPR compliance challenge that document denormalization creates.
Research & Synthesis Note
This article was developed with AI-assisted deep search, specification cross-referencing, and technical research synthesis.
#PostgreSQL#MySQL#Relational Database#ACID#MVCC#Row-Level Security#pgvector
Technical Series
Modern Database Paradigms
Part 2 of 8