Aurora MySQL Migration: Scale Writes, Ditch Limits

hero image

You built on Amazon Aurora MySQL because it promised scale and managed convenience. Now you are hitting walls. Write throughput plateaus even after you add read replicas. You are eyeing application-side sharding, but that means rewriting routing logic, managing cross-shard transactions, and multiplying your operational surface. Every new tenant or workload threatens to push you over the edge.

There is a clearer path forward. TiDB is a distributed SQL database that gives you MySQL horizontal scaling without the architectural gymnastics. Write throughput scales as you add nodes. You keep MySQL compatibility and avoid vendor lock-in. And migration happens with minimal application changes or rewrites: you point your connection strings at a new endpoint. This playbook shows you how to execute an Amazon Aurora MySQL migration that unlocks growth instead of multiplying complexity.

Audience: Storage Leads and SREs

Storage leads own the database platform: capacity planning, cost, the managed-versus-control trade-off, and the long-term shape of your horizontal scaling plan. When Amazon Aurora's architecture constrains growth, they are the ones tasked with finding Aurora alternatives that preserve uptime while enabling the next phase of scale.

SREs live with the consequences. They own uptime SLAs, incident response, and the runbooks that keep services healthy. For them, Amazon Aurora's single-writer ceiling and proprietary features create operational friction that compounds with every new cluster.

If any of these sound familiar, you are the audience:

  • Your write throughput is trending toward the limit of your largest Aurora instance.
  • Your team is designing an application-side sharding scheme to buy headroom.
  • Your best engineers are maintaining routing and re-shard tooling instead of shipping.
  • Amazon Aurora-specific features have quietly spread through your codebase.

What both groups need from an Amazon Aurora migration:

  • Predictable cutover with minimal risk and a clear rollback path.
  • Fewer moving parts than sharding across multiple Aurora clusters.
  • MySQL write throughput that grows with demand, not with architectural complexity.
  • Preserved MySQL high availability without vendor-specific dependencies.

Pain: Amazon Aurora MySQL Limitations Block Growth

Teams do not migrate away from Amazon Aurora on a whim. The decision comes after hitting specific ceilings that force uncomfortable architectural choices. Here is what the breaking point looks like.

The Single-Writer Ceiling

Amazon Aurora separates compute from storage, which is excellent for read scaling: you add read replicas easily and they share the same storage layer. But writes flow through a single writer instance. According to AWS's own figures, Amazon Aurora MySQL processes up to roughly 200,000 writes per second on its largest supported instance class, and its original published benchmark measured about 101,000 write requests per second. Those numbers are ceilings, not floors: they represent the top of what a single writer can do, and once you reach them there is no larger instance to buy. Your real-world ceiling is typically lower, since production workloads rarely match a tuned sysbench benchmark.

As you approach the ceiling, the symptoms are consistent:

  • Write latency climbs: p95 and p99 rise as the writer instance saturates CPU or network.
  • Connection pools exhaust: applications queue waiting for the overloaded writer.
  • Replication lag appears: read replicas fall behind during write bursts, causing stale reads.
  • Instance headroom runs out: you are already on the largest class available.

At this point your options narrow. You can shard at the application layer, which means rewriting core logic and accepting cross-shard transaction complexity. Or you can move to a distributed SQL architecture that scales writes horizontally with minimal application changes or rewrites.

Portability Friction and Vendor Lock-In

Amazon Aurora's proprietary features are convenient early and constraining later. Capabilities like Backtrack and Aurora Serverless do not map cleanly to other MySQL-compatible databases, which locks you into AWS in subtle ways:

  • Migration complexity: moving clouds or on-premises means auditing every Aurora-specific feature and finding equivalents.
  • Negotiation leverage: without a credible exit option, you have less room in pricing discussions.
  • Multi-cloud strategy: data-residency or disaster-recovery goals that span clouds become architecturally hard.

Teams usually realize portability matters at an inconvenient moment: a new region where Amazon Aurora options are limited, a compliance requirement for data residency, or an acquisition running on different infrastructure. By then the Amazon Aurora-specific dependencies are already spread through the codebase.

