Postgres has the deeper feature set and the more honest engine. MySQL has the broader ecosystem and the easier replication story. New projects without strong reasons should default to Postgres.
PostgreSQL
The most advanced open-source relational database.
Both are mature, free, and run serious workloads at every scale. They differ in shape, not in capability. Postgres is denser with features and type safety; MySQL is denser with operational lore and library support. The right pick usually comes down to which density a team already lives inside.
Quick takes
If you're…
You need CHECK constraints, custom types, or array columns→PostgresNative in Postgres; bolted on or missing in MySQL.
You need JSON querying with real indexes on properties→PostgresJSONB with GIN indexes is faster at scale than MySQL JSON.
You run a read-heavy WordPress, phpBB, or Magento app→MySQLThese stacks were built MySQL-first. The ecosystem assumes it.
You want logical replication with point-in-time recovery→PostgresPostgres ships both logical and physical replication; MySQL binlog is more brittle.
You need full-text search across many languages→Postgrestsvector handles 20+ language stemmers. MySQL FULLTEXT is basic English.
Your team has long-running MySQL ops experience→MySQLFamiliarity matters more than feature parity for most teams.
You need geospatial queries→PostgresPostGIS is the reference implementation; MySQL spatial is rarely used.
You want the cheapest possible managed instance at micro scale→MySQLMySQL on RDS or Cloud SQL is consistently a touch cheaper at tiny sizes.
Full ACID with serializable isolation via Serializable Snapshot Isolation. The default isolation level is Read Committed; serializable is one setting away. Constraints are enforced at the engine level.
MySQL
Full ACID on the InnoDB engine, which is the default since 5.5. The older MyISAM engine, still around for legacy schemas, is non-transactional and silently ignores foreign keys. That has eaten production data more than once.
core
Query optimizer
edge: Postgres
Postgres
Cost-based and statistics-driven. EXPLAIN ANALYZE produces detailed plans you can trust. The planner handles CTEs, window functions, and recursive queries without manual hints.
MySQL
Heuristic with some cost-based elements. Multi-table joins sometimes need optimizer hints to produce sensible plans. Improved in 8.0 but still trails Postgres on the harder cases.
ops
Replication
edge: MySQL
Postgres
WAL streaming for hot standbys, plus logical replication since version 10 for selective table sync. Multi-master needs external tooling like BDR or Citus. Failover is manual unless Patroni or Stolon is wired in.
MySQL
Native primary-replica via binlog. Group Replication and InnoDB Cluster ship in-box for multi-master and automatic failover. This is the one area where MySQL is operationally simpler.
features
Extensibility
edge: Postgres
Postgres
Custom types, operators, aggregate functions, embedded languages (Python, R, V8), and 250-plus extensions on PGXN, including pgvector, PostGIS, TimescaleDB, Citus, and pg_partman.
MySQL
Plugin architecture exists, ecosystem is thinner. Storage engines are pluggable (InnoDB, MyISAM, MyRocks, MEMORY) but rarely changed in production. Vector search and similar extensions trail Postgres by years.
features
JSON support
edge: Postgres
Postgres
JSONB stores parsed binary. GIN indexes on JSONB make property lookups fast. A hybrid relational and document schema can live in one table and one query.
MySQL
JSON type since 5.7. Storage is text-shaped. Functional indexes work but querying is slower at scale than JSONB. Fine for occasional JSON columns; not for JSON as primary storage.
core
Concurrency model
tie
Postgres
MVCC with per-row versioning. Long-running readers do not block writers. Vacuum is the trade-off: dead rows accumulate and autovacuum has to manage them. Tuning matters at high write rates.
MySQL
MVCC on InnoDB with similar guarantees in practice. Undo logs grow instead of dead rows; the purge thread cleans them. Slightly easier steady-state ops for write-heavy workloads.
features
Full-text search
edge: Postgres
Postgres
tsvector and tsquery, with stemmers for 20-plus languages, configurable dictionaries, ranking functions, GIN and GIST indexes. Good enough that apps under roughly 10M documents rarely need a dedicated search engine.
MySQL
FULLTEXT indexes on InnoDB (5.7+) and MyISAM. Basic English stemmer, limited stop-word configuration, no ranking customisation. Useful as a starter; teams outgrow it.
ecosystem
Hosted offerings
edge: Postgres
Postgres
AWS RDS and Aurora, GCP Cloud SQL, Azure Database, plus specialists like Neon, Supabase, Crunchy, Aiven, and Timescale Cloud. Aurora Postgres uses a different storage layer underneath.
MySQL
AWS RDS and Aurora, GCP Cloud SQL, Azure Database, PlanetScale (Vitess-based), Aiven. Strong managed-tier presence but fewer specialist providers.
Benchmark
Read-heavy OLTP, sysbench oltp_read_only
sysbench 1.0.20, 64 client threads, 10M-row table, default tuning, c5.4xlarge (16 vCPU, 64 GB RAM). Numbers approximated from the OnGres "Best practices for benchmarking with sysbench" public study; JSON property lookup added from Crunchy Data's JSONB benchmark.