Skip to main content

PostgreSQL vs MySQL: Which Database Should You Choose in 2026?

Tianzhou · Aug 6, 2026

Update history

  1. Rewrote for MySQL 9.7 LTS and PostgreSQL 19 Beta: Hypergraph Optimizer, JSON Duality Views, Dynamic Data Masking, REPACK CONCURRENTLY, 64-bit MultiXact. Corrected the ONLY_FULL_GROUP_BY and window-frame claims and replaced the FAQ with a workload decision section.
  2. Updated for Postgres 18, MySQL 9.x, Stack Overflow 2025.
  3. Added online DDL.
  4. Added scaling and sharding references.
  5. Initial version.

This post is maintained by Bytebase, an open-source database governance platform that can manage both Postgres and MySQL. We update the post every year.

This is a series of articles between MySQL and PostgreSQL:


For the impatient, jump to the last section to see the comparison table. The References collect many industry best practices.

For the last three years, this comparison had a boring answer. Postgres was what you picked, MySQL was what you inherited. 2026 made the question worth asking again, because three things landed within a few months of each other:

  1. MySQL 8.0, still the most deployed version in the wild, reached end of life in April 2026. The largest install base in the industry has to move whether it wants to or not.
  2. MySQL 9.7 LTS shipped on April 21, 2026, the first LTS since 8.4, and pushed the new optimizer, Group Replication observability, and OpenTelemetry into free Community (my full read).
  3. PostgreSQL 19 Beta 1 shipped on June 4, 2026 with the feature set frozen: in-core plan advice, online REPACK, parallel autovacuum, and the end of the MultiXact ceiling (details).

So a lot of teams are re-picking right now, most of them on 2023 information.

The long-term trend has not reversed. Postgres remains one of the top climbers in the DB-Engines rankings, and in the Stack Overflow survey (2025, 2024) it has been the most admired (65%) and desired (46%) database for the third year in a row.

stackoverflow

MySQL is probably still the world's most popular open source database by install base, while Postgres positions itself as the world's most advanced open source relational database.

At Bytebase, we work with both databases extensively since the Bytebase product needs to integrate with both databases as well as their derivatives. Our founders also build Google Cloud SQL, one of the largest hosted MySQL & Postgres cloud services.

Based on our operating experience, below we compare the two across license, connection model, performance, features, extensibility, usability, operability, and ecosystem.

Unless otherwise specified, the comparison below is between the current stable releases, Postgres 18 vs. MySQL 9.7 LTS (using InnoDB). Postgres 19 items come from Beta 1 and are not GA yet. We also use Postgres instead of PostgreSQL throughout the article, though we know the latter is the official name, which is considered as the biggest mistake in Postgres History.

License

MySQL Community Edition is licensed under GPL. Postgres is released under the PostgreSQL License, a liberal Open Source license similar to the BSD or MIT licenses.

GPL is infectious: distribute MySQL as part of your software and you owe your source under a GPL-compatible license, unless you buy a commercial license from Oracle. And it's Oracle's ownership, not the license, that is why MariaDB forked, and why a few of the capabilities below (masking, JavaScript stored programs) live only in the paid Enterprise Edition. Postgres has no such split.

Connection Model

Postgres uses process per connection where each connection spawns a new process. MySQL uses thread per connection where each connection spawns a new thread.

Postgres gets better isolation this way: an invalid memory access bug crashes one process instead of the whole server. The process model also costs more resources, so a Postgres production deployment should proxy connections through a pooler such as PgBouncer or pgcat. A serverless application that opens connections carelessly will melt a Postgres instance long before it bothers MySQL.

Performance

For most workloads, the performance between Postgres and MySQL is comparable with at most 30% variations. On the other hand, regardless of which database you choose, if your query misses an index, it could be 10x ~ 1000x degradation. The index matters more than the engine.

