Free — no signup required

Online Schema Migrations in PostgreSQL

2 min read

MySQL has gh-ost and pt-osc. PostgreSQL has its own ecosystem of tools that follow the same shadow table principles but adapted to Postgres internals.

Native PostgreSQL Behavior

PostgreSQL's ALTER TABLE is more nuanced than MySQL's. Some operations are instant and require no table rewrite: adding a nullable column with no default, adding a column with a default (PostgreSQL 11+), or changing a column's type when no rewrite is needed.

However, adding a NOT NULL constraint, changing a column type that requires a rewrite, or adding a constraint that must be validated against all existing rows will still lock the table.

The CREATE INDEX CONCURRENTLY Solution

For index creation, PostgreSQL has a built-in solution: adding the CONCURRENTLY keyword to CREATE INDEX takes roughly twice as long — it builds the index structure, then scans for any rows written during the build, then validates — but it never blocks writes, unlike a standard CREATE INDEX which locks the table for the duration of the build.

pgroll and pg-osc

For complex schema changes in PostgreSQL, tools like pgroll (from Xata) implement the expand-contract pattern automatically. They maintain two versions of the schema simultaneously and use views and triggers to route reads and writes appropriately during the transition window.

Key PostgreSQL-specific gotcha: Adding a column with a NOT NULL constraint and no default requires a full table rewrite even in PostgreSQL. The workaround is the expand-contract pattern: add the column as nullable, backfill the data in batches, then add the NOT NULL constraint — which in PostgreSQL 12+ can be done as a CHECK constraint marked NOT VALID (fast, no full scan) and validated separately with VALIDATE CONSTRAINT (which takes a weaker lock than rewriting the whole table).

This is one of 18 chapters

Every chapter is free — Kubernetes, Terraform, SRE, distributed systems, and more — with fast daily review built in.

Sign up free