Back to SQL vs NoSQL
Q2Question 2 of 2

Advantages of MongoDB over MySQL, and where each fits

Key Concepts
  • MongoDB stores flexible, schema-less BSON documents and scales horizontally through built-in sharding and replica sets.
  • MySQL stores structured data in tables with a fixed schema, ACID transactions, foreign keys, and powerful joins.
  • MongoDB's embedded documents avoid joins by nesting related data inside one document for fast single-read access.
  • MongoDB's native horizontal scaling distributes data across many nodes automatically, while MySQL write-scaling requires application-level sharding.
  • MySQL's referential integrity with foreign keys rejects bad data and prevents dangling relationships, which is critical for financial data.
  • MongoDB now supports multi-document ACID transactions, and MySQL supports JSON columns, but the core strengths of each remain different.
  • Use MySQL for the transactional core (accounts, transfers) and MongoDB for flexible, read-heavy, horizontally-scaled workloads (profiles, feeds, catalogs).

Definition

MongoDB wins on flexible schema, embedded documents, and native horizontal scale; MySQL wins on relational integrity, ACID joins, and strong consistency.
  • MongoDB – A document database that stores flexible, schema-less BSON documents and scales horizontally through built-in sharding and replica sets, making it strong for evolving, hierarchical, or denormalized data.
  • MySQL – A relational database with a fixed schema, ACID transactions, foreign keys, and powerful joins, making it strong for structured, highly relational data needing strong consistency.
  • MongoDB advantages – Schema flexibility (no migrations for new fields), embedded documents (avoid joins), and native horizontal scaling (auto-sharding).
  • MySQL advantages – Referential integrity (foreign keys reject bad data), mature ACID transactions across multiple tables, and expressive SQL joins for ad hoc queries.

MySQL is the National Bank of India's strict double-entry ledger where every account, transaction, and customer links together with firm rules and everything balances. MongoDB is a flexible customer folder where each person's folder can hold whatever papers they have, with no requirement that folders match, and the folders can be spread across many rooms to serve millions quickly. You keep the money in the strict ledger and the flexible, fast-changing, read-heavy data in the folders.

Understanding the Concepts

What is MongoDB?

MongoDB is a document database that stores data as flexible BSON (Binary JSON) documents. Each document can have a different structure, and related data can be embedded inside a single document. MongoDB provides built-in sharding and replica sets for horizontal scaling and high availability.

JAVA
package com.bank.service;

import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
import java.util.Arrays;

public class CustomerProfileRepository {

    private final MongoCollection<Document> profiles;

    public CustomerProfileRepository(MongoDatabase db) {
        this.profiles = db.getCollection("customer_profiles");
    }

    public void saveProfile(String customerId, String name) {
        Document profile = new Document("customerId", customerId)
                .append("name", name)
                .append("addresses", Arrays.asList(
                        new Document("type", "home").append("city", "Mumbai"),
                        new Document("type", "office").append("city", "Delhi")
                ))
                .append("preferences", new Document("language", "en")
                        .append("notifications", true));
        profiles.insertOne(profile);
        // No schema migration needed to add "preferences"
    }
}

The document embeds addresses and preferences directly. Adding a new field like preferences requires no migration.

What is MySQL?

MySQL is a relational database that stores data in tables with a fixed schema. Every row must conform to the defined columns and types. Relationships are modeled with foreign keys, and data is queried using SQL with joins.

JAVA
package com.bank.service;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.DataSource;

public class AccountRepository {

    private final DataSource dataSource;