Postgres 18 introduced an asynchronous I/O subsystem, selected by the io_method variable (io_uring on Linux, a worker-based fallback elsewhere). The first wave covers reads: sequential scans, bitmap heap scans, and vacuum. MySQL's 9.7 release ships no equivalent I/O work; its performance news this cycle is on the planning side, the Hypergraph Optimizer described below.

Saying that, MySQL does have an edge for extreme write-intensive workloads, as Uber and OtterTune both documented. Unless your business reaches Uber-like scale, sheer performance is not a deciding factor. Companies like Instagram and Notion are also able to herd Postgres at super scale.

Features

Object Hierarchy

MySQL employs a 4 level system: Instance.Database.Table.Column

Postgres employs a 5 level system: Instance.Database.Schema.Table.Column (Instance in Postgres is often called Cluster).

The extra level is the first thing that bites during a migration. A MySQL "database" is closer to a Postgres schema than to a Postgres database.

ACID Transaction

Both databases provide ACID transactions. Overall, Postgres provides stronger transaction support:

DatabaseDMLDDL
MySQL until 8.0YesNo
MySQL since 8.0YesSingle statement atomic DDL
PostgresYesYes

The practical consequence is migration safety. A failed multi-statement migration rolls back cleanly on Postgres and leaves MySQL half-applied. Check out Postgres vs. MySQL: DDL Transaction Difference for a detailed analysis.

Security

Postgres gives you Row Level Security (RLS) in core. MySQL gives you column masking, and only in Enterprise.

Both support RBAC. Postgres supports RLS out of the box, while MySQL needs extra views to emulate it. MySQL 9.7 Enterprise moves the other way with a first-class Dynamic Data Masking policy object: attach it to a column and every read path goes through it, including SELECT * and mysqldump. Postgres has no in-core equivalent. Each engine covers the half the other misses. MySQL hides the value, Postgres hides the row.

For authentication, Postgres 18 adds OAuth 2.0 and MySQL 9.1 added WebAuthn. Both have deprecated older methods.

Query Optimizer

Postgres still has the better optimizer out of the box. MySQL 9.7 narrowed the gap for join-heavy queries by moving the Hypergraph Optimizer into Community Edition.

The Postgres advantage is long-standing, and this rant explains why practitioners feel it. Postgres 18 adds skip scan for multicolumn B-tree indexes, and 19 adds pg_plan_advice, an in-core planner-advisor framework, after years of telling users to fix bad plans with pg_hint_plan or SET enable_* flags.

MySQL's classical optimizer is a left-deep, greedy join enumerator: fast to plan, fine on OLTP, out of its depth once an analytical query joins many tables. The Hypergraph Optimizer considers bushy plans and picks hash versus nested-loop per join on cost. Before counting that as a win: it is experimental and off by default, and on Oracle's published TPC-DS run 14 queries regressed by 50% or more even though the wins outnumbered the losses. Turn it on per statement with SET_VAR, not globally.

Online DDL

MySQL has the more complete online DDL story. Postgres closes part of the gap in 19.

Postgres provides online DDL for the following cases:

  1. ADD COLUMN without a default value.
  2. (Postgres 11+) ADD COLUMN with a default value.
  3. Specify CONCURRENTLY when running CREATE INDEX.
  4. (Postgres 18+) NOT NULL constraints can be added without a full table scan.
  5. (Postgres 19 beta) REPACK CONCURRENTLY rebuilds a bloated table without an ACCESS EXCLUSIVE lock, replacing VACUUM FULL, CLUSTER, and the third-party pg_repack.

MySQL covers more of it natively. ALTER TABLE takes an ALGORITHM of INSTANT, INPLACE or COPY, and gh-ost and pt-online-schema-change fill the gaps where it is limited.

Replication

Postgres replicates physically by default, MySQL logically. The bigger difference is failover: MySQL ships it inside the server, Postgres hands it to external tools.

