Relational database comparison
PostgreSQL vs MySQL
The two most popular open-source relational databases. Both are battle-tested and widely supported in the cloud — but they differ in architecture, features, and philosophy. Choosing between them comes down to feature richness, performance profile, and ecosystem preference.
TL;DR
- PostgreSQL — advanced types (JSONB, arrays, hstore), window functions, tablespaces, a rich extension ecosystem (PostGIS, pgvector, TimescaleDB), strong SQL standards compliance, and a fully permissive PostgreSQL Licence. The default choice for new projects in the wider developer community.
- MySQL — simpler initial setup, fast on straightforward read-heavy workloads, the default for LAMP stacks and CMS platforms (WordPress, Drupal), dual GPL/commercial licence. InnoDB is the standard storage engine;
performance_schemaprovides deep runtime diagnostics. - Both are excellent. For most greenfield applications today, PostgreSQL is the community default. MySQL remains dominant where a LAMP ecosystem, managed hosting, or existing team expertise drives the decision.
Side-by-side comparison
| Aspect | PostgreSQL | MySQL |
|---|---|---|
| Concurrency model | MVCC — readers never block writers | InnoDB also uses MVCC; row-level locking |
| JSON support | JSONB — binary, indexable, 50+ operators | JSON column since 5.7 — text-stored, fewer operators |
| Full-text search | Built-in tsvector/tsquery with ranking & stemming | FULLTEXT indexes on InnoDB/MyISAM; basic ranking |
| Window functions | Full SQL:2003 support since v8.4 | Added in MySQL 8.0 |
| Extensions | PostGIS, pgvector, TimescaleDB, pg_cron, pg_trgm… | Plugin architecture; smaller ecosystem |
| Storage engines | Single storage layer (heap + WAL) | Pluggable: InnoDB (default), MyISAM, Memory… |
| Replication | Streaming WAL-based + logical replication | Binlog replication, GTID-based; Group Replication |
| Tablespaces | Supported — map tables to different disk volumes | InnoDB tablespaces supported; per-table files |
| Diagnostics | pg_stat_* system views | performance_schema + sys schema |
| Licensing | PostgreSQL Licence — permissive, no restrictions | GPL (Community) or paid commercial (Enterprise) |
What is PostgreSQL?
PostgreSQL (often called Postgres) is an open-source object-relational database system with over 35 years of development. It began as the POSTGRES project at UC Berkeley in 1986 and became fully open-source in 1996. It is governed by the PostgreSQL Global Development Group and released under the permissive PostgreSQL Licence, which imposes no restrictions on commercial use or distribution.
Postgres uses MVCC (Multi-Version Concurrency Control) throughout its storage engine. Every row written creates a new version; old versions are retained until a VACUUM pass reclaims space. This means readers never block writers and writers never block readers — a significant advantage for mixed read/write workloads.
Its extension system is one of its most valued features. Extensions such as PostGIS (geospatial), pgvector (vector similarity search for AI applications), TimescaleDB (time-series), and pg_cron (scheduled jobs) can be installed into the database with CREATE EXTENSION. This makes Postgres a foundation for specialised workloads without leaving the relational model.
PostgreSQL's JSONB type stores JSON in a parsed binary format. Unlike plain text JSON columns, JSONB supports GIN indexes, key-existence operators (?), containment operators (@>), and path queries — making it competitive with document databases for many semi-structured data use cases. This is particularly relevant when teams debate SQL vs NoSQL for new projects.
What is MySQL?
MySQL is an open-source relational database originally developed by MySQL AB in 1995. It was acquired by Sun Microsystems in 2008 and then by Oracle in 2010. The Community Edition is released under the GPL (GNU General Public Licence); the Enterprise Edition requires a commercial licence from Oracle. A popular fork, MariaDB, was created by MySQL's original authors after the Oracle acquisition and is largely compatible.
MySQL's architecture supports pluggable storage engines. The default and most important is InnoDB, which provides ACID transactions, row-level locking, foreign key enforcement, and its own MVCC implementation. Older engines such as MyISAM lack transactions but were historically faster for read-only workloads. The Memory engine stores data in RAM for temporary tables.
performance_schema is a MySQL facility that instruments the server's internal execution events — query timing, lock waits, I/O, and memory usage. It is MySQL's primary tool for performance analysis, complemented by the sys schema, which provides human-readable views over performance_schema data.
MySQL's binary log (binlog) records every change made to the database and drives replication. GTID-based replication assigns a globally unique identifier to each transaction, simplifying failover and topology changes. MySQL 8.0 also introduced Group Replication for synchronous multi-primary clusters.
Key differences in depth
Licensing: PostgreSQL Licence vs GPL
The PostgreSQL Licence is a permissive open-source licence similar to the MIT or BSD licences. You can use, modify, and distribute PostgreSQL in proprietary products without obligation to release your source code. This makes it particularly attractive for SaaS companies and cloud providers.
MySQL Community Edition is under the GPL version 2. The GPL's "copyleft" provisions mean that if you distribute software that links against MySQL, you must either release that software under the GPL or purchase a commercial licence from Oracle. For internal use — the most common scenario — this is not an issue. However, it has influenced some companies to prefer PostgreSQL or to use MariaDB as a drop-in replacement.
Full-text search
PostgreSQL's full-text search is based on tsvector (a sorted list of normalised lexemes) and tsquery (a search expression). The to_tsvector() and to_tsquery() functions handle stemming, stop words, and ranking via ts_rank(). GIN indexes make queries fast. It handles multiple languages via configurable dictionaries.
MySQL provides FULLTEXT indexes on InnoDB tables (since MySQL 5.6). Queries use MATCH … AGAINST syntax in BOOLEAN or NATURAL LANGUAGE mode. It is serviceable for basic search but lacks the linguistic depth and composability of PostgreSQL's approach. For production search, both databases are often complemented by Elasticsearch anyway.
Tablespaces
Both databases support tablespaces — the ability to map database objects to specific directories or disk volumes. In PostgreSQL, CREATE TABLESPACE fast_ssd LOCATION '/mnt/ssd' lets you place hot tables on faster storage. InnoDB tablespaces work similarly, with per-table files (innodb_file_per_table) or shared tablespace files.
Code side-by-side
Query JSON data stored in a metadata column:
PostgreSQL (JSONB)
-- Find products where metadata->>'category' = 'books'
SELECT id, name, metadata->>'price' AS price
FROM products
WHERE metadata @> '{"category": "books"}'
ORDER BY (metadata->>'price')::numeric DESC;
-- GIN index for fast key lookups
CREATE INDEX idx_products_meta
ON products USING GIN (metadata); MySQL (JSON column)
-- Find products where category = 'books'
SELECT id, name,
JSON_EXTRACT(metadata, '$.price') AS price
FROM products
WHERE JSON_EXTRACT(metadata, '$.category') = 'books'
ORDER BY JSON_EXTRACT(metadata, '$.price') DESC;
-- Functional index on JSON path (MySQL 8.0+)
ALTER TABLE products
ADD INDEX idx_category
((CAST(metadata->>'$.category' AS CHAR(50)))); Window function: rank users by order total per region:
PostgreSQL
SELECT user_id, region, total,
RANK() OVER (
PARTITION BY region
ORDER BY total DESC
) AS regional_rank
FROM orders; MySQL 8.0+
SELECT user_id, region, total,
RANK() OVER (
PARTITION BY region
ORDER BY total DESC
) AS regional_rank
FROM orders; Window function syntax is identical — MySQL 8.0 added full SQL:2003 window function support, catching up with PostgreSQL.
How engineers talk about PostgreSQL vs MySQL
Understanding the vocabulary used in code reviews, architecture discussions, and incident retrospectives will help you follow and contribute to database conversations in English.
PostgreSQL phrases
- "We store metadata as JSONB so we can query inside it without a schema migration."
- "I added a GIN index on the JSONB column — key lookups are now sub-millisecond."
- "The query uses a window function:
ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY created_at)." - "We set up logical replication to stream changes to the analytics replica without downtime."
- "Vacuum is reclaiming dead tuples left by MVCC — that's expected on a write-heavy table."
- "We installed the pgvector extension to store and query embeddings alongside relational data."
- "The tablespace is pointed at the NVMe volume — index scans are about 3× faster."
MySQL phrases
- "The site runs on a LAMP stack — Linux, Apache, MySQL, PHP — so MySQL was the obvious choice."
- "We enabled GTID-based replication so failover is just a
CHANGE MASTER TOcommand." - "Check performance_schema — there's a lock wait showing up on the users table."
- "We switched every table to InnoDB years ago — MyISAM has no transactions or foreign keys."
- "PlanetScale uses MySQL under the hood with Vitess sharding for horizontal scale."
- "The binary log is enabled for point-in-time recovery and replication."
- "Strict mode was off — MySQL was silently truncating strings to fit the column."
Decision guide: when to choose which
- Complex queries, JOINs, analytical workloads, window functions → PostgreSQL
- Need JSONB, arrays, hstore, or custom data types → PostgreSQL
- Geospatial data (PostGIS) or vector similarity search (pgvector for AI) → PostgreSQL
- Licensing must be fully permissive (SaaS, commercial distribution) → PostgreSQL
- Rich extension ecosystem required → PostgreSQL
- Existing WordPress, Drupal, or LAMP-based stack → MySQL
- Team deeply experienced with MySQL and no compelling feature gap → MySQL (if it works, don't migrate)
- Need deep runtime diagnostics without external tooling → MySQL (performance_schema)
- Greenfield project with no constraints → PostgreSQL (community default)
If you are also deciding between a relational and a non-relational approach, see SQL vs NoSQL. For database deployment in containers and orchestration, see Docker vs Kubernetes.
Key vocabulary
- MVCC (Multi-Version Concurrency Control)
- A concurrency technique where each transaction reads from a snapshot of the database, allowing reads and writes to proceed in parallel without locking each other out. Both PostgreSQL and MySQL's InnoDB use MVCC.
- JSONB
- PostgreSQL's binary JSON type. Stored in a parsed, decomposed format that supports GIN indexing, containment queries (
@>), and key-existence checks (?). More powerful than plain JSON text columns. - Extension
- A PostgreSQL module installed with
CREATE EXTENSIONthat adds new types, functions, index methods, or entire subsystems. PostGIS, pgvector, TimescaleDB, and pg_trgm are common examples. - Tablespace
- A named location on disk where database objects (tables, indexes) are stored. Allows placing hot or large tables on specific storage volumes for performance or capacity reasons.
- InnoDB
- MySQL's default and recommended storage engine. Provides ACID transactions, row-level locking, foreign key enforcement, and MVCC. Replaced the older MyISAM engine (which lacked transactions) as the MySQL default in version 5.5.
- performance_schema
- A MySQL instrumentation layer that records runtime metrics — query latency, lock waits, memory usage, I/O — into in-memory tables. The
sysschema provides readable summary views over this data. - WAL (Write-Ahead Log)
- PostgreSQL's transaction log. All changes are written to the WAL before being applied to data files, ensuring durability and enabling streaming replication to standby servers.
- GTID (Global Transaction ID)
- MySQL's mechanism for assigning a unique identifier to every committed transaction across the replication topology. Makes failover and replica re-pointing simpler and safer.
- Full-text search
- The ability to search natural language text efficiently. PostgreSQL uses
tsvector/tsquerywith stemming and ranking. MySQL usesFULLTEXTindexes withMATCH … AGAINSTsyntax. - Window function
- An SQL function that operates across a set of rows related to the current row without collapsing them into a single output row. Examples:
RANK(),LAG(),SUM() OVER (…). Both databases support them fully in current versions.
Frequently asked questions
What is the main practical difference between PostgreSQL and MySQL?
Both are mature open-source relational databases. PostgreSQL is often called the most standards-compliant and feature-rich option — it has advanced types (arrays, hstore, JSONB), window functions, powerful extensions, and uses MVCC for concurrency. MySQL (and its fork MariaDB) is known for simplicity, fast read-heavy workloads, and its enormous hosting ecosystem, particularly in LAMP stacks.
What is MVCC and does MySQL support it?
MVCC (Multi-Version Concurrency Control) is a technique where each transaction sees a snapshot of the database at the time it started. Readers do not block writers, and writers do not block readers. PostgreSQL uses MVCC throughout. MySQL's InnoDB storage engine also implements MVCC, but PostgreSQL's implementation is generally considered more thorough and better integrated with the planner.
Does PostgreSQL support JSON better than MySQL?
Yes. PostgreSQL's JSONB type stores JSON in a binary, parsed format that supports GIN indexing, efficient key lookups, and a rich set of operators and functions. MySQL has a JSON column type added in 5.7, but its capabilities are more limited and it lacks binary storage. If you need to query inside JSON frequently, PostgreSQL is the stronger choice.
Is MySQL free to use?
MySQL is dual-licensed: the Community Edition is open-source under the GPL, which is free for most uses. However, the GPL can impose obligations if you distribute software that links against MySQL. The commercial Enterprise Edition requires a paid licence. PostgreSQL uses the PostgreSQL Licence — a permissive, MIT-like licence with no commercial restrictions whatsoever.
What are PostgreSQL extensions and why do they matter?
Extensions are pluggable modules that add new data types, functions, index methods, or entire capabilities to PostgreSQL. Key examples: PostGIS (geospatial), TimescaleDB (time-series), pgvector (vector search for AI), pg_cron (scheduled jobs), and pg_trgm (trigram search). MySQL has a plugin architecture but far fewer first-party or ecosystem extensions.
Which databases support PostgreSQL or MySQL on major cloud platforms?
Both are widely supported. AWS offers RDS for PostgreSQL and RDS for MySQL (plus Aurora, which is compatible with both). Google Cloud offers Cloud SQL for both. Azure offers Azure Database for PostgreSQL and Azure Database for MySQL. Supabase and Neon are popular managed Postgres-as-a-service platforms. In practice, cloud support is not a deciding factor between the two.
Can I switch from MySQL to PostgreSQL easily?
Migration is possible but requires meaningful effort. SQL dialects differ: AUTO_INCREMENT vs IDENTITY/SERIAL, backtick identifier quoting vs double-quote, different string functions, and different handling of empty strings vs NULL. Tools such as pgloader can automate schema and data migration, but stored procedures, triggers, and application-level query strings often need manual rewriting.