    public AccountRepository(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public BigDecimal getBalance(String accountId) throws SQLException {
        String sql = "SELECT a.balance, c.name "
                   + "FROM accounts a "
                   + "JOIN customers c ON a.customer_id = c.customer_id "
                   + "WHERE a.account_id = ?";
        try (Connection conn = dataSource.getConnection();
             PreparedStatement ps = conn.prepareStatement(sql)) {
            ps.setString(1, accountId);
            try (ResultSet rs = ps.executeQuery()) {
                if (rs.next()) {
                    return rs.getBigDecimal("balance");
                }
                throw new IllegalArgumentException("Account not found: " + accountId);
            }
        }
    }
}

The SQL join links accounts to customers through a foreign key. MySQL guarantees that every customer_id in the accounts table references a valid customer.

How is MongoDB different from MySQL? (Comparison)

The core differences emerge from their data models.

| Feature | MongoDB | MySQL |
|---------|---------|-------|
| Schema | Flexible, schema-less | Fixed, enforced at write |
| Data model | BSON documents, embedded | Normalized tables, joins |
| Transactions | Multi-doc ACID (added later) | Native ACID, battle-tested |
| Scaling writes | Native sharding | Application-level sharding |
| Joins | Not native, application-side | Native SQL joins |
| Integrity | Application-enforced | Foreign keys, constraints |
| Schema changes | Write new fields directly | ALTER TABLE migrations |

JAVA
package com.bank.model;

// MySQL approach: normalized, separate tables
// Customer table: customer_id, name, email
// Address table:  address_id, customer_id (FK), city, type
// To read a customer with addresses: JOIN

// MongoDB approach: embedded, one document
// { customerId: "C001", name: "Priya",
//   addresses: [{type: "home", city: "Mumbai"}, ...] }
// To read a customer with addresses: single findOne()

Why is schema flexibility MongoDB's headline advantage?

MySQL requires columns and types defined up front. Adding a field to a table with billions of rows means an ALTER TABLE migration that can lock the table or take hours to rebuild. MongoDB stores documents with no enforced schema, so different documents in the same collection can have different fields. Adding a field is as simple as writing it in the next insert.

JAVA
package com.bank.service;

import com.mongodb.client.MongoCollection;
import org.bson.Document;

public class ProfileEvolutionExample {

    // Sprint 1: profile has name and email
    public void saveV1(MongoCollection<Document> profiles) {
        profiles.insertOne(new Document("customerId", "C001")
                .append("name", "Rajesh")
                .append("email", "rajesh@nbi.com"));
    }

    // Sprint 2: add mobile number, no migration needed
    public void saveV2(MongoCollection<Document> profiles) {
        profiles.insertOne(new Document("customerId", "C002")
                .append("name", "Priya")
                .append("email", "priya@nbi.com")
                .append("mobile", "+91-9876543210"));
        // C001 does not have "mobile" and that is fine
    }
}

Documents C001 and C002 coexist in the same collection with different shapes. This suits data whose structure changes every sprint.

Why are embedded documents an advantage?

In MySQL, a customer with addresses, orders, and preferences is spread across several tables. Reading the whole picture requires joins across those tables. In MongoDB, you embed related data inside one document, so a single read returns everything without any join.

JAVA
package com.bank.service;

import com.mongodb.client.MongoCollection;
import org.bson.Document;
import static com.mongodb.client.model.Filters.eq;

public class EmbeddedDocumentExample {

    public Document getFullCustomerProfile(MongoCollection<Document> profiles,
                                            String customerId) {
        // One read returns customer + addresses + preferences + recent activity
        Document profile = profiles.find(eq("customerId", customerId)).first();
        // No joins, no N+1 queries
        // The document contains everything the mobile app needs
        return profile;
    }
}

For read-heavy, self-contained entities like customer profiles, this is faster and simpler than joining four MySQL tables.

Why is native horizontal scaling an advantage?

MongoDB has first-class sharding and replica sets built in. Data is automatically distributed across many nodes, and adding capacity is a configuration change, not an engineering project. MySQL scales reads easily with replicas, but scaling writes beyond one node requires application-level sharding that is operationally difficult.

DIAGRAM
MySQL Write Scaling (Hard):
┌──────────┐     ┌──────────┐     ┌──────────┐
│  App 1   │     │  App 2   │     │  App 3   │
│ shard    │     │ shard    │     │ shard    │
│ routing  │     │ routing  │     │ routing  │
└────┬─────┘     └────┬─────┘     └────┬─────┘
     │                │                │
     ▼                ▼                ▼
┌──────────┐  ┌──────────┐  ┌──────────┐
│ MySQL    │  │ MySQL    │  │ MySQL    │
│ Shard 1  │  │ Shard 2  │  │ Shard 3  │
└──────────┘  └──────────┘  └──────────┘
  (Application must route queries to the right shard)

MongoDB Write Scaling (Built-in):
┌──────────┐     ┌──────────┐     ┌──────────┐
│  App 1   │     │  App 2   │     │  App 3   │
└────┬─────┘     └────┬─────┘     └────┬─────┘
     │                │                │
     └────────┬───────┘────────┬───────┘
              ▼                ▼
         ┌──────────┐    ┌──────────┐
         │  mongos   │    │  mongos   │
         │ (router)  │    │ (router)  │
         └────┬──────┘    └─────┬────┘
              │                 │
     ┌────────┼─────────────────┼───────┐
     ▼        ▼                 ▼       ▼
 ┌────────┐ ┌────────┐  ┌────────┐ ┌────────┐
 │Shard 1 │ │Shard 2 │  │Shard 3 │ │Shard 4 │
 └────────┘ └────────┘  └────────┘ └────────┘
  (MongoDB routes automatically via mongos)

When should you use MySQL?

MySQL fits the transactional core: accounts, balances, transfers, orders, and anything that is relational, needs strong consistency, and benefits from joins and constraints.

JAVA
package com.bank.service;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import javax.sql.DataSource;

public class TransferService {

