Zero-Downtime MySQL to PostgreSQL Migration with CDC
Dump-and-restore means downtime. This is the dual-run playbook for migrating MySQL to PostgreSQL with CDC: initial snapshot, catch-up, a concrete cutover checklist, and a real rollback window.
Notes for the problems that show up after launch: bad plans, awkward migrations, index debt, vacuum pressure, replica lag, and the small decisions that make PostgreSQL easier to operate.
Dump-and-restore means downtime. This is the dual-run playbook for migrating MySQL to PostgreSQL with CDC: initial snapshot, catch-up, a concrete cutover checklist, and a real rollback window.
MyRocks swaps InnoDB's B+tree for RocksDB's LSM tree, trading slower range scans for far cheaper writes and better compression. Here is when that trade is worth making — and when it is not.
Partition pruning is free; partitionwise joins and aggregates are not. What enable_partitionwise_join and enable_partitionwise_aggregate actually change, and when they backfire on many partitions.
PostgreSQL's 32-bit transaction IDs wrap around after roughly two billion transactions of visibility horizon. How freezing prevents it, how to monitor age(datfrozenxid), and how to dig out of the emergency.
Semi-synchronous replication promises that a commit is acknowledged by at least one replica before the client sees success. The fine print — silent fallback to async, wait points, and what an ack actually means — is where production incidents live.
A decommissioned standby left its replication slot behind, and over one quiet weekend the slot pinned 214 GB of WAL until the primary ran out of disk and PANIC'd. Here is the mechanism, the monitoring queries, and the circuit breaker that caps the damage.
Your MySQL knowledge is an asset on PostgreSQL, but only after translation. This is the first-week mental-model guide: psql habits, catalogs, roles, config files, the vacuum mindset, and the sharp edges to respect early.
Row counts are not proof. This post covers cross-engine checksums, the collation, float, and timezone traps that make MySQL and PostgreSQL disagree, and how to build reconciliation jobs.
SHOW PROCESSLIST is the first thing everyone runs and the most misread output in MySQL operations. Here is what the thread states actually mean, which pileups matter, and how to build a triage habit around performance_schema.threads.
The processlist fills with 'Waiting for table metadata lock' and the application stops, but the cause is a session doing nothing at all. A field guide to the MDL pileup, the kill-or-wait decision, and the guardrails that make it rare.
At peak our p99 commit latency jumped from 4 ms to 2.8 seconds while CPU pinned at 100% and the disks sat idle. pg_stat_activity showed 1,400 sessions queued on wait_event='WALInsertLock' — this is how we found it and what actually fixed it.
MariaDB has real sequence objects; MySQL never got them. They shine for shared id spaces and ids known before the insert, and they are a waste of a round trip for a single hot table.