MongoDB - 对比MySQL与MongoDB:新手该选哪种数据库

110 阅读11分钟

MongoDB - 对比MySQL与MongoDB:新手该选哪种数据库 🤔📊

在开始一个新项目时,开发者常常会面临一个关键决策:用 MySQL 还是 MongoDB?  这个问题看似简单,实则关乎整个应用的架构、性能、可维护性乃至未来的扩展能力。

对于刚入门的开发者来说,面对“关系型”与“非关系型”、“SQL”与“NoSQL”这些术语,很容易陷入迷茫。别担心,本文将用最通俗易懂的方式,带你彻底搞懂 MySQL 和 MongoDB 的核心区别,并通过真实的 Java 代码示例、性能对比图表和适用场景分析,帮助你做出最适合自己的选择。

准备好了吗?让我们一起揭开数据库选型的神秘面纱!🚀


从“表”到“文档”:两种完全不同的数据世界 🌍

要理解 MySQL 和 MongoDB 的区别,首先要从它们存储数据的方式说起。

🔷 MySQL:结构化的表格世界 📊

MySQL 是一个关系型数据库(Relational Database) 。它把数据存储在**表(Table)**中,就像 Excel 表格一样,有固定的行和列。

  • 表(Table) :存储一类数据,比如 users 表。
  • 行(Row) :代表一条记录,比如一个用户。
  • 列(Column) :代表一个字段,比如 nameage
  • 主键(Primary Key) :唯一标识一条记录,比如 id
  • 外键(Foreign Key) :关联其他表,建立数据关系。
-- users 表
+----+--------+-----+---------------------+
| id | name   | age | email               |
+----+--------+-----+---------------------+
| 1  | 张三   | 25  | zhangsan@email.com  |
| 2  | 李四   | 30  | lisi@email.com      |
+----+--------+-----+---------------------+

-- orders 表
+----+---------+------------+---------+
| id | user_id | product    | amount  |
+----+---------+------------+---------+
| 1  | 1       | iPhone     | 6999    |
| 2  | 2       | MacBook    | 12999   |
+----+---------+------------+---------+

运行项目并下载源码sql
123456789101112131415

💡 通过 user_id 外键,可以将 orders 表与 users 表关联起来。


🟩 MongoDB:灵活的文档宇宙 📄

MongoDB 是一个文档型数据库(Document Database) ,属于 NoSQL 家族。它把数据存储为 BSON 文档(Binary JSON) ,类似于 JSON 对象。

  • 集合(Collection) :类似于表,但没有固定的结构。
  • 文档(Document) :基本数据单元,是一个键值对的集合。
  • _id:每个文档的唯一标识,类似于主键。
// users 集合中的文档
{
  "_id": "60d5ecf4f1a2c34567890123",
  "name": "张三",
  "age": 25,
  "email": "zhangsan@email.com",
  "hobbies": ["读书", "游泳"],
  "address": {
    "city": "北京",
    "district": "朝阳区"
  }
}

// orders 集合中的文档
{
  "_id": "60d5ecf4f1a2c34567890124",
  "user_id": "60d5ecf4f1a2c34567890123",
  "product": "iPhone",
  "amount": 6999
}

运行项目并下载源码json
1234567891011121314151617181920

💡 注意:hobbies 是一个数组,address 是一个嵌套对象,这种结构在 MySQL 中需要多个表才能实现。


🔄 数据模型对比图

graph TD
    subgraph MySQL[MySQL - 关系型]
        direction TB
        Users[users 表] -->|JOIN| Orders[orders 表]
        Users -->|JOIN| Profiles[profiles 表]
        style MySQL fill:#f99,stroke:#333
    end

    subgraph MongoDB[MongoDB - 文档型]
        direction TB
        UsersDoc[users 文档] -->|嵌入| Address[address: {city: '北京'}]
        UsersDoc -->|嵌入| Hobbies[hobbies: ['读书', '游泳']]
        UsersDoc -->|引用| OrdersRef[orders: [ObjectId('...')]]
        style MongoDB fill:#9f9,stroke:#333
    end

    MySQL -->|结构固定| A[修改表结构困难]
    MongoDB -->|灵活可变| B[轻松适应需求变化]

运行项目并下载源码mermaid
123456789101112131415161718

核心特性大比拼 🥊

下面我们从多个维度,对 MySQL 和 MongoDB 进行全方位对比。

1️⃣ 数据结构与 Schema

特性MySQLMongoDB
Schema固定(Fixed Schema)动态(Dynamic Schema)
灵活性低。修改表结构(如加列)需执行 ALTER TABLE,可能锁表。高。同一集合的文档可以有不同的字段,支持动态添加。
适用场景数据结构稳定,业务逻辑清晰。数据结构多变,需求迭代快。
🎯 举例说明:

假设产品经理说:“用户现在可以自定义个人资料字段了!”

  • MySQL:你需要执行 ALTER TABLE users ADD COLUMN favorite_color VARCHAR(20);,如果表很大,这个操作可能耗时很长,影响线上服务。
  • MongoDB:无需任何操作!直接在插入文档时加上 "favorite_color": "蓝色" 即可,其他用户没有这个字段也没关系。