    private final DataSource dataSource;

    public TransferService(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public void transfer(String fromAccount, String toAccount, BigDecimal amount)
            throws SQLException {
        try (Connection conn = dataSource.getConnection()) {
            conn.setAutoCommit(false);
            try {
                execute(conn, "UPDATE accounts SET balance = balance - ? WHERE account_id = ?",
                        amount, fromAccount);
                execute(conn, "UPDATE accounts SET balance = balance + ? WHERE account_id = ?",
                        amount, toAccount);
                conn.commit();
            } catch (SQLException e) {
                conn.rollback();
                throw e;
            }
        }
    }

    private void execute(Connection conn, String sql, BigDecimal amount, String accountId)
            throws SQLException {
        try (PreparedStatement ps = conn.prepareStatement(sql)) {
            ps.setBigDecimal(1, amount);
            ps.setString(2, accountId);
            ps.executeUpdate();
        }
    }
}

The ACID transaction guarantees that Priya's transfer either fully completes or fully rolls back. MySQL's foreign keys ensure every account references a valid customer.

When should you use MongoDB?

MongoDB fits flexible, read-heavy, horizontally-scaled workloads: customer profiles, activity feeds, product catalogs, content management, session data, and event logs.

JAVA
package com.bank.service;

import com.mongodb.client.MongoCollection;
import org.bson.Document;
import java.util.Date;

public class EventLogService {

    private final MongoCollection<Document> events;

    public EventLogService(MongoCollection<Document> events) {
        this.events = events;
    }