See how a distributed SQL database removes the single-writer ceiling and scales writes with node count.

Solution: A Distributed SQL Database

TiDB addresses Amazon Aurora MySQL limitations with a different architecture. Instead of a single-writer model, TiDB distributes data and writes across many nodes. You get MySQL horizontal scaling for reads and writes, keep MySQL compatibility for a straightforward migration, and preserve portability across clouds and on-premises.

MySQL Compatibility, Horizontal Scale

TiDB is MySQL compatible at the protocol and syntax level, so existing applications, ORMs, and tools work with minimal modification. Unlike Amazon Aurora's single writer, TiDB distributes writes across the cluster. As you add storage nodes, write capacity grows.

The architecture has three parts:

  • TiDB nodes: a stateless SQL layer that handles queries and transactions. Add nodes to increase query concurrency.
  • TiKV nodes: the distributed storage layer that holds data in Regions. Add nodes to increase storage capacity and write throughput.
  • PD (Placement Driver): coordinates the cluster, manages metadata, and rebalances data automatically as nodes join or leave.
-- Connect with a standard MySQL client
mysql -h tidb-cluster.example.com -P 4000 -u root

-- Run standard MySQL queries
SELECT COUNT(*) FROM orders WHERE created_at > NOW() - INTERVAL 1 DAY;

This distribution removes the need for application-level sharding. The database grows by adding nodes, not by fragmenting your data model or your routing logic. Because writes spread across TiKV nodes rather than funneling through one writer, write capacity scales roughly linearly with node count, so you raise the ceiling by adding hardware instead of hitting a single-instance wall. Exact throughput depends on your schema, workload, and hardware, so benchmark against your own workload rather than a headline number.

Built-In HA and Operational Simplicity

MySQL high availability in TiDB is automatic and does not depend on proprietary services. Data replicates using the Raft consensus algorithm across multiple nodes, typically three replicas. If a node fails, the cluster elects a new leader for the affected Regions within seconds, with no manual failover orchestration.

  • No single-writer bottleneck: writes distribute across the cluster.
  • Automatic rebalancing: PD redistributes data when you add or remove nodes.
  • Online schema changes: add indexes or modify schemas without downtime.
  • Unified monitoring: built-in dashboards cover cluster health and query performance across all nodes.
  • Standard backup and restore: point-in-time recovery works the same way regardless of cluster size.

With TiDB you manage one logical database instead of a fleet of Aurora clusters. Backups, monitoring, access control, and capacity planning operate at the cluster level, not per shard.

With vs. Without TiDB: Breaking the Wall

When you hit Aurora's write ceiling, one path leads to sharding complexity and the other to horizontal scale without architectural surgery.

Without TiDB: Shards, Routers, Runbooks

The conventional answer to Aurora's write ceiling is more Aurora clusters plus application-level sharding. It is well documented and painful.

  • Design a shard key that distributes writes evenly.
  • Implement routing logic to direct queries to the correct shard.
  • Rewrite cross-shard queries as scatter-gather patterns.
  • Handle cross-shard transactions, which often means giving up ACID guarantees.
  • Build re-sharding tools for when data distribution skews.
# Example: app-side routing logic (illustrative)
def get_shard_for_user(user_id):
    shard_number = hash(user_id) % TOTAL_SHARDS
    return SHARD_CONNECTIONS[shard_number]

# Cross-shard reads become an application problem
def get_recent_orders_all_users(cutoff):
    results = []
    for shard in SHARD_CONNECTIONS:
        partial = shard.query(
            "SELECT * FROM orders WHERE created_at > ?", cutoff)
        results.extend(partial)
    return sorted(results, key=lambda x: x.created_at)
  • Per-shard runbooks: failover, backup, and monitoring configs multiply by shard count.
  • Coordinated changes: schema changes and deployments must line up across every shard.
  • Uneven load: some shards run hot while others sit idle, with no easy rebalance.
  • Cost growth: each cluster needs multiple instances for HA, so shard count multiplies your instance count.
  • Velocity drag: every new feature has to account for sharding and cross-shard edge cases.

With TiDB: Linear MySQL Write Throughput