2️⃣ 查询语言

特性MySQLMongoDB
语言SQL(Structured Query Language)MongoDB Query Language(基于 JSON)
学习曲线需要学习 SQL 语法(SELECTJOINGROUP BY 等)。更接近编程语言,JSON 风格查询,对开发者更友好。
复杂查询支持复杂的 JOIN、子查询、窗口函数等。支持聚合管道(Aggregation Pipeline)、文本搜索、地理空间查询,但 JOIN 较弱(通过 $lookup 实现)。
🔍 查询示例对比

需求:查询年龄在 20-30 岁之间,且城市为“北京”的用户。

  • MySQL (SQL)

    SELECT u.name, u.age, p.city 
    FROM users u 
    JOIN profiles p ON u.id = p.user_id 
    WHERE u.age BETWEEN 20 AND 30 
      AND p.city = '北京';
    
    运行项目并下载源码sql
    12345
    
  • MongoDB (Query)

    db.users.find({
      "age": { $gte: 20, $lte: 30 },
      "address.city": "北京"
    })
    
    运行项目并下载源码javascript
    运行
    1234
    

💡 显然,MongoDB 的查询更简洁,尤其是当数据是嵌入式结构时。


3️⃣ 性能表现 ⚡

特性MySQLMongoDB
读写性能优秀,但大量 JOIN 或复杂查询时性能下降。写入性能极高,读取性能优秀,尤其适合高并发写入场景(如日志、IoT)。
索引支持 B-Tree、全文索引、空间索引等。支持单字段、复合、文本、地理空间、TTL 等多种索引。
内存使用数据和索引缓存在内存中。使用内存映射文件(Memory-Mapped Files),将数据文件直接映射到内存,读取速度快。
📊 性能对比图(模拟高并发写入)
graph LR
    A[并发写入请求] --> B{数据库}
    B --> C[MySQL]
    B --> D[MongoDB]
    C --> E[性能下降明显]
    D --> F[性能稳定,吞吐量高]
    style C fill:#f99,stroke:#333
    style D fill:#9f9,stroke:#333

运行项目并下载源码mermaid
12345678

4️⃣ 可扩展性 🌐

特性MySQLMongoDB
扩展方式主要靠垂直扩展(升级服务器硬件)和分库分表(Sharding),实现复杂。原生支持水平扩展(Horizontal Scaling),通过分片(Sharding)  轻松将数据分布到多个服务器。
高可用通过主从复制(Master-Slave Replication)实现,故障转移需手动或借助工具。通过副本集(Replica Set)  实现,自动故障转移,高可用性强。
分布式需要额外工具(如 MyCAT、ShardingSphere)或云服务支持。内置分布式能力,适合大规模分布式系统。

5️⃣ 事务支持 🔒

特性MySQLMongoDB
事务支持完整的 ACID 事务,包括多表事务。从 4.0 版本开始支持多文档 ACID 事务,但性能开销较大,通常建议在副本集或分片集群上使用。
适用场景银行转账、订单支付等强一致性要求高的场景。可以支持,但若非必要,建议通过设计避免复杂事务。

Java 代码实战:两种数据库如何操作?💻

下面我们通过 Spring Boot 项目,分别演示如何用 Java 操作 MySQL 和 MongoDB。

🛠️ 环境准备

  • 安装 MySQL 8.0
  • 安装 MongoDB 6.0
  • 创建数据库:test_db

🟦 MySQL + JPA 实现

📦 Maven 依赖
<!-- pom.xml -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

运行项目并下载源码xml
123456789101112131415161718192021
⚙️ 配置文件
# application.yml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC
    username: root
    password: your_password
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: update  # 自动创建/更新表结构
    show-sql: true
    properties:
      hibernate:
        format_sql: true

运行项目并下载源码yaml
1234567891011121314
📄 实体类
// User.java
import lombok.Data;
import javax.persistence.*;

@Data
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    private Integer age;

    @Column(unique = true)
    private String email;
}

运行项目并下载源码java
运行
1234567891011121314151617181920
// UserRepository.java
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    List<User> findByName(String name);
    List<User> findByAgeBetween(Integer minAge, Integer maxAge);
}

运行项目并下载源码java
运行
1234567891011
💻 控制器
// UserController.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/mysql/users")
public class UserController {

    @Autowired
    private UserRepository userRepository;

    @PostMapping
    public User createUser(@RequestBody User user) {
        return userRepository.save(user);
    }

    @GetMapping
    public List<User> getAllUsers() {
        return userRepository.findAll();
    }

    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        return userRepository.findById(id).orElse(null);
    }

    @GetMapping("/search/name")
    public List<User> getUsersByName(@RequestParam String name) {
        return userRepository.findByName(name);
    }
}

运行项目并下载源码java
运行
123456789101112131415161718192021222324252627282930313233

🟩 MongoDB + Spring Data 实现

