如果把数据库课程写成一串命令,读者往往能在当天照着输入,却很难在一周后解释自己为什么这样设计。这门课换一种走法:我们从一家叫“纸舟书店”的小型线上书店出发,一边补齐 MongoDB 的历史、概念和取舍,一边把书籍、顾客与订单真正存进去。后面的每一次查询、更新、聚合、备份和扩展,都继续使用同一组数据。
全课以 MongoDB 8.0 主版本为基线。实践部分优先使用 Docker 官方 mongo:8.0 镜像和 mongosh,这样不同操作系统看到的数据库行为尽量一致。8.0 标签会随安全修复更新补丁版本,因此课程只把 8.0 当作稳定边界,不把某个补丁号写成永远不变的前提。
这一节先解决“MongoDB 为什么存在、文档模型解决什么问题、什么时候不该用它”。下一节才会启动数据库。先建立判断框架,后面的命令才不会变成需要死记的咒语。
纸舟书店最初只需要记录书名和价格,一张二维表当然足够。业务继续生长后,每本书有多个标签、多个版本、作者信息、出版社信息,有些书还有试读章节。不同类别的书字段也不完全一样:纸质书关心开本和重量,电子书关心文件格式,套装书还要记录包含哪些册。
关系型数据库可以严谨地表达这些数据。我们可以把书籍、作者、标签、版本拆成多张表,再用主键和外键连接。问题不在于关系模型做不到,而在于应用每次读取“一本书的商品页”时,常常希望一次得到一个完整对象;如果数据天然围绕这个对象一起读取、一起修改,频繁拆表和组装就会增加开发与查询的复杂度。
MongoDB 选择把一条业务记录表示为“文档”。一本文档可以包含标量、数组和嵌套文档:
{
_id: "book-mongodb",
title: "MongoDB 从入门到实践",
category: "数据库",
price: 89,
stock: 20,
tags: ["MongoDB", "后端"],
publisher: {
name: "纸舟出版社",
city: "上海"
},
published: true
}这段结构和应用代码中的对象很接近。tags 不必拆成多行,publisher 也可以跟书籍放在一起。MongoDB 会把它保存为 BSON,而不是直接保存文本 JSON;BSON 为日期、二进制、ObjectId、Decimal128 等类型提供了明确表示。
“可以嵌套”不等于“全部嵌套”。如果一份出版社资料会被十万本书频繁独立修改,把完整出版社信息复制进每本书会带来大量重复更新。文档模型仍然需要设计,只是设计问题从“怎样拆表”变成了“哪些数据拥有共同的生命周期”。
MongoDB 的故事始于 2007 年。MongoDB 官方的公司历史记载,Dwight Merriman、Eliot Horowitz 和 Kevin Ryan 创办了 10gen。他们此前参与过大规模在线广告系统,亲历了业务增长时数据平台在扩展和开发效率上的压力。
最初的目标并不是单独做一款数据库,而是构建一套面向云计算的应用平台。团队后来发现,平台里最有价值的部分是那套面向文档、便于横向扩展的数据层,于是在 2009 年发布 MongoDB。名称来自 “humongous”,强调它希望处理很大的数据规模。10gen 后来更名为 MongoDB, Inc.。
理解这段历史有两个好处。
第一,MongoDB 从一开始就面向应用开发者。它的查询语言使用文档结构,驱动程序把数据库结果映射成语言中的对象,开发者可以用相近的结构思考接口数据与持久化数据。
第二,MongoDB 的分布式能力不是后来简单贴上的标签。副本集负责高可用,分片负责把数据和请求分散到多个分片上。课程后半部分会逐步搭建这些能力,但我们不会因为它能分片,就假设每个小项目都应该分片。
MongoDB 经常被归入 NoSQL。这个名字最有用的地方,是提醒我们它不以传统关系表作为核心抽象;最容易误导的地方,是让人以为它“不要结构”“不支持事务”或“与 SQL 数据库只能二选一”。现代 MongoDB 支持校验规则、索引、聚合、多文档事务和分布式部署。真正要比较的是数据模型、访问模式、一致性需求与团队的运维能力。
学习 MongoDB 时,可以先把结构记成四层:部署、数据库、集合、文档。

