MongoDB — 文档数据库代表
待复核MongoDB 是一个把数据存成 BSON 文档的数据库。日常类比:关系数据库像表格,每一行都要按固定列填写;MongoDB 像一份份档案袋,每个档案袋里可以放姓名、地址、购买记录、嵌套清单这些结构化材料。
它的基本单位不是”行”,而是文档。文档看起来很像 JSON,但实际存储格式是 BSON:二进制、更适合机器读写,也能表达日期、ObjectId、长整数等 JSON 原本没有的类型。
项目仓库里最重要的两个可执行组件是 mongod 和 mongos:前者是真正存数据的数据库服务器,后者是分片集群里的查询路由器。这个拆分说明 MongoDB 从一开始就不只想做”单机 JSON 存储”,而是想把文档模型扩到高可用和水平扩展。
不理解 MongoDB,下面这些事很难解释:
- 为什么很多 Web 应用喜欢把用户资料、订单明细、评论列表放进一个文档,而不是拆成十几张表再 JOIN 回来
- 为什么
_id会自动生成、默认唯一,并且几乎所有 MongoDB 文档都围着它组织 - 为什么副本集是生产部署的基本单位:单节点能跑,但挂了就没主库;副本集才能选新主
- 为什么分片不是”把文件夹分几份”这么简单,而是要选分片键、走
mongos路由、让后台均衡器迁移数据段
MongoDB 的核心可以抓 三个关键词:
-
BSON 文档模型:一条记录可以有嵌套对象和数组。类比:一份简历可以直接写教育经历、项目经历、联系方式,而不是把每一段拆到不同表格再编号关联。好处是读取一个业务对象很顺;代价是模型设计要提前想清楚哪些字段会一起读、哪些会无限增长。
-
副本集:多个
mongod保存同一份数据,只有 primary 接收写入,secondary 从 primary 的 oplog 异步追数据。类比:一个班有班长和备份记录员,班长请假时大家投票选新班长。好处是自动故障转移;代价是短暂选举期间可能没有可写主节点。 -
分片集群:数据按分片键切成范围或哈希段,分布到多个 shard;客户端连接
mongos,由它决定请求去哪里。类比:仓库按订单号把包裹分到多个分拣中心,前台只负责收单和查路由。好处是横向扩容;代价是分片键选错会让所有请求挤到一个 shard。
案例 1:商品详情直接存成一份文档
Section titled “案例 1:商品详情直接存成一份文档”db.products.insertOne({ sku: "book-001", title: "MongoDB Guide", price: 59, tags: ["database", "nosql"], stock: { warehouseA: 12, warehouseB: 4 }})
db.products.find({ "stock.warehouseA": { $gte: 10 } })逐部分解释:
tags是数组,stock是嵌套文档,一条商品记录就能装下常一起读取的信息"stock.warehouseA"是点符号,用来查嵌套字段- 这个模型适合商品详情页:页面打开时通常需要标题、价格、标签和库存一起回来
案例 2:三节点副本集保住可用性
Section titled “案例 2:三节点副本集保住可用性”rs.initiate({ _id: "rs0", members: [ { _id: 0, host: "mongo0.example.net:27017" }, { _id: 1, host: "mongo1.example.net:27017" }, { _id: 2, host: "mongo2.example.net:27017" } ]})
rs.status()逐部分解释:
rs0是副本集名字,同一组成员必须认同这个名字- 三个 member 都保存数据;奇数节点更容易在故障时形成多数派
rs.status()用来确认谁是 primary、谁是 secondary,以及是否正在选举
案例 3:订单集合按用户哈希分片
Section titled “案例 3:订单集合按用户哈希分片”sh.addShard("ordersA/a1.example.net:27018,a2.example.net:27018,a3.example.net:27018")sh.addShard("ordersB/b1.example.net:27018,b2.example.net:27018,b3.example.net:27018")
sh.shardCollection("shop.orders", { userId: "hashed" })db.orders.find({ userId: "u123" })逐部分解释:
sh.addShard()把两个 shard replica set 加入集群{ userId: "hashed" }让用户 ID 被哈希后分布,避免递增 ID 把写入压到同一台机器- 查询带上
userId时,mongos更容易把请求路由到目标 shard,而不是广播给所有 shard
-
把 MongoDB 当”随便塞 JSON”:字段可以灵活,不代表模型可以乱;嵌套数组无限增长会让单文档越来越难更新。
-
忘记 16 MB 文档上限:BSON 单文档有大小限制,大附件和超长历史记录应该拆出去或用专门存储。
-
只用
w:1就以为安全:primary 确认不等于多数副本确认,故障切换时未复制到多数派的写入可能回滚。 -
分片键凭直觉选:低基数字段、单调递增字段、查询很少携带的字段,都可能造成热点或广播查询。
适用 vs 不适用场景
Section titled “适用 vs 不适用场景”适用:
- 用户资料、商品详情、内容管理等”一个业务对象天然是一份文档”的场景
- schema 会演进、字段差异较大的 Web / 移动应用后端
- 需要副本集高可用、读扩展、变更流或地理位置查询的业务系统
- 数据量和写入量会横向增长,且能设计出合理分片键的集合
不适用:
- 强依赖复杂 JOIN、固定报表和严谨关系约束的系统,优先考虑 postgresql / mysql
- 金融账本这类要求强事务语义极清晰的核心链路,不能只靠”文档灵活”做设计
- 小数据量单机足够的项目,分片集群的运维复杂度不值得
- 分片键无法稳定预测的业务,强行分片会把问题从单机性能变成集群治理
历史小故事(可跳过)
Section titled “历史小故事(可跳过)”- 2007 年前后:10gen 团队开始做面向云应用的平台,后来把其中的数据库部分独立出来。
- 2009 年:MongoDB 早期版本开源,名字来自 “humongous”,强调能处理很大的数据集。
- 2010s:文档数据库和 NoSQL 浪潮一起流行,MongoDB 成为很多 Web 团队学习 NoSQL 的入口。
- 2018 年:MongoDB Server 改用 SSPL 许可,引发开源社区对”开源数据库商业化”的长期讨论。
- 2020s:Atlas 托管服务、变更流、事务、向量搜索等能力补齐,MongoDB 从”灵活文档库”变成完整数据平台。
- 文档模型不是偷懒版 SQL:它把常一起读取的数据放近,换取更少 JOIN 和更直观的对象表达。
- 高可用靠副本集,不靠单机祈祷:primary、secondary、oplog、选举是理解 MongoDB 生产部署的底座。
- 水平扩展靠分片键质量:
mongos、config server、shard 都只是工具,真正决定效果的是数据怎么切。 - 灵活 schema 也需要纪律:越灵活,越要用命名、索引、文档大小和生命周期规则约束增长。
- 官方源码仓库:mongodb/mongo
- 官方手册:MongoDB Manual
- 文档模型:Documents
- 复制概览:Replication
- 分片概览:Sharding
- couchdb —— 另一个经典文档数据库,设计重点更偏离线复制和 HTTP 接口
- postgresql —— 关系模型代表;和 MongoDB 的文档模型形成最常见对比
- redis —— 常与 MongoDB 搭配做缓存、会话和排行榜
- couchdb —— 同属文档数据库,但复制模型和 API 风格不同
- arangodb —— 多模型数据库,能把文档、图和 KV 放在同一套系统里
- ferretdb —— 用 PostgreSQL 模拟 MongoDB 协议的兼容层
- cockroachdb —— 走分布式 SQL 路线,和 MongoDB 的 NoSQL 路线形成对照
(暂无反向链接)