•CASE STUDY

Evaluating and Executing a MySQL to MongoDB Migration

5 min read·821 words·Intermediate

Asked at

1 candidate report in Aug 2026

How to use this case study

SDE-2 / Mid

  • Compare relational (tables, joins, transactions) and document (nested JSON documents) models
  • Say when each fits

SDE-3 / Senior

  • Decide based on access patterns and consistency needs
  • Then plan a safe migration: dual writes or CDC
  • Backfill
  • Validation
  • Shadow reads and cutover with rollback

Staff / Principal

  • Demand evidence for the claimed benefits (benchmarks, metrics)
  • Consider cheaper alternatives (indexes, sharding MySQL)
  • Manage risk and team skills

Problem RestatementProblem

TikTok asked: a team proposes migrating an existing MySQL-backed service to MongoDB. How would you:

  1. decide whether the migration is justified,
  2. compare the data models and consistency needs,
  3. execute the migration safely, and
  4. prove the claimed benefits?

Step 1: Understand the Motivation

Ask "what problem are we solving?". Common reasons:

  • "Schema changes are painful" (the data is naturally nested or varies per record).
  • "We need to scale writes beyond one MySQL primary."
  • "Our queries always load a whole object with 5 joins."
Then ask whether there are cheaper fixes: better indexes, query tuning, read replicas, caching, JSON columns in MySQL, or sharding MySQL (Vitess). A migration is costly and risky, so it must clearly beat these.

Step 2: Compare Models Against Real Access Patterns

AspectMySQL (relational)MongoDB (document)
Data shapeNormalized tables, joinsNested documents (an object and its children together)
TransactionsMature multi-row ACIDMulti-document transactions exist, but best to design single-document updates
SchemaEnforced, migrations neededFlexible (validation optional)
Scaling writesVertical, or manual/Vitess shardingBuilt-in sharding by shard key
QueriesRich joins, ad-hoc SQL, reportingGreat for "get this whole object", weaker for cross-entity joins/analytics
Good fit for MongoDB: each request reads or writes one aggregate (e.g., a user profile with settings and preferences, or a product with variable attributes), rarely joins across aggregates, and needs horizontal scaling. Bad fit: heavy relational queries across entities, strict multi-entity transactions (money, inventory), and lots of reporting SQL.

List the top 10 queries by volume and latency, and model them as documents. If most become single-document reads, that's a good sign. If many need $lookup (joins), it's a warning.

Deep Dive — Executing the migration without a bad weekendDeep dive

The decision is made and the document model is designed. Getting there from a live MySQL service is where migrations actually fail.

Weak

Big-bang cutover in a maintenance window

Freeze writes, export, transform, load, point the service at MongoDB, unfreeze.

Architecture diagram
%%{init: {"look":"handDrawn","handDrawnSeed":7,"theme":"base","fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","themeVariables":{"fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","fontSize":"16px","primaryColor":"#fff4e6","primaryBorderColor":"#1e1e1e","primaryTextColor":"#1e1e1e","secondaryColor":"#e7f5ff","tertiaryColor":"#ebfbee","lineColor":"#1e1e1e","textColor":"#1e1e1e","mainBkg":"#fff4e6","nodeBorder":"#1e1e1e","clusterBkg":"#f8f9fa","edgeLabelBackground":"#ffffff","classText":"#1e1e1e"}}}%%
flowchart LR
  FREEZE["Take the service down"] --> EXP["Export terabytes"]
  EXP --> XF["Transform to documents"]
  XF --> LOAD["Load into MongoDB"]
  LOAD --> SW["Switch the service"]
  SW --> BUG["A transform bug appears under real traffic"]
  BUG --> BACK["Roll back - but MongoDB took hours of writes MySQL never saw"]

The window is sized by the largest table and always overruns. Worse, once writes have gone to MongoDB the rollback path is gone, so the only options are fixing forward under pressure or losing data.

Good

Backfill, then switch

Copy historical data ahead of time and cut over with a much shorter freeze.

The window shrinks, which is the main pain point. But the backfill goes stale from the moment it starts, so there is still a gap to reconcile at cutover, and the first time MongoDB serves production reads is the moment everything depends on it. Any modelling mistake is discovered with all the traffic on it.

Best

Backfill, keep in sync with CDC, then shadow before you switch

