Postgres vs MySQL Performance: 2026 Benchmarks With Sources
Real workload context. Every vendor-sourced figure is labelled. No cherry-picked single-number claims.
Neither PostgreSQL nor MySQL is universally faster. The winner depends on the workload.
Across seven common workloads, MySQL holds a slight edge on simple primary-key reads, sustained insert-heavy writes, and raw connection scaling without pooling. PostgreSQL leads on complex multi-table joins, JSONB / document queries, and analytical (OLAP) workloads. For 99% of applications both are fast enough, so ownership, ecosystem fit, and feature requirements usually matter more than raw benchmark throughput.
| Workload | Winner | Margin |
|---|---|---|
| Simple point-lookup (primary key read) | MySQL | Slight edge; size depends on schema and hardware |
| Complex multi-table joins / analytical queries | Postgres | Workload-dependent; Postgres planner has more join strategies |
| JSON / JSONB query performance | Postgres | Structural advantage for ad-hoc JSON paths; parity for a single known path |
| Insert-heavy / sustained write workloads | MySQL | MySQL InnoDB tuned for sustained writes; Postgres VACUUM overhead can show |
| Connection scaling (1000+ concurrent connections) | MySQL | Without PgBouncer, Postgres degrades sharply. With PgBouncer: near parity. |
| OLAP / analytical queries (without HeatWave) | Postgres | Same planner advantage; Citus and pg_duckdb add columnar |
| Analytics with MySQL HeatWave (on OCI or AWS) | MySQL HeatWave | 400x vs Amazon RDS for MySQL (Oracle's own TPC-H figure) |
Margins are workload-dependent and include vendor-sourced figures (flagged below). Replicate against your own workload before deciding.
Simple point-lookup (primary key read)
Slight edge; size depends on schema and hardware
MySQL InnoDB stores the table itself in primary-key order, so a PK lookup reads the row straight out of the index. PostgreSQL stores rows in a heap, so an index scan finds a tuple pointer and then fetches the row from the heap, unless the query is satisfied by an index-only scan. That extra step is the structural reason MySQL tends to edge simple PK reads. PostgreSQL 18 (September 2025) added asynchronous I/O, which narrows read-side gaps.
Complex multi-table joins / analytical queries
Workload-dependent; Postgres planner has more join strategies
PostgreSQL's planner can cost nested-loop, hash and merge joins and reorder joins across a wider search space, and it parallelises sequential scans, joins and aggregates. MySQL historically planned around nested-loop joins and added hash join only in 8.0.18. On multi-table queries with complex predicates, CTEs and aggregation, that wider strategy set is why analytics-adjacent OLTP tends toward Postgres. This is an architectural reading, not a measured result: run your own queries before treating it as a magnitude.
JSON / JSONB query performance
Structural advantage for ad-hoc JSON paths; parity for a single known path
A Postgres GIN index on a JSONB column answers arbitrary containment and path queries without declaring them up front. MySQL indexes JSON through multi-valued indexes (8.0.17+) for arrays and generated-column or functional indexes for specific scalar paths, so each path you want indexed has to be declared in the schema. Where the query paths are known and stable, MySQL is competitive; where they are ad hoc, Postgres does not need a schema change.
Insert-heavy / sustained write workloads
MySQL InnoDB tuned for sustained writes; Postgres VACUUM overhead can show
PostgreSQL's MVCC leaves a dead tuple behind on every UPDATE and DELETE, so sustained write workloads depend on autovacuum keeping up; when it does not, bloat shows up as degraded throughput. InnoDB instead keeps old row versions in a separate undo log. Postgres is competitive on inserts, but heavy UPDATE workloads are where this surfaces and needs tuning. PG 17-18 vacuum memory management and asynchronous I/O narrowed the gap.
Connection scaling (1000+ concurrent connections)
Without PgBouncer, Postgres degrades sharply. With PgBouncer: near parity.
PostgreSQL starts a separate backend process per connection, so at 1000+ connections memory and context-switch overhead become dominant. MySQL uses a thread per connection, which costs less each. With PgBouncer in transaction mode multiplexing many client connections onto a small server pool, Postgres reaches practical parity for most workloads.
OLAP / analytical queries (without HeatWave)
Same planner advantage; Citus and pg_duckdb add columnar
On a standard non-sharded deployment the analytical edge comes from the same planner breadth as multi-table joins, not from a separate columnar engine: stock Postgres and stock MySQL are both row stores. Postgres has open extensions that add columnar or vectorised execution (Citus, pg_duckdb). MySQL has no open OLAP acceleration layer; HeatWave is proprietary and cloud-only.
Analytics with MySQL HeatWave (on OCI or AWS)
400x vs Amazon RDS for MySQL (Oracle's own TPC-H figure)
MySQL HeatWave's in-memory OLAP acceleration is real for the query patterns it supports. Oracle's headline multiples are vendor-published and each one names a different baseline: 400x versus Amazon RDS for MySQL, 1100x versus Amazon Aurora, 18x versus Redshift, all on TPC-H at Oracle's chosen data sizes. A multiple without its baseline and dataset means nothing, so treat these as vendor claims and test your own workload. HeatWave is available only on OCI and AWS, and independent benchmarks are limited.
The honest closer
Performance is rarely the deciding factor in a Postgres vs MySQL choice. Both databases are fast enough for 99% of applications. The ownership question, ecosystem fit, operational familiarity, and specific feature requirements (PostGIS, JSONB GIN, exclusion constraints, or Vitess sharding) matter more than raw benchmark throughput.
Choose based on the workload decision matrix. Verify performance with your specific query patterns and data volumes before making a final call.