MongoDB 与 MySQL 的区别
最高频的题目,几乎是 MongoDB 面试的必问开场。下面按维度逐条对比,最后给选型决策。
总览
| 维度 | MySQL (InnoDB) | MongoDB (WiredTiger) |
|---|---|---|
| 数据模型 | 关系型,二维表,行列固定 | 文档型,BSON,字段可不同 |
| Schema | 强制,变更要 DDL | 不强制(可选 JSON Schema 校验) |
| 查询语言 | SQL(标准、通用) | MQL + 聚合管道(私有) |
| 关联 | JOIN,优化器自动选策略 | $lookup,能力弱、性能差 |
| 事务 | 原生强项,默认 RR | 4.0+ 支持,设计上建议少用 |
| 索引结构 | B+ 树,主键聚簇 | B+ 树,非聚簇 |
| 锁粒度 | 行锁(悲观阻塞) | 文档锁(乐观重试) |
| 水平扩展 | 需要中间件分库分表 | 内置分片 |
| 高可用 | 主从 + 哨兵 / MGR | 副本集(类 Raft,内置) |
| 默认持久化 | 每次提交 fsync | 内存写入即返回(可配) |
| 压缩 | 默认关闭 | 默认 Snappy |
| 嵌套 / 数组 | 要 JSON 列或拆表 | 原生支持,可直接建索引 |
| 生态成熟度 | 极高,几乎所有工具都支持 | 较好但不如 MySQL |
逐维度展开
1. 数据模型与 schema
MySQL 的强 schema 是约束也是保障:类型错了插不进去、字段不存在会报错,数据质量有数据库层兜底。代价是变更成本 —— 加字段要 ALTER TABLE,大表上这是有风险的运维操作。
MongoDB 的灵活 schema 适合两类场景:
- 字段频繁演进 —— 快速迭代的产品,今天加个字段明天改个结构
- 字段本身就不统一 —— 商品属性因品类而异、第三方 API 返回结构不定、埋点事件各有各的 payload
关系库处理第二类场景只有三个不好的选择:大宽表(大量 NULL)、EAV 表(查询极其痛苦)、JSON 列(索引和查询能力有限)。
:::warning 「无 schema」是陷阱式表述 MongoDB 不是不需要设计 schema,而是把 schema 的维护责任从数据库转移到了应用层。实际后果:
- 同一个集合里可能同时存在
userName/username/user_name三种拼写 - 某个字段有时是字符串有时是数字,查询条件写不对
- 老数据缺少新加的字段,代码里到处是空值判断
所以成熟团队都会加 schema validation($jsonSchema)把约束加回来,并在应用层用类型定义(TypeScript interface / Go struct)统一。面试主动指出这一点,比单纯夸「灵活」显得更有经验。
:::
2. 关联查询
这是最实际的差距。 MySQL 的 JOIN 有几十年的优化器积累(选驱动表、选择 Nested-Loop / Hash Join、下推谓词)。
MongoDB 的 $lookup 本质是嵌套循环,且:
- 结果是数组,通常要跟
$unwind - 只有 LEFT OUTER JOIN 语义
- 被关联集合的字段必须有索引,否则灾难
- 分片集合支持有限、不能跨库
所以两者的建模思路根本不同:MySQL 靠拆表 + JOIN,MongoDB 靠嵌入 + 冗余。被问「MongoDB 怎么做多表关联」,标准答案是**「尽量不做 —— 通过嵌入或冗余在建模阶段消除关联需求」**,而不是介绍 $lookup 的用法。
3. 事务
MySQL 的事务是基础能力:ACID 完备、MVCC 成熟、隔离级别可选、性能经过长期优化。
MongoDB 4.0+ 的多文档事务是真 ACID,但有 60 秒生命周期限制、长事务撑内存、分片下走 2PC 开销大,且官方建议优先靠建模规避。
核心差异在设计取向(这是得分点):
- MySQL:规范化拆表 → 跨表一致性只能靠事务 → 事务是必需品
- MongoDB:一个业务对象一个文档 → 单文档操作天然原子 → 事务是逃生舱
所以「MongoDB 里大量用事务」本身是一个建模警告信号。
4. 索引实现的差异
两者都是 B+ 树,但组织方式不同:
InnoDB 聚簇索引 —— 数据存在主键索引的叶子节点里。
- 主键查询:一次树查找直接拿到数据 ✅
- 二级索引查询:先查二级索引拿主键,再回表 ⚠️
- 推论:主键越大,所有二级索引都变大(叶子存主键值);主键随机会导致页分裂
WiredTiger 非聚簇 —— 所有索引叶子存 RecordId,数据单独存。
- 连主键查询都要「回表」一次 ⚠️
- 好处:没有「主键大小影响全部索引」的问题;没有随机主键导致的页分裂悬崖
- 5.3+ 的 clustered collection 可以按
_id聚簇,补上这个短板
MongoDB 索引的独有能力:数组字段(多键索引)、嵌套字段直接建索引("a.b.c")、TTL 自动过期、通配符索引。这些在 MySQL 里要么做不到要么很别扭。
5. 水平扩展
MongoDB 的最硬优势。 分片是官方内置能力:加分片 → balancer 自动迁移 chunk → 应用无感。
MySQL 的分库分表需要 ShardingSphere / Vitess 等中间件或自研,且要自己处理全局 ID、跨库 JOIN、分布式事务、跨库分页。扩容通常意味着双写迁移和停机窗口。
但要公允地说两点:
- MongoDB 分片的分片键选择同样是不可逆的决策,选错了(基数不足、单调递增、不在高频查询条件里)照样是灾难,和 MySQL 选错分片键的后果一样严重
- MySQL 生态现在有 TiDB / OceanBase / PolarDB-X 这类分布式数据库,兼容 MySQL 协议且存储层自动分片,把这个差距大幅缩小了
6. 高可用
| MySQL | MongoDB | |
|---|---|---|
| 组件 | 主从 + 独立的哨兵进程 / MGR | 副本集内置,无需额外组件 |
| 选主 | 哨兵选 leader 再挑新主 | 类 Raft,节点间直接选举 |
| 防脑裂 | 需手动配 min-replicas-to-write | 多数派天然保证,少数派的主自动降级 |
| 故障转移时间 | 取决于配置,通常 10~30 秒 | 约 10~12 秒 |
| 写确认级别 | innodb_flush_log_at_trx_commit + 半同步 | writeConcern(更细粒度,可按请求设) |
MongoDB 在这一层设计更干净 —— 副本集是数据库自身的一部分,不是外挂方案,且 writeConcern 可以按单次操作指定(「这笔是订单,用 majority;这条是日志,用 w:1」),MySQL 只能全局配置。
7. 性能
「MongoDB 比 MySQL 快」是个要拆解的说法:
- 写入 —— 默认配置下 MongoDB 快,但很大程度上是因为默认持久化级别更弱(
w:1, j:false不等刷盘 vs InnoDB 每次提交 fsync)。调成同等可靠性(w:majority, j:true)后差距大幅缩小 - 单文档读 —— 嵌入模型一次读取拿到完整对象,比多表 JOIN 快。但这是建模的胜利而非引擎的胜利
- 复杂查询 / 聚合分析 —— MySQL 的优化器更成熟,复杂 SQL 通常更快且更好写
- 高并发点查 —— 两者都能靠索引和内存做到很好,差异不大
:::tip 这个「默认持久化级别」的点很能加分 面试里说「MongoDB 写入快」很常见,但能主动补一句「不过这部分优势来自默认 writeConcern 更弱,调到同等可靠性后差距会小很多」,立刻显示出你理解的是机制而不是 benchmark 结论。 :::
🔥 选型决策
选 MySQL
- 强事务 + 复杂关联 —— 支付、账务、ERP、库存
- schema 稳定 —— 业务模型清晰且不常变
- 重度分析 / BI —— SQL 的表达力和生态工具不可替代
- 团队和生态 —— 运维经验、监控、备份、人才储备都更成熟
- 默认选择 —— 没有明确理由选别的时,选 MySQL 是更稳妥的工程决策
选 MongoDB
- schema 易变或天然异构 —— 商品属性、埋点事件、CMS 内容、第三方数据
- 以文档为单位读写 —— 一个业务对象一次性存取,没有复杂关联需求
- 数据量预期持续高速增长 —— 需要内置的水平扩展
- 时序 / 日志 / 物联网 —— 配合时序集合和分桶模式
- 快速原型 —— 不想在需求未定型时反复改表结构
- 地理空间查询 ——
2dsphere比 MySQL 的空间扩展更好用
混合使用(现实中最常见)
大中型系统基本都是组合:
MySQL —— 交易核心(订单、支付、账户),需要强一致
MongoDB —— 商品详情、内容、日志、埋点、配置
Redis —— 缓存、会话、排行榜、分布式锁
ES —— 全文搜索、复杂筛选、聚合分析
选型不是二选一,而是「哪部分数据交给哪个存储」。 被问「你会选 MySQL 还是 MongoDB」时,先问清业务场景再给结论,并说明分界线在哪 —— 这比直接站队任何一边都更好。
常见追问
「MongoDB 是 CAP 里的哪个?」 默认偏 CP —— 网络分区时少数派不可写(保一致性)。但通过 readPreference: secondaryPreferred + w: 1 可以偏向 AP。所以准确答案是**「可调」**,CAP 的取舍点暴露给了应用。
「MongoDB 的锁比 MySQL 粒度更细吗?」 现在是同级的(文档 vs 行)。但历史上 MongoDB 很差 —— 2.2 之前是实例级锁,2.2~3.0 是库级锁,3.0 的 WiredTiger 才做到文档级。所以早年「MongoDB 并发差」的印象是真实的,只是已经过时了。知道这段历史能很好地回答这题。
「为什么有些公司从 MongoDB 迁回 MySQL?」 典型原因:当初选 MongoDB 只是为了「不想设计表结构」,但业务逐渐需要强一致和复杂关联;或者建模不当(大量 $lookup、无界数组),性能和维护成本双双恶化。教训是:选 MongoDB 要因为文档模型适合业务,而不是因为想逃避建模。
「MySQL 的 JSON 列能替代 MongoDB 吗?」 部分可以。MySQL 5.7+ 的 JSON 类型支持路径查询和虚拟列索引,适合「大部分字段固定 + 少量扩展属性」的场景。但差距在:JSON 列的索引要手动建虚拟列、没有数组元素索引、没有聚合管道、更新子字段的性能差、没有内置分片。如果异构数据只占一小部分,用 MySQL JSON 列是更省事的选择;如果整个数据模型都是文档形态,用 MongoDB。