TiDB removes the sharding logic by distributing writes at the storage layer. Your application sees one logical database. The cluster handles distribution, replication, and rebalancing.

-- Same query, distribution handled transparently
INSERT INTO orders (user_id, product_id, amount)
VALUES (12345, 67890, 99.99);
-- The application does not know which node stores this row.
  • Single endpoint: applications connect to one cluster address.
  • Unified monitoring: one dashboard for cluster-wide health and performance.
  • Standard SQL: cross-partition queries just work, with no scatter-gather.
  • Consistent transactions: full ACID across the cluster, not per-shard compromises.
  • One backup schedule: point-in-time recovery covers the whole cluster.
CapabilityAurora ShardingTiDB
Write throughputCeiling per cluster; grows only by adding shardsGrows with node count
Application changesExtensive: routing, cross-shard logic, transaction compromisesMinimal: same MySQL protocol and SQL
Operational complexityMultiplies with shard countConstant: one cluster
Cross-partition queriesApplication responsibility (scatter-gather)Native SQL joins and aggregations
RebalancingManual re-shardingAutomatic (PD)
ACID guaranteesPer-shard; cross-shard is eventualFull ACID across the cluster
Table 1: Aurora application-level sharding versus TiDB distributed SQL across key operational dimensions.

Migration Flight Plan: Zero-Drama Aurora MySQL Migration

Migrating from Aurora to TiDB follows a structured path that minimizes risk and keeps service continuity. Here is the flight plan.

Phase 1: Discovery and Scope

Inventory your Aurora usage to find dependencies and compatibility concerns: Aurora-specific features, storage-engine variations, and custom extensions.

-- Inventory Aurora-specific variables
SHOW VARIABLES LIKE 'aurora%';

-- Identify the largest tables for migration planning
SELECT TABLE_SCHEMA, TABLE_NAME,
       ROUND((DATA_LENGTH + INDEX_LENGTH)/1024/1024/1024, 2) AS size_gb,
       TABLE_ROWS
FROM information_schema.TABLES
WHERE TABLE_SCHEMA NOT IN ('mysql','information_schema','performance_schema')
ORDER BY (DATA_LENGTH + INDEX_LENGTH) DESC
LIMIT 20;

Document the Aurora-specific features your application relies on. Most have TiDB equivalents or can be refactored, but knowing them up front prevents cutover surprises.

Phase 2: Compatibility and DDL Validation

Normalize dialect differences and validate the schema on a TiDB staging cluster.

# Export the Aurora schema (illustrative)
mysqldump --no-data --skip-triggers --triggers \
  -h aurora-cluster.amazonaws.com -u admin -p mydatabase > schema.sql

# Import on a TiDB staging cluster (illustrative)
mysql -h tidb-staging.example.com -P 4000 -u root mydatabase < schema.sql

Run your application's integration tests against staging. This confirms queries behave correctly and surfaces any differences early.

Phase 3: Data Migration

Choose your tool by downtime tolerance and data volume. For minimal downtime, use TiDB Data Migration (DM) or AWS Database Migration Service to replicate continuously from Aurora to TiDB.

# TiDB Data Migration task (illustrative)
name: aurora-to-tidb
task-mode: all   # full load + incremental
target-database:
  host: "tidb-prod.example.com"
  port: 4000
mysql-instances:
  - source-id: "aurora-source"
# Start replication and monitor lag
tiup dmctl --master-addr <addr>:8261 start-task dm-task.yaml
tiup dmctl --master-addr <addr>:8261 query-status aurora-to-tidb

Phase 4: Cutover

Execute the switchover when replication lag is minimal and validation passes. Two strategies:

Dual-write (lower risk, requires application changes): write to TiDB as the new source of truth and to Aurora as a rollback safety net during a validation window, then remove the Aurora writes.

Direct switchover (faster, brief write freeze):

  1. Set Aurora to read-only.
  2. Wait for replication lag to reach zero.
  3. Run validation queries to confirm data parity.
  4. Point application connection strings at TiDB.
  5. Resume writes and monitor.
  6. Verify HA failover behaves as expected.
