MongoDB 是 文档数据库。文档是 BSON,集合里的文档不必同一套字段。它不是分布式文件系统——那是对早期介绍的误译。默认存储引擎是 WiredTiger:文档级锁、压缩、4.0 起副本集上的多文档事务,4.2 起分片集群也能开事务。
| SQL | MongoDB |
|---|---|
| 库 / 表 / 行 / 列 | 库 / Collection / Document / Field |
| 主键 | _id(常为 ObjectId) |
| JOIN | 嵌入文档,或聚合里的 $lookup |
先把数据模型想清楚:经常一起读的嵌进去,一对多很大再拆集合。不要一上来就按第三范式建一堆 $lookup。
先 Replica Set,再考虑 Sharding
老的 Master-Slave 已经废弃。现在的高可用是 Replica Set:

- 写必须上 Primary,再记进 oplog
- Secondary 拉 oplog 复制
- 节点互相心跳,Primary 挂了自动选举
- 可以加 Arbiter 凑投票数,它不存数据
- 读偏好
primary最安全;secondary能分流,但可能读到落后的数据
驱动自己做拓扑发现:拿种子节点发 hello,之后自己盯着谁是 Primary。中间不必再塞一层代理。
数据大到单副本集的磁盘或写入扛不住,再上 Sharding:

- Shard:通常自己就是一个 Replica Set,只存一部分 chunk
- Config Server:chunk 范围和所在 shard
- mongos:应用只连它。带 shard key 的查询走单 shard;不带就 scatter-gather
- Shard key 一旦选定很难改,要选基数高、写入能打散的字段
副本集解决的是「这份数据挂了还能读」。分片解决的是「一份数据放不下、写不进」。先复制,后切开。
聚合是管道,不是另一套 SQL
文档一站一站往下传:$match 过滤,$group 分组,$project 整形,$unwind 拆数组,$lookup 联另一集合。能 $match 的尽早做,少把整表推进后面的阶段。
db.orders.aggregate([
{ $match: { status: "completed" } },
{ $group: { _id: "$user_id", total: { $sum: "$amount" } } }
])
事务、索引、更新时别整单覆盖
多文档事务遵循 ACID,但默认大约 60 秒超时,长事务会拖住 WiredTiger 缓存。集合、索引的创建删除不要放进事务。读关注在事务里常用 snapshot,写关注常用 majority,读偏好必须 primary。
更新用 $set / $inc / $push,不要把整份文档当 replacement 传进去,否则没写到的字段会被抹掉。查询找不到文档是 mongo.ErrNoDocuments,要单独判断。
唯一、稀疏、hashed 索引按查询路径建。hashed 索引常给 shard key 用,不能支持范围查询。
一句话:文档模型先定,副本集先上;分片和跨文档事务都是后加的成本,不是默认标配。