    public void logLoginEvent(String customerId, String device, String ip) {
        Document event = new Document("customerId", customerId)
                .append("type", "LOGIN")
                .append("device", device)
                .append("ip", ip)
                .append("timestamp", new Date())
                .append("metadata", new Document("appVersion", "4.2.1")
                        .append("os", "Android 14"));
        events.insertOne(event);
        // High write throughput, auto-sharded across nodes
    }
}

Event logs arrive at high throughput with varying metadata. MongoDB handles this with auto-sharding and no schema constraints.

What are common mistakes?

The biggest mistake is choosing MongoDB for data that is actually relational and needs ACID guarantees.

JAVA
// WRONG: Banking ledger in MongoDB
// Transfer across two documents is NOT atomic by default
// db.accounts.updateOne({accountId: "SAV2001"}, {$inc: {balance: -50000}})
// db.accounts.updateOne({accountId: "SAV2002"}, {$inc: {balance: 50000}})
// If the second update fails, money disappears!

// CORRECT: Banking ledger in MySQL with ACID
// BEGIN; UPDATE accounts SET balance = balance - 50000 WHERE account_id = 'SAV2001';
//         UPDATE accounts SET balance = balance + 50000 WHERE account_id = 'SAV2002';
// COMMIT; -- both succeed or both roll back

The reverse mistake is using MySQL for a high-throughput event stream with a schema that changes every sprint, fighting ALTER TABLE migrations and application-level sharding when MongoDB would handle it natively.

What should a senior engineer know?

MongoDB has added multi-document ACID transactions in version 4.0 and later, and MySQL 8.0 supports JSON columns with indexing. The lines blur, but the core strengths remain different. Heavy use of transactions in MongoDB signals the data model may actually be relational. MySQL's JSON support is a convenience, not a replacement for a native document database.

JAVA
// MongoDB multi-document transaction (available but not the primary design point)
// ClientSession session = client.startSession();
// session.startTransaction();
// accounts.updateOne(session, eq("accountId", "SAV2001"), inc("balance", -50000));
// accounts.updateOne(session, eq("accountId", "SAV2002"), inc("balance", 50000));
// session.commitTransaction();

// If you find yourself writing many multi-document transactions in MongoDB,
// consider whether a relational database is a better fit for that data.

The senior approach is polyglot persistence: use each database where its strengths match the data and access pattern, and accept that a modern system often has more than one data store.

Visual Overview

DIAGRAM
┌─────────────────────────────────────────────────────────┐
│          MongoDB vs MySQL: Where Each Fits              │
├─────────────────────────────────────────────────────────┤
│                                                         │
│   ┌──────────────────────────────────────────────┐     │
│   │           National Bank of India             │     │
│   └──────────────────┬───────────────────────────┘     │
│                      │                                  │
│          ┌───────────┴───────────┐                     │
│          ▼                       ▼                     │
│   ┌─────────────┐        ┌─────────────┐               │
│   │   MySQL     │        │  MongoDB    │               │
│   │ (InnoDB)    │        │ (Sharded)   │               │
│   ├─────────────┤        ├─────────────┤               │
│   │ Accounts    │        │ Profiles    │               │
│   │ Balances    │        │ Activity    │               │
│   │ Transfers   │        │ Events      │               │
│   │ Loans       │        │ Preferences │               │
│   ├─────────────┤        ├─────────────┤               │
│   │ ACID        │        │ Flexible    │               │
│   │ Foreign Key │        │ Embedded    │               │
│   │ Joins       │        │ Sharded     │               │
│   └─────────────┘        └─────────────┘               │
│                                                         │
│          ┌───────────────────────┐                     │
│          │       Redis           │                     │
│          │  Sessions + Cache     │                     │
│          └───────────────────────┘                     │
└─────────────────────────────────────────────────────────┘

Key Takeaway

MongoDB's advantages over MySQL come from its document model and distributed design. The headline advantage is schema flexibility: MySQL requires columns and types defined up front, and adding a field to a huge table means an ALTER TABLE migration that can lock or rebuild it. MongoDB stores documents with no enforced schema, so different documents can have different fields, and adding a field is as simple as writing it in the next insert. This suits data whose shape changes quickly or varies per record, like customer profiles and activity feeds.

The second advantage is embedded documents that avoid joins. Instead of spreading a customer across several MySQL tables and joining them, Rajesh embeds addresses, preferences, and notifications inside one MongoDB document, so a single read returns the whole entity without any join. The third advantage is native horizontal scaling: MongoDB has first-class sharding and replica sets, so scaling write throughput across many machines is a configuration change, while scaling MySQL writes beyond one node requires application-level sharding.

MySQL's advantages are equally important. It enforces a schema and referential integrity through foreign keys, so bad or dangling data is rejected at write time. It provides battle-tested ACID transactions across multiple rows and tables, which is essential for money where a transfer must debit and credit atomically. And its SQL join and aggregation engine answers rich ad hoc questions across normalized data that a document store handles awkwardly, while normalization keeps a single source of truth and avoids the update anomalies that embedded duplication can cause.

At the National Bank of India, Pankaj kept accounts, balances, and transfers on MySQL with InnoDB because transfers must be atomic, foreign keys must hold, and reporting needs joins. For the mobile app's customer profiles and preferences, which varied per customer and changed shape every release, Rajesh used MongoDB so the team could add fields without migrations and read a whole profile in one document. As event volume grew, MongoDB's native sharding handled the load.

Remember one sentence: MongoDB wins on flexible schema, embedded documents, and native horizontal scale, while MySQL wins on relational integrity, ACID joins, and strong consistency, so use each where it fits.

---

Share
Use ← → arrow keys to navigate