For Postgres, the standard replication is physical replication using WAL, with logical replication available via Publish/Subscribe. Failover orchestration is not in core, so production clusters run Patroni or an equivalent. Postgres 19 does fix the worst logical-replication footgun: sequences behind SERIAL and IDENTITY columns were never replicated, so promoting a subscriber produced primary-key violations at cutover. Publications now support sequences.

For MySQL, the standard replication is logical replication using binlog, and Group Replication provides a multi-primary-capable HA topology with automatic primary election and node eviction inside the server. Until 9.7 the components that told you why the group made a decision were Enterprise-only, so you had a built-in HA story you could not debug at 3am without a contract. 9.7 moves them into Community, which turns a paywalled lead into a real one.

UUIDs

Postgres 18 introduces a native uuidv7() function for timestamp-ordered UUIDs, which keeps global uniqueness without wrecking index locality. MySQL has no native v7 generator. The closest equivalent is UUID_TO_BIN(UUID(), 1), which byte-swaps a v1 UUID into time-ordered binary form.

JSON

Both Postgres and MySQL support a native JSON column. Postgres has the richer query surface: more operators, and indexes on JSON fields via jsonb plus GIN.

MySQL 9.7 answers with something Postgres does not have. JSON Duality Views let an application read and write a JSON document while the server stores normalized relational rows, with INSERT, UPDATE and DELETE now in Community. A constraint violation on the base tables rolls back the document write, instead of being swallowed by an ORM in the middle.

Detailed breakdown in Postgres vs. MySQL: JSON Support.

CTE (Common Table Expression)

Postgres has a more comprehensive support for CTE:

  • SELECT, UPDATE, INSERT, DELETE inside a CTE.
  • SELECT, UPDATE, INSERT, DELETE following a CTE.

MySQL supports:

  • SELECT inside a CTE.
  • SELECT, UPDATE, and DELETE following a CTE.

Window Functions

Both support the standard window function set, ROWS and RANGE frames included. MySQL 8.0 closed the function gap too, so LAG(), LEAD(), FIRST_VALUE(), and LAST_VALUE() are all supported. What Postgres still has over MySQL: the GROUPS frame type, the frame EXCLUDE clause, and an implementation generally considered more efficient.

AI

Postgres wins the vector workload today. pgvector has become the de-facto standard, with both IVFFlat and HNSW indexing. MySQL 9 introduced a VECTOR type with up to 16,383 dimensions, but there is no ANN index in the core server, so approximate search at scale means HeatWave or an external vector store.

Extensibility

Postgres supports extensions, and this is its deepest structural advantage. PostGIS brings geospatial capabilities, pgvector brings vector search, Foreign Data Wrapper (FDW) queries into other data systems, and pg_stat_statements tracks planning and execution statistics. None of these required a change to core Postgres.

MySQL has a pluggable storage engine architecture that gave birth to InnoDB. But InnoDB has become the dominant engine, so the pluggable architecture now serves as an API boundary rather than an extension point. MySQL 9.0+ adds JavaScript stored programs via GraalVM, in Enterprise Edition only.

For auth, both support pluggable authentication module (PAM).

Usability

Postgres is more rigorous while MySQL is more forgivable, though the gap is smaller than its reputation suggests.

  • MySQL is case-insensitive by default. Postgres is case-sensitive by default.
  • MySQL lets you join tables across different databases. Postgres can only join tables inside a single database, unless using the FDW extension.
  • MySQL historically allowed non-aggregated columns in a SELECT with GROUP BY. ONLY_FULL_GROUP_BY has been in the default sql_mode since 5.7, so this only bites legacy configurations that turned it off.

Where the difference is real is the first week. A new engineer is productive with MySQL in a day. Postgres asks more up front: roles, schemas, search_path, vacuum, autovacuum, TOAST, wraparound.

Operability

This is where the 2026 decision actually gets made, and it is the section most comparisons skip.