Architecture diagram
%%{init: {"look":"handDrawn","handDrawnSeed":7,"theme":"base","fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","themeVariables":{"fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","fontSize":"16px","primaryColor":"#fff4e6","primaryBorderColor":"#1e1e1e","primaryTextColor":"#1e1e1e","secondaryColor":"#e7f5ff","tertiaryColor":"#ebfbee","lineColor":"#1e1e1e","textColor":"#1e1e1e","mainBkg":"#fff4e6","nodeBorder":"#1e1e1e","clusterBkg":"#f8f9fa","edgeLabelBackground":"#ffffff","classText":"#1e1e1e"}}}%%
flowchart LR
  APP["Service"] -->|"writes"| MY[("MySQL - source of truth")]
  MY -->|"CDC from the binlog"| SYNC["Transform to documents"]
  SYNC --> MG[("MongoDB")]
  BF["Backfill job"] --> MG
  APP -->|"shadow reads - result discarded"| MG
  CMP["Diff checker"] --> MY
  CMP --> MG
  CMP --> REP["Mismatch report - fix the transform, replay"]
  MG --> RAMP["Ramp real reads: 1% -> 50% -> 100%"]
  RAMP --> CUT["Move writes last - MySQL stays readable for rollback"]
  • CDC removes the gap. Change data capture from the binlog keeps MongoDB continuously current, so there is no stale backfill to reconcile and no long freeze — cutover becomes a routing decision rather than an operation.
  • Shadow reads prove the model before it matters. Serve from MySQL, also read from MongoDB, discard the result and diff them. Modelling mistakes and transform bugs surface under real traffic with zero user impact, which is the only way to find them honestly.
  • Move reads before writes, gradually. Ramp read traffic and watch latency and diffs; switch writes last, so until that moment MySQL is still the source of truth and rollback is instant.

Pick the shard key from the access patterns before any of this — high cardinality, spreading writes, aligned with the most common query. It is effectively permanent, so it is the one decision worth making slowly.

And keep the diff checker running for a while after cutover. It is the cheapest possible verification that the new model is actually right, and it costs nothing once it is already built.

Step 4: Prove the Benefits

  • Before starting: record baselines (p50/p99 latency of key queries, throughput limits, cost, developer time spent on schema changes, incidents).
  • During shadow reads: compare latency and resource use under real traffic.
  • After: the same metrics, plus cost. If the benefits don't show up, stop at the shadow phase. That's the value of a reversible plan.

Wrap-UpWrap-up

Start from the concrete problem and rule out cheaper fixes, then compare the relational and document models against the service's real top queries and consistency needs (MongoDB fits aggregate-centric, horizontally scaling workloads, while relational fits joins, reporting and multi-entity transactions). If it's justified, migrate with a backfill plus CDC sync, validation, shadow reads, a gradual read-then-write cutover with a reverse-sync rollback path, and prove the gains against recorded baselines.

More Case Studies

Frequently Asked Questions

What is the Evaluating and Executing a MySQL to MongoDB Migration system design question?

Evaluating and Executing a MySQL to MongoDB Migration is a system design interview question asked at FAANG companies. It covers databases, distributed systems and tests your ability to design scalable, production-ready systems. InterviewSkool's breakdown walks you through requirements, API design, architecture, and trade-offs.

Which companies ask the Evaluating and Executing a MySQL to MongoDB Migration question?

TikTok have reportedly asked variations of this question in system design interviews. The exact wording may differ, but the core design challenges remain the same.

How should I prepare for the Evaluating and Executing a MySQL to MongoDB Migration interview question?

Start with the problem statement and scale estimates, then design the high-level architecture. Focus on the core components, data model, and API design. InterviewSkool's breakdown covers the full solution with mermaid diagrams and trade-off analysis to help you prep efficiently.

What level is the Evaluating and Executing a MySQL to MongoDB Migration question?

This question is suitable for SDE-2, SDE-3, and Staff engineer interviews. The level guidance on this page provides specific tips for each level — SDE-2 candidates should focus on core architecture, while Staff engineers should discuss trade-offs, monitoring, and incremental rollouts.

Practice with a Mock Interview

Apply what you learned in a live system design mock interview with InterviewSkool's AI interviewer.

Start System Design Interview →