📦 Maven 依赖
<!-- pom.xml -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-mongodb</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

运行项目并下载源码xml
12345678910111213141516
⚙️ 配置文件
# application.yml
spring:
  data:
    mongodb:
      uri: mongodb://localhost:27017/test_db

运行项目并下载源码yaml
12345
📄 实体类
// User.java
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import java.util.List;

@Data
@Document(collection = "users")
public class User {
    @Id
    private String id;
    private String name;
    private Integer age;
    private String email;
    private List<String> hobbies; // MongoDB 支持直接存储数组
}

运行项目并下载源码java
运行
1234567891011121314151617
// UserRepository.java
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface UserRepository extends MongoRepository<User, String> {
    List<User> findByName(String name);
    List<User> findByAgeBetween(Integer minAge, Integer maxAge);
    List<User> findByHobbiesIn(List<String> hobbies); // 查询包含指定爱好的用户
}

运行项目并下载源码java
运行
123456789101112
💻 控制器
// UserController.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/mongodb/users")
public class UserController {

    @Autowired
    private UserRepository userRepository;

    @PostMapping
    public User createUser(@RequestBody User user) {
        return userRepository.save(user);
    }

    @GetMapping
    public List<User> getAllUsers() {
        return userRepository.findAll();
    }

    @GetMapping("/{id}")
    public User getUserById(@PathVariable String id) {
        return userRepository.findById(id).orElse(null);
    }

    @GetMapping("/search/name")
    public List<User> getUsersByName(@RequestParam String name) {
        return userRepository.findByName(name);
    }

    // MongoDB 特有:根据爱好查询
    @GetMapping("/search/hobbies")
    public List<User> getUsersByHobbies(@RequestParam List<String> hobbies) {
        return userRepository.findByHobbiesIn(hobbies);
    }
}

运行项目并下载源码java
运行
123456789101112131415161718192021222324252627282930313233343536373839

🔍 代码对比分析

方面MySQL + JPAMongoDB + Spring Data
实体类需要 @Entity@Table@Column 等注解,与数据库表强绑定。注解更简洁(@Document),字段定义更自由。
Repository方法名基于 JPA 规范,支持复杂查询。方法名类似,但支持数组、嵌套字段查询更自然。
灵活性添加新字段需修改表结构。直接在实体类添加字段即可,无需改表。
查询能力支持复杂 JOIN 和子查询。支持聚合管道,适合数据处理和分析。

新手该如何选择?🎯 一份决策指南

作为新手,选型时不必追求“最好”,而应选择“最适合”的。以下是几个关键问题,帮你做出决策:

❓ 1. 你的数据结构是固定的还是多变的?

  • 固定(如用户管理、订单系统)→ MySQL
  • 多变(如内容发布、用户行为日志)→ MongoDB

❓ 2. 是否需要复杂的关联查询(JOIN)?

  • 需要(如报表、数据分析)→ MySQL
  • 不需要,或可以通过嵌入式文档解决 → MongoDB

❓ 3. 写入压力大吗?(如 IoT、日志)

  • 写入频繁,高并发 → MongoDB
  • 读写均衡,事务要求高 → MySQL

❓ 4. 是否需要水平扩展?

  • 预计数据量巨大,需分布式部署 → MongoDB
  • 数据量中等,垂直扩展即可 → MySQL

❓ 5. 团队技术栈和经验

  • 熟悉 SQL 和关系型数据库 → MySQL
  • 熟悉 JSON 和现代 Web 开发 → MongoDB

🎯 典型场景推荐

应用类型推荐数据库理由
博客/CMS✅ MongoDB文章内容结构多变,支持富文本、标签、评论嵌入。
电商后台✅ MySQL订单、库存、支付等需要强事务一致性。
社交应用✅ MongoDB用户资料、动态、消息等数据灵活,读写频繁。
物联网平台✅ MongoDB海量传感器数据写入,时间序列存储。
用户管理系统⚖️ 两者皆可结构固定可用 MySQL;若需自定义字段,MongoDB 更优。

总结:没有银弹,只有合适 💎

经过一番深入对比,我们可以得出结论:

  • MySQL 是成熟、稳定、可靠的选择,适合需要强一致性、复杂查询和事务的传统业务系统。
  • MongoDB 是灵活、可扩展、高性能的选择,适合数据结构多变、高并发写入、分布式的现代互联网应用。

🚫 不要迷信“NoSQL 一定比 SQL 快” ,性能取决于具体场景和优化。
🚫 不要认为“MongoDB 不能做事务” ,它支持 ACID,只是使用场景不同。

🧭 给新手的建议:

  1. 从 MySQL 开始:SQL 是数据库的通用语言,掌握 MySQL 能为你打下坚实的基础。
  2. 在合适场景尝试 MongoDB:当你遇到“加字段好麻烦”、“JOIN 查询好慢”时,就是尝试 MongoDB 的好时机。
  3. 两者可以共存:现代应用常采用 Polyglot Persistence(多语言持久化) ,MySQL 处理核心交易,MongoDB 处理日志和用户行为,各司其职。