MongoDB是什么?架构是怎么样的?

摘要:从一次"关系型数据库表设计改了100次"的架构痛点出发,深度剖析MongoDB文档数据库的灵活性与适用场景。通过文档模型、副本集、分片集群的架构设计,以及与MySQL在Schema、查询、扩展性上的本质差异,揭秘为什么MongoDB适合敏捷开发、为什么游戏和内容平台都用MongoDB、以及分片键的选择如何影响性能。配合架构图展示数据分布,给出Schema设计和索引优化的最佳实践。


💥 翻车现场

周一早上,产品经理又来改需求了。

产品经理:"用户表要加几个字段……"

哈吉米(崩溃):"这是第50次改表结构了!"

MySQL的痛苦

-- 第1版
CREATE TABLE user (
  id BIGINT PRIMARY KEY,
  username VARCHAR(50),
  phone VARCHAR(20)
);

-- 第2版(加字段)
ALTER TABLE user ADD COLUMN email VARCHAR(100);
ALTER TABLE user ADD COLUMN age INT;
-- 耗时:5分钟(1000万数据)

-- 第3版(又加字段)
ALTER TABLE user ADD COLUMN address VARCHAR(200);
ALTER TABLE user ADD COLUMN hobby VARCHAR(500);
-- 耗时:5分钟

-- 第50版……
ALTER TABLE user ADD COLUMN ...
-- 每次都要ALTER TABLE,影响线上服务

问题

1. 表结构固定(Schema严格)
2. 加字段要ALTER TABLE(锁表)
3. 字段类型固定(VARCHAR改TEXT要重建表)
4. 关系复杂(JOIN查询慢)

技术总监:"试试MongoDB,Schema灵活,不用频繁改表。"
哈吉米:"MongoDB是啥?"

南北绿豆和阿西噶阿西来了。

南北绿豆:"MongoDB是文档数据库,Schema灵活,适合快速迭代。"
阿西噶阿西:"来,我给你讲讲MongoDB和MySQL的区别。"


🤔 MongoDB是什么?

MongoDB的定位

阿西噶阿西在白板上写下定义。

MongoDB:
基于文档的NoSQL数据库

核心特点:
1. 文档模型(JSON/BSON)
2. Schema灵活(无固定结构)
3. 横向扩展(分片)
4. 高性能(内存映射)
5. 分布式(副本集、分片)

定位:
适合快速迭代、半结构化数据、大规模读写

MongoDB vs MySQL

数据模型对比

MySQL(关系型):
用户表:
id | username | phone
----|----------|-------
1   | alice    | 138...

订单表:
id | user_id | amount
----|---------|-------
1   | 1       | 100

关系:
用户 1:N 订单(外键关联)


MongoDB(文档型):
用户文档:
{
  "_id": 1,
  "username": "alice",
  "phone": "138...",
  "orders": [           ← 嵌套文档(无需JOIN)
    {"id": 1, "amount": 100},
    {"id": 2, "amount": 200}
  ]
}

特点:
- 一个文档包含所有相关数据
- 无需JOIN
- 查询快(一次查询拿到所有数据)

Schema灵活性

MySQL(Schema严格):
所有记录必须有相同的字段

user表:
{"id": 1, "username": "alice", "phone": "138..."}
{"id": 2, "username": "bob", "phone": "139..."}
{"id": 3, "username": "charlie", "phone": "150...", "email": "..."}  
❌ 错误:email字段不存在,需要先ALTER TABLE


MongoDB(Schema灵活):
每个文档可以有不同的字段

users集合:
{"_id": 1, "username": "alice", "phone": "138..."}
{"_id": 2, "username": "bob", "phone": "139...", "email": "bob@example.com"}  ✅
{"_id": 3, "username": "charlie", "age": 25, "hobby": ["游泳", "跑步"]}  ✅

特点:
- 每个文档独立
- 字段可以不同
- 类型可以不同
- 嵌套任意深度

南北绿豆:"看到了吗?MongoDB不需要ALTER TABLE,直接插入新字段即可!"

哈吉米:"太灵活了!这样就不用频繁改表结构了。"


🎯 MongoDB的核心架构

副本集(Replica Set)

副本集架构(高可用):

┌─────────────────────────────────┐
│         副本集                   │
│                                 │
│  ┌─────────┐                   │
│  │ Primary │  ← 主节点(读写)   │
│  └────┬────┘                   │
│       │ 复制                    │
│  ┌────┴───┬──────────┐         │
│  ↓        ↓          ↓         │
│┌────┐  ┌────┐    ┌────┐       │
││Sec1│  │Sec2│    │Sec3│  ← 从节点(只读)│
│└────┘  └────┘    └────┘       │
└─────────────────────────────────┘

特点:
- Primary挂了,自动选举新Primary(秒级)
- 数据自动同步
- 高可用 ✅

选举机制

Primary挂了:
1. Secondary检测到Primary心跳停止
2. 发起选举(投票)
3. 获得多数票的Secondary升级为Primary
4. 客户端自动连接新Primary

选举时间:约10秒

分片集群(Sharded Cluster)

分片架构(横向扩展):

                  ┌────────────────┐
                  │  Config Server │  ← 配置服务器(元数据)
                  │   副本集3节点   │
                  └────────────────┘
                         ↓
                  ┌────────────────┐
                  │  mongos路由器  │  ← 查询路由(代理)
                  └────────────────┘
                         ↓
    ┌───────────────┬────────────┬───────────────┐
    ↓               ↓            ↓               ↓