集合可以容纳字段不同的文档,但“技术上允许”并不是“业务上合理”。如果 books 中一半是书籍、一半是支付流水,查询、索引和权限都会变得难以理解。我们仍然让一个集合服务一种清楚的业务概念。
文档还有一个很重要的边界:单个文档的写入具有原子性。假设书籍文档同时保存 stock 和 featured,一次更新可以一起修改这两个字段,其他客户端不会看到只改了一半的中间状态。这是后面设计库存操作时的重要依据。
MongoDB 的单个 BSON 文档有大小上限。课程使用的书籍与订单远小于这个边界,但在设计无限增长的评论数组、日志数组或订单明细时,必须意识到“把所有东西塞进一个文档”迟早会失控。
全课固定使用数据库 bookstore 和三个核心集合:
bookstore
├── books 书籍目录、价格、库存、标签与上架状态
├── customers 顾客资料与收货地址
└── orders 订单状态、顾客引用与下单时的商品快照我们会有意采用可读的字符串 _id,例如 book-mongodb、customer-lin 和 order-1001。MongoDB 完全支持自动生成的 ObjectId,真实项目也经常使用 ObjectId;课程使用固定字符串,是为了让每条命令都能复制,并让查询结果不受随机标识影响。
三类文档承担不同的建模任务:
books 让我们学习 BSON、CRUD、查询、数组、嵌套字段、索引和聚合。customers 把收货地址嵌入顾客文档,展示“一起读取、一起维护”的数据为何适合嵌入。orders 使用 customerId 和 bookId 引用其他文档,同时保存下单时的书名与单价快照。即使书籍以后改名或调价,历史订单仍然能说明当时卖了什么、卖多少钱。这里已经出现 MongoDB 建模最重要的判断:引用负责指向一个独立实体,快照负责保存历史事实。二者可以同时存在,不必强行只选一种。
MongoDB 通常适合以下问题:
纸舟书店符合其中几项:书籍标签和订单明细天然是数组,商品详情适合按整本文档读取,订单需要保留当时的商品快照。它足够真实,又不会用庞大业务细节掩盖数据库知识。
MongoDB 并不只适合“没有规则的数据”。恰恰相反,一个可靠的 MongoDB 项目需要清楚的字段含义、校验规则、索引策略和迁移方案。灵活模式的价值在于允许受控演进,而不是允许字段名随意变化。
如果业务核心是大量复杂的跨实体关联、强约束和成熟的关系分析,关系型数据库可能更直接。例如总账系统依赖严格科目关系和复杂报表,使用关系模型往往能更自然地表达约束与连接。
如果团队需要对任意维度做即席分析,数据仓库或列式分析系统可能比业务数据库合适。MongoDB 的聚合框架很强,但业务查询与大规模分析并不是同一种负载。
如果数据几乎没有查询需求,只需要廉价保存大对象,对象存储会比把文件直接塞进数据库更简单。MongoDB 提供 GridFS,但“存在这个功能”不代表所有文件都应该这样存。
还要警惕三个常见误区:
判断 MongoDB 是否合适,不要从“它流不流行”开始。先写出最常见的读写操作,再观察数据是否围绕清楚的文档边界一起变化。访问模式比口号更可靠。
接下来的课程会沿着同一条项目线推进:
mongosh 完成版本体检。explain 比较扫描文档数,而不是凭感觉谈性能。每个实践段落都遵守同一顺序:先解释知识点,再执行操作,然后展示并解读结果。动态生成的容器 ID、时间戳和补丁版本会明确标注,不会被包装成固定答案。
一家内容平台的文章包含标题、正文、作者摘要、若干标签和不同类型的扩展区块。页面访问时通常整篇读取,字段还会随着编辑器能力演进。仅根据这些信息,文档模型是一个值得验证的候选方案。
读完这一节,你应该能说清楚四件事:MongoDB 为什么从 10gen 的平台项目中独立出来;数据库、集合和文档之间是什么关系;嵌入与引用分别在解决什么问题;哪些场景下不应只因为“数据像 JSON”就选择 MongoDB。
下一节会准备 Docker,启动 mongo:8.0,初始化一个单节点副本集,并通过 ping、db.version() 和 db.hello() 完成第一次体检。纸舟书店从那一步开始真正拥有可查询的数据空间。