-- Validation pattern: compare counts across critical tables
-- Run on both Aurora and TiDB immediately before cutover
SELECT 'orders' AS table_name, COUNT(*) AS row_count,
       MAX(created_at) AS latest FROM orders
UNION ALL
SELECT 'customers' AS table_name, COUNT(*) AS row_count,
       MAX(updated_at) AS latest FROM customers;

Phase 5: Performance Tuning

After cutover, tune for your workload: set placement policies for hot data, configure resource control if needed, and scale out to meet your throughput targets.

-- Find the slowest statements post-migration
SELECT digest_text,
       SUM(sum_latency)/SUM(exec_count)/1000000 AS avg_ms,
       MAX(max_latency)/1000000 AS max_ms,
       SUM(exec_count) AS exec_count
FROM information_schema.cluster_statements_summary
GROUP BY digest, digest_text
ORDER BY avg_ms DESC LIMIT 20;

Map your Aurora usage into a phased migration sequence with clear validation gates and a clean rollback path.

Day-2 Ops: High Availability at Scale

After migration, build operational patterns that use TiDB's architecture instead of inheriting Aurora's complexity.

Capacity Planning for Sustained Growth

Unlike a single-writer ceiling, TiDB capacity planning is predictable: monitor write-throughput trends and add nodes before you hit constraints. A good rule is to scale out when sustained writes approach roughly 80% of current cluster capacity.

Unified Monitoring and Alerting

TiDB provides built-in dashboards and Prometheus metrics for cluster-wide observability. Key metrics: query latency (p95, p99), write throughput, Region distribution balance, storage utilization per node, and replication lag.

Standardized Backup, Restore, and DR

Use TiDB's Backup & Restore (BR) for cluster-wide backups. One schedule covers the whole distributed database instead of per-shard choreography.

# Illustrative BR full backup
tiup br backup full \
  --pd "${PD_IP}:2379" \
  --storage "s3://backups/tidb/$(date +%Y%m%d)"

Proof and Next Steps

Real teams have moved off Aurora's single-writer constraints onto TiDB and kept performance predictable as workloads grew.

Plaid: Migrating 234 Databases off Amazon Aurora MySQL

Plaid migrated from Amazon Aurora MySQL to TiDB to escape the reliability, maintenance, and scaling limits of a single-writer architecture. The scale of the project is the proof point: a six-person team moved 234 databases across roughly 100 services from Aurora to self-hosted TiDB over about two and a half years, using a phased approach that started with non-critical services and frontloaded the riskiest Tier 0 workloads.

The operational results are the part worth showing leadership:

  • Upgrade effort collapsed. In early 2023, upgrading from Amazon Aurora 5.6 to 5.7 cost Plaid 104 minutes of planned downtime and 26 engineering weeks. In 2025, upgrading all TiDB clusters took about one engineering week with zero downtime, eliminating 100% of the downtime and 96% of the effort.
  • Cutovers got faster and safer. As their automation compounded, per-service cutovers dropped from three to four weeks to around one week, with write downtime falling from roughly five minutes to under 60 seconds.
  • Scaling stopped being an event. TiDB's online schema changes and horizontal scale-out let Plaid handle demand spikes and modify large tables without taking services offline, addressing the exact single-writer pain that drove the migration.

Plaid evaluated alternatives including Google Spanner and chose TiDB for its MySQL compatibility, horizontal scalability, and distributed transactions, a direct match for teams looking for an Amazon Aurora alternative that does not require rewriting the application layer.

Next Steps

If you are ready to validate an exit plan with real workload data, these steps get you to a go/no-go decision quickly.

Try TiDB Cloud

Run your real workload patterns against a single SQL endpoint and turn "it should work" into proof. Real results without shard boundaries, and a before/after ops comparison you can take to leadership.

Book a Workshop

A working session that maps your Aurora usage into a phased migration sequence with clear validation gates and a clean rollback path.

FAQs

Near-zero-downtime migration is the standard approach. Using TiDB Data Migration or AWS DMS, you perform a full load followed by continuous replication, then cut over during a short write freeze once replication lag reaches zero and validation passes.