┌─────────┐   ┌─────────┐   ┌─────────┐   ┌─────────┐
│ Shard-1 │   │ Shard-2 │   │ Shard-3 │   │ Shard-4 │  ← 分片(每个是副本集)
│副本集   │   │副本集   │   │副本集   │   │副本集   │
└─────────┘   └─────────┘   └─────────┘   └─────────┘

分片规则:
hash(user_id) % 4 → Shard编号

层级关系

Sharded Cluster
  ├─ mongos(路由器)
  ├─ Config Server(配置服务器)
  └─ Shard(分片)
      └─ Replica Set(副本集)
          ├─ Primary
          └─ Secondary

🎯 MongoDB的数据模型

文档结构

// MongoDB文档(BSON格式)
{
  "_id": ObjectId("507f1f77bcf86cd799439011"),  // 自动生成的ID
  "username": "alice",
  "age": 25,
  "email": "alice@example.com",
  "address": {                // 嵌套文档
    "city": "北京",
    "street": "朝阳路100号"
  },
  "hobbies": ["游泳", "跑步"],  // 数组
  "orders": [                  // 嵌套数组
    {
      "order_id": 1001,
      "amount": 100,
      "products": [
        {"name": "手机", "price": 3000}
      ]
    }
  ],
  "created_at": ISODate("2024-10-07T10:00:00Z"),
  "tags": ["VIP", "活跃用户"]
}

好处

1. 灵活:字段可以随意增减
2. 嵌套:相关数据存在一起(无需JOIN)
3. 数组:天然支持多值
4. 类型丰富:字符串、数字、日期、数组、对象...

🎯 什么时候用MongoDB?什么时候用MySQL?

选型对比

场景MySQLMongoDB推荐
结构化数据(订单、用户)✅ ACID事务⚠️ 单文档事务MySQL
半结构化数据(日志、评论)❌ 字段固定✅ Schema灵活MongoDB
频繁改表结构❌ ALTER TABLE慢✅ 直接插入MongoDB
复杂关系查询(多表JOIN)✅ JOIN强大❌ 不支持JOINMySQL
大量写入(日志)⚠️ 写入慢✅ 写入快MongoDB
事务要求高(金融)✅ ACID⚠️ 弱MySQL
快速迭代(创业项目)❌ 改表慢✅ Schema灵活MongoDB

实际应用

用MongoDB

场景1:内容管理系统
- 文章、评论结构不固定
- 嵌套关系多
- 快速迭代

场景2:游戏
- 玩家数据结构多变
- 道具、技能、装备各不相同
- 写入量大

场景3:物联网
- 传感器数据格式多样
- 海量数据
- 时序数据

用MySQL

场景1:电商核心数据
- 订单、用户、商品
- 事务要求高
- 关系查询多

场景2:金融
- 账户、交易
- ACID必须
- 数据一致性

场景3:传统业务
- 表结构稳定
- SQL查询复杂

🎓 面试标准答案

题目:MongoDB是什么?和MySQL的区别?

答案

MongoDB:基于文档的NoSQL数据库

核心特点

  • 文档模型(JSON/BSON)
  • Schema灵活(无固定结构)
  • 横向扩展(分片)
  • 高性能(内存映射)

vs MySQL

特性MySQLMongoDB
数据模型关系型(表、行、列)文档型(集合、文档)
Schema固定(改表要ALTER)灵活(直接插入)
JOIN✅ 支持❌ 不支持(嵌套文档)
事务✅ ACID⚠️ 单文档
扩展垂直(分库分表复杂)横向(分片简单)
查询语言SQLJavaScript/MQL

适用场景

  • MongoDB:Schema多变、嵌套数据、大量写入
  • MySQL:事务要求高、复杂查询、关系复杂

题目:MongoDB的架构是怎样的?

答案

2种架构

1. 副本集(Replica Set)

  • Primary:主节点(读写)
  • Secondary:从节点(只读)
  • 自动故障转移(Primary挂了,选举新Primary)
  • 适用:单机数据量不大(< 1TB)

2. 分片集群(Sharded Cluster)

  • mongos:路由器(查询分发)
  • Config Server:配置服务器(元数据)
  • Shard:分片(每个是副本集)
  • 适用:海量数据(> 1TB)

分片规则

  • 哈希分片:hash(user_id) % N
  • 范围分片:按时间、地区

扩展性

  • 加Shard:线性扩展
  • 自动数据平衡

🎉 结束语

两周后,哈吉米把用户画像数据迁移到了MongoDB。

哈吉米:"用MongoDB后,产品改需求不用改表了,直接插入新字段即可!"

南北绿豆:"对,MongoDB的Schema灵活性适合快速迭代。"

阿西噶阿西:"记住:MySQL适合关系型、事务型,MongoDB适合文档型、灵活型。"

哈吉米:"还有MongoDB的嵌套文档,一次查询拿到所有数据,不用JOIN。"

南北绿豆:"对,理解了MongoDB和MySQL的区别,就知道如何选型了!"


记忆口诀

MongoDB文档数据库,Schema灵活NoSQL
文档模型JSON格式,嵌套数组无需JOIN
副本集高可用,分片集群横向扩展
快速迭代敏捷开发,半结构化数据首选
MySQL关系事务强,MongoDB灵活扩展好