Home Posts Postgres to TiDB Migration Guide for Global Scalability
System Architecture

Postgres to TiDB Migration Guide for Global Scalability

Postgres to TiDB Migration Guide for Global Scalability
Dillip Chowdary
Dillip Chowdary
Tech Entrepreneur & Innovator · May 05, 2026 · 10 min read

Bottom Line

The safest Postgres-to-TiDB migration path is a two-phase move: translate schema into MySQL-compatible DDL, then load PostgreSQL data as SQL inserts through TiDB Lightning. For large imports, use TiDB Lightning's local backend; for cutover, plan a brief write freeze because TiDB DM does not ingest PostgreSQL directly.

Key Takeaways

  • TiDB Lightning physical mode is documented at up to 500 GiB/hour for initial loads.
  • TiDB DM supports MySQL-compatible sources, not PostgreSQL, so final cutover needs a write freeze.
  • Use pg_dump with INSERT output for portability, then split files so Lightning can parallelize work.
  • Review PostgreSQL-only features first: sequences, triggers, functions, extensions, and type differences.

Migrating from PostgreSQL to TiDB is less about swapping one SQL engine for another and more about changing the operational model behind your application. TiDB gives you horizontal scale, distributed transactions, and MySQL-wire compatibility, but the move works best when you treat it as a controlled schema translation plus bulk import exercise. As of May 05, 2026, PingCAP's stable self-managed docs are on v8.5, and the most reliable path from Postgres is still dump, transform, load, then cut over.

  • TiDB Lightning physical mode is documented at up to 500 GiB/hour for initial loads.
  • TiDB DM supports MySQL-compatible sources, not PostgreSQL, so final cutover needs a write freeze.
  • Use pg_dump with insert output for portability, then split files so Lightning can parallelize work.
  • Review PostgreSQL-only features first: sequences, triggers, functions, extensions, and type differences.
DimensionPostgresTiDBEdge
Horizontal write scalingUsually vertical first, sharding laterBuilt for distributed scale-outTiDB
SQL compatibility targetNative PostgreSQL ecosystemMySQL 5.7/8.0 compatibilityPostgres for PG-native apps
Bulk initial importStrong native tools, but not TiDB target-readyTiDB Lightning with local or tidb backendTiDB
Global operational modelReplication and partitioning patterns varySingle distributed SQL layerTiDB
Procedures, triggers, PG extensionsRich native supportRequires redesign or removal in many casesPostgres

Why This Migration Works

Bottom Line

Use PostgreSQL only as the export source, not as a replication peer. Translate the schema first, import data with TiDB Lightning, and schedule a short write freeze for the final cutover.

Prerequisites

  • A TiDB cluster with empty target tables.
  • Shell access to PostgreSQL and the TiDB import host.
  • pg_dump from a current PostgreSQL client, ideally aligned with your server major version.
  • TiUP, dumpling, and tidb-lightning installed on the import host.
  • A schema review checklist for PostgreSQL-specific objects: triggers, functions, extensions, partitioning, and sequence usage.
  • Enough disk for source dump files and TiDB Lightning temporary sort data.

This approach works because TiDB ingests MySQL-compatible SQL well, while PostgreSQL's logical dump can emit plain SQL scripts. The catch is compatibility: TiDB is highly compatible with MySQL 5.7 and 8.0, not PostgreSQL syntax. That means your job is to convert the DDL once, then feed TiDB portable insert statements. If you need to clean sample data for rehearsal environments, use TechBytes' Data Masking Tool before exporting production-like records.

When to Choose Postgres vs TiDB

Choose Postgres when:

  • Your application depends heavily on PostgreSQL extensions, stored procedures, or trigger-driven workflows.
  • You do not need multi-node write scale and your current bottleneck is still operational, not architectural.
  • Your team wants to preserve PostgreSQL-native tooling end to end.

Choose TiDB when:

  • You need one logical SQL layer across regions or fast-growing workloads.
  • You are already comfortable standardizing on MySQL-compatible application behavior.
  • You want a high-throughput initial import path with TiDB Lightning and distributed scale-out later.

Step-by-Step Migration

Step 1. Audit the PostgreSQL schema before you export

Do not start with the data. Start by identifying what cannot move as-is. In practice, the most common blockers are PostgreSQL-specific extensions, sequence semantics, trigger logic, and data types that need MySQL-compatible replacements.

psql "$PGURL" -c "\dn"
psql "$PGURL" -c "\dx"
psql "$PGURL" -c "SELECT schemaname, tablename FROM pg_tables WHERE schemaname NOT IN ('pg_catalog','information_schema');"

For each table, decide whether the TiDB version should keep the same shape or simplify it. A typical translation looks like this:

-- PostgreSQL
CREATE TABLE accounts (
  id BIGSERIAL PRIMARY KEY,
  email TEXT NOT NULL UNIQUE,
  is_active BOOLEAN NOT NULL DEFAULT TRUE,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- TiDB-compatible rewrite
CREATE TABLE accounts (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  email VARCHAR(255) NOT NULL UNIQUE,
  is_active BOOLEAN NOT NULL DEFAULT TRUE,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
Watch out: TiDB does not support several MySQL features either, including stored procedures and triggers in self-managed deployments. If your PostgreSQL app relies on those constructs, move the logic into application code or background jobs before migration.

Step 2. Install the TiDB import tooling

PingCAP's documented path is to install TiUP first, then install the components you need.

curl --proto '=https' --tlsv1.2 -sSf https://tiup-mirrors.pingcap.com/install.sh | sh
source ~/.bashrc

tiup install dumpling
tiup install tidb-lightning
tiup --version

If you want to normalize or clean up generated SQL before review, run your translated DDL through the TechBytes Code Formatter. That is especially useful when you are diffing multiple schema revisions under time pressure.

Step 3. Export PostgreSQL as portable SQL inserts

For TiDB, plain SQL is the most straightforward PostgreSQL export format because it avoids PostgreSQL's native COPY syntax. The tradeoff is speed: --inserts and --column-inserts are slower to generate, but far safer for a non-Postgres target.

mkdir -p /data/pg-export

pg_dump "$PGURL" \
  --schema-only \
  --quote-all-identifiers \
  --file=/data/pg-export/00-schema-postgres.sql

pg_dump "$PGURL" \
  --data-only \
  --inserts \
  --column-inserts \
  --quote-all-identifiers \
  --file=/data/pg-export/10-data.sql

Do not import the PostgreSQL schema file directly. Use it only as reference. Create a separate TiDB-ready schema file after translation:

mysql -h ${TIDB_HOST} -P 4000 -u ${TIDB_USER} -p${TIDB_PASSWORD} < /data/pg-export/01-schema-tidb.sql

For large databases, avoid one monolithic data file. TiDB Lightning's docs note that a single SQL file is hard to split efficiently, and file sizes around 256 MiB are a better target. Dump by table or logical group so Lightning can parallelize the load.

Step 4. Prepare TiDB Lightning

For initial imports into empty tables, TiDB recommends the local backend for large volumes. The stable docs compare that mode at 100~500 GiB/hour versus 10~50 GiB/hour for the tidb backend. Use tidb if the dataset is under 1 TiB or if the cluster must stay fully available during import.

[lightning]
level = "info"
file = "tidb-lightning.log"

[tikv-importer]
backend = "local"
sorted-kv-dir = "/data/sorted-kv"

[mydumper]
data-source-dir = "/data/pg-export"

[tidb]
host = "${TIDB_HOST}"
port = 4000
user = "${TIDB_USER}"
password = "${TIDB_PASSWORD}"
status-port = 10080
pd-addr = "${PD_ADDR}"
mkdir -p /data/sorted-kv
tiup tidb-lightning --config tidb-lightning.toml

Step 5. Run the cutover with a short write freeze

This is the operational difference many teams miss. TiDB Data Migration supports MySQL-compatible upstreams, not PostgreSQL. So there is no official PingCAP direct PostgreSQL-to-TiDB incremental replication path to keep tail changes flowing while users continue writing. The practical answer is a controlled freeze window:

  1. Rehearse the migration on a recent snapshot.
  2. Stop application writes to PostgreSQL.
  3. Take the final PostgreSQL data export for changed tables or the full small-delta set.
  4. Import the delta into TiDB.
  5. Run validation queries and smoke tests.
  6. Switch the application connection string to TiDB.

Verify and Cut Over

Expected output

The fastest signal of import success is the TiDB Lightning log. PingCAP's docs call out the phrase below as the success indicator.

grep -n "the whole procedure completed" tidb-lightning.log

You also want application-level checks, not just transport-level checks:

-- PostgreSQL
SELECT COUNT(*) FROM accounts;
SELECT MAX(id) FROM accounts;

-- TiDB
SELECT COUNT(*) FROM accounts;
SELECT MAX(id) FROM accounts;
SHOW TABLES;

At minimum, verify these items before DNS or app cutover:

  • Row counts match for every critical table.
  • Primary and unique key behavior matches your expectations.
  • Boolean, timestamp, and text columns round-trip correctly through the app.
  • Read-after-write paths succeed against TiDB from the real application runtime.
Pro tip: Create a migration scorecard before the window starts: per-table row counts, top 20 critical queries, and a fixed smoke test list. That turns cutover into a checklist instead of an argument.

Troubleshooting

Top 3 issues

  • Import is slow or appears stuck: A single giant SQL file limits TiDB Lightning parallelism. Re-export by table or split files toward 256 MiB chunks, then rerun the import.
  • DDL fails on TiDB: Your translated schema still includes PostgreSQL-native constructs such as sequences, extension types, or trigger-dependent behavior. Rewrite those objects into MySQL-compatible DDL before retrying.
  • Data drift appears at cutover: Writes continued on PostgreSQL after the final export. Because TiDB DM does not consume PostgreSQL directly, the fix is procedural: enforce a clean write freeze and rerun the delta import.

What's Next

  • Benchmark the migrated workload under real concurrency, not only synthetic import speed.
  • Review primary key and hotspot behavior after cutover, especially on append-heavy tables.
  • Automate schema translation rules so future PostgreSQL services follow the same pattern.
  • Turn the migration runbook into an internal standard for rollback, validation, and smoke tests.

Frequently Asked Questions

Can TiDB DM replicate directly from PostgreSQL? +
No. TiDB Data Migration (DM) is designed for MySQL-compatible upstreams, not PostgreSQL. For a Postgres-to-TiDB move, the common production pattern is schema translation plus bulk import, followed by a short write freeze for final cutover.
Which TiDB Lightning backend should I use for a PostgreSQL migration? +
Use backend = "local" for the fastest initial load into empty tables, especially at larger scale. Use backend = "tidb" when the dataset is smaller or when the target cluster must keep serving traffic during the import.
Why use pg_dump with --inserts instead of PostgreSQL COPY output? +
pg_dump --inserts emits portable SQL statements, which are easier to adapt for a non-PostgreSQL target. It is slower than PostgreSQL-native restore paths, but it removes the need to translate COPY syntax during migration.
What PostgreSQL features usually need manual rewrites before moving to TiDB? +
The usual problem areas are sequences and serial columns, trigger-based logic, stored procedures, extensions, and data types that do not map cleanly to MySQL-compatible DDL. Review these before exporting data, because schema issues are harder to untangle after the load starts.

Get Engineering Deep-Dives in Your Inbox

Weekly breakdowns of architecture, security, and developer tooling — no fluff.

Found this useful? Share it.