Skip to content

datos-sistemas/migrar-base-datos

MySQL to PostgreSQL Migration: Proven Plan for 13M+ Rows

Migrate MySQL to PostgreSQL with zero downtime using pgloader, row-level validation, and a rollback plan under 15 minutes. Tested on 13M+ rows.

Pablo Estrada · 13 de mayo de 2026 · 9 min de lectura

Migrating a MySQL database to PostgreSQL in an enterprise environment requires three things no homegrown ETL delivers: rigorous data type mapping, row-by-row validation pre and post cutover, and a rollback plan executable in under fifteen minutes. The difference between a clean migration and one that breaks production is those first two weeks of discovery — not the tools. Here's the plan proven on real projects with millions of rows.

Why migrate to PostgreSQL in 2026?

The five reasons we see consistently across enterprise clients in LATAM.

  1. Native JSONB: business logic with semi-structured data without losing relational integrity
  2. Materialized views with concurrent refresh — something MySQL handles poorly
  3. Native Row Level Security, essential for multi-tenant LATAM deployments
  4. Powerful extensions: PostGIS for geo, pg_trgm for fuzzy search, pgvector for AI
  5. Open source license with no enterprise gotchas like Oracle MySQL's

If your company grew on MySQL Community and now handles geospatial data, AI workloads, or multiple tenants, Postgres is the natural next step.

Type mapping: the phase that breaks the most migrations

MySQL Type PostgreSQL Type Gotcha
TINYINT(1) BOOLEAN Zero to false, non-zero to true
DATETIME TIMESTAMP or TIMESTAMPTZ Decide timezone explicitly
AUTO_INCREMENT SERIAL or IDENTITY Reset sequence after load
TEXT, MEDIUMTEXT, LONGTEXT TEXT (just one) Postgres doesn't differentiate
ENUM CREATE TYPE custom enum Stricter, validates more
DOUBLE DOUBLE PRECISION Nearly identical, no gotcha
GROUP_CONCAT string_agg Different syntax
ON UPDATE CURRENT_TIMESTAMP Explicit trigger Postgres has no shortcut

Every one of these types has a scenario where silent conversion breaks business logic. The boolean is the most painful: a TINYINT(1) with value 2 in MySQL is truthy; after migration it may become true or null depending on the tool. Row-by-row validation catches it; aggregate auditing does not.

The serious migration workflow in seven phases

  1. Full inventory: tables, views, triggers, stored procedures, foreign keys
  2. Type and constraint mapping, table by table, with unit tests
  3. Set up the destination Postgres instance with an identical schema before loading data
  4. Initial load with pgloader or custom ETL, master tables before detail tables
  5. Row-by-row validation: row count, checksum aggregates, 1,000-row sample per table
  6. Logical replication for continuous sync during the cutover window
  7. Application cutover with a feature flag, active monitoring, documented rollback plan

Real case: 98M-row snapshot in 48 hours

We worked with a multinational distributor in Guatemala with SQL Server as the source (not MySQL, but the principle applies identically). Full snapshot with no destination schema: 239 countries, 446 offices, 2.7 million customers and contacts, 98 million rows of historical data.

  • Python snapshot worker with pymssql + PyArrow + Parquet
  • 8-parallel chunking, 50,000-row batches, 10-queries-per-second throttle
  • 197 tables snapshotted
  • 2.7 GB of Bronze Parquet in Supabase Storage
  • 2,528 files in bucket
  • Triple verification: source = Bronze = Silver
  • Zero orphan FKs in destination population
  • 204 real operative offices identified

The opposite of a nightly mysqldump and restoring on top of Postgres and hoping nothing breaks.

The four cutover strategies

Depending on volume and downtime tolerance, one of these four fits your scenario.

Strategy Downtime Suitable Volume
Dump and restore Hours to one day Up to 100GB, weekend window
Batch ETL with freeze 2 to 8 hours 100GB to 1TB, overnight window
Logical replication with switchover Minutes 1TB and above
Parallel dual-write deployment Zero Critical for banking, healthcare, 24/7 retail

Parallel deployment is the gold standard but requires building an application layer that temporarily writes to both databases. Worth it for mission-critical operations.

The seven mistakes that break production

  1. Not backing up before cutover: rollback becomes impossible if something fails
  2. Skipping row-by-row validation: you discover discrepancies weeks later
  3. Changing collation without testing: PostgreSQL handles Unicode differently and sort order changes
  4. Ignoring timezones: TIMESTAMP without TZ loses an hour twice a year
  5. Foreign keys loaded out of order: integrity violations and mass rollback
  6. No rollback plan: if the app goes down, you have thirty minutes before it escalates to the CEO
  7. Zero post-cutover monitoring: silent bugs surface when a customer complains

Build it in-house or bring in outside help?

For volumes under 50GB with a simple schema, an internal DBA with two dedicated weeks can execute this well. For larger volumes, integrations with five or more systems, or a zero-downtime requirement, the external investment pays for itself.

What used to take thirty engineers and eighteen months, we deliver in twelve weeks with MAGIA / Forge methodology and rigorous architecture. No retainers, no tied licenses, code in your name.

Real costs by volume

Volume Internal cost (2 DBAs, 6 weeks) MAGIA / Forge cost
Up to 50GB, simple schema $12,000 $8,000
50–500GB, 5 systems $60,000 $20,000 flat
Over 1TB, mission critical $150,000 From $20,000 with scoped engagement

Beyond price, the key difference is operational documentation. A Catalizadora migration delivers a runbook, rollback plan, and KPIs baseline. Zero post-delivery retainer.

Next steps

If your MySQL is no longer holding up under volume, or you need modern extensions like JSONB, multi-tenant RLS, or pgvector for AI, a free technical session saves you months of costly mistakes. MAGIA / Forge delivers custom software in twelve weeks with active CI/CD and automated testing. MAGIA / Core covers scenarios where you also need a unified data lake and dashboards.

A technical call with the team that actually builds — not with an SDR. No pitch deck.

Preguntas frecuentes

How long does a MySQL to PostgreSQL enterprise migration take?

It depends on volume. Up to 100GB and 100M rows, about one week with a few hours of downtime. With logical replication and parallel deployment, zero downtime, two to four weeks. Databases over 1TB can take two months done right.

What tool should I use to migrate MySQL to PostgreSQL?

pgloader is the open source standard for batch migration. For continuous sync in production, use Debezium with Kafka. For enterprise volumes with heavy ETL, AWS DMS or managed services are a valid option.

Which data types break when migrating from MySQL to Postgres?

TINYINT(1) to BOOLEAN requires explicit conversion; DATETIME loses timezone if you don't normalize to TIMESTAMPTZ; AUTO_INCREMENT shifts to SERIAL or IDENTITY; and MySQL ENUMs don't migrate directly. Map each one before you start.

Can you migrate with zero production downtime?

Yes, with parallel deployment: a new Postgres database running alongside MySQL, continuous sync in one or both directions, and an application switchover once everything validates. Zero downtime is achievable but requires planning.

What does a serious Postgres migration cost?

Between $8,000 and $80,000 depending on volume and complexity. A professional migration includes configured pgloader, row-by-row validation, logical replication for zero-downtime cutover, and a documented rollback plan.

¿Esto aplica a tu operación?

Déjanos tu correo y te escribimos en menos de 24 horas con un diagnóstico inicial sin costo. Sin pitch, sin agenda comercial.

¿Prefieres conversar antes? Agenda 30 minutos con Pablo Estrada — sin pitch deck.

Agendar llamada →