On the MySQL side, the calendar decides for you. MySQL 8.0 went end of life in April 2026, so the most-deployed version in the world is now unsupported. MySQL runs a three-month Innovation cadence with an LTS every two years, so anyone leaving 8.0 lands on 9.7, or on 8.4 LTS for the more conservative hop. Plan that upgrade as a project, not a patch. We also encountered a few replication bugs when operating a huge MySQL fleet at Google Cloud, though those only surface at extreme load.

On the Postgres side, the tax is vacuum. The XID wraparound issue is the famous one. Its quieter sibling, MultiXact member exhaustion, does not show up on standard XID dashboards and took Metronome down four times in a month in 2025. Postgres 19 widens MultiXact members to 64 bits, eliminating that ceiling rather than raising it, and adds parallel autovacuum so a slow vacuum is finally diagnosable.

Both are mature. Only the shape of the tax differs: MySQL asks for an upgrade project on Oracle's schedule, Postgres asks for a standing understanding of vacuum.

Ecosystem

All common SQL tools support both well. But because Postgres is extensible and still community-owned, its ecosystem has thrived: every application platform offering a hosted database picks Postgres, from Heroku in the early days to Supabase, render, and Fly.io today.

There is also a series of Postgres derived databases targeting different workloads:

This is the one dimension where 9.7 changes nothing.

Postgres or MySQL

PostgresMySQL
Current release18 (19 in beta)9.7 LTS
LicensePostgres License (MIT alike)GPL Community + paid Enterprise
Connection ModelProcess per connectionThread per connection
PerformanceInternet scaleBetter in extreme write-intensive workload
OptimizerBetter default planner, plan advice in 19 betaHypergraph Optimizer in Community, experimental
SecurityRow Level Security in coreColumn masking, Enterprise only
Online DDLPartial, REPACK CONCURRENTLY in 19 betaINSTANT/INPLACE plus gh-ost, pt-osc
HAStreaming replication, failover via PatroniGroup Replication with failover in the server
JSONRicher operators and indexingRead-write Duality Views
Vectorpgvector with HNSW and IVFFlatVECTOR type, no ANN index in core
ExtensibilityExtensionsStorage engine API only
UsabilityRigorous, more up frontForgivable, productive in a day
OperabilityVacuum literacy requiredUpgrade on Oracle's LTS calendar
EcosystemMore hosting providers and derivativesLarge install base

Choosing between Postgres and MySQL is still hard and often causes heated debate. hn

The table settles nothing on its own. The answer depends on which situation you are in, and four of them come up over and over.

Starting a new application. Pick Postgres. Extensions, the ecosystem, and the hiring market all point the same way, and you inherit no upgrade debt. This is where the consensus is right.

Running a MySQL 8.0 fleet. Your decision is not Postgres versus MySQL, it is an in-place upgrade (8.4 or 9.7) versus a migration. Stacking a cross-engine migration onto a forced upgrade is two risky projects on one deadline.

Write-heavy OLTP with short transactions. MySQL is at home here. InnoDB keeps old row versions in the undo log rather than the heap, and that design difference is behind most of the vacuum complaints on the Postgres side.

AI, vector search, or analytics next to OLTP. Postgres, with the extension ecosystem doing the specialized work.

The tiebreaker beats all four: what your team already knows. The sophistication of Postgres does cost some handiness, and a team fluent in MySQL will ship a safer system on MySQL than a shaky one on Postgres. If you are unfamiliar with Postgres, spin up an instance from a cloud provider and run a couple of queries before you commit.


It's also common that Postgres and MySQL co-exist inside an organization. And if you want to manage the database development lifecycle for both of them, please check out Bytebase.

change-query-secure-govern-database-all-in-one

Postgres vs MySQL Comparison Series

References

Scaling and Sharding

Upgrading and Migration

Automation

Schema Migration Tools

MySQL

Postgres

Back to blog

Explore the standard for database governance