01. NoSQL 跟 SQL 到底差在哪
关系型数据库(SQL)把数据存成一张张互相有关联的表——用户表、订单表、商品表,查的时候用 JOIN 把它们缝在一起。
MongoDB 是文档数据库——不像表那样一行行一列列,而是把数据存成 BSON(类似 JSON)的文档。一个文档就是一个自包含的数据单元,用户的订单可以嵌套在用户文档里,不用拆散到多个表。
核心区别:SQL 的表结构是死的,所有行都得一个样。MongoDB 文档是灵活的,不同文档可以有不同字段。SQL 靠 JOIN 拆表关联,MongoDB 靠嵌套文档减少关联,但也能用 lookup 做类似 JOIN 的事。
选 MongoDB 的场景:数据格式不固定经常变;数据之间天然有嵌套关系(如博客文章加评论);快速原型开发。不适合:复杂事务多、需要大量关联查询的场景。
02. 安装与 MongoDB Shell(mongosh)
MongoDB 安装后自带 mongosh(新版命令行客户端,替代老版 mongo shell)。mongosh 是个完整的 Node.js REPL,可以直接写 JavaScript。
常用命令:show dbs 看所有数据库,use mydb 切换数据库,show collections 看当前库所有集合,db.help() 看帮助。
集合相当于 SQL 的表,文档相当于 SQL 的行。MongoDB 会自动为每个文档生成一个下划线 id 字段,类型是 ObjectId——一个 12 字节的唯一 ID,包含时间戳信息。
bash
# 启动 MongoDB 服务
mongod --dbpath /data/db
# 连接
mongosh mongodb://localhost:27017
# mongosh 常用命令
show dbs
use mydb
show collections
db.users.find()
db.users.findOne()
exitmongosh 用的是 JavaScript 语法,db.users.findOne() 后面不需要分号但加了也没事。
03. 文档模型设计——嵌套 vs 引用
MongoDB 里设计数据模型有一个核心问题:嵌套还是引用?
嵌套——把关联数据直接嵌入到父文档里。比如把评论嵌入文章文档里。好处是一次查询就全拿到了,不用 JOIN。坏处是文档可能变大超过 16MB 限制;评论多了更新也麻烦。
引用——类似 SQL 的外键。文章和评论分开存,评论文档里放一个文章 ID 指向文章。好处是灵活安全,坏处是查的时候可能要跑 lookup 关联。
选嵌套还是引用的经验法则:子数据跟父数据同生共死且数量不多(几十条)就嵌套;子数据可能被多个父文档引用或数量很大就引用。
javascript
// 嵌套模型:文章里嵌入评论
{
_id: ObjectId("..."),
title: "MongoDB 教程",
comments: [
{ user: "Alice", text: "好文章!", date: ISODate("2024-01-01") },
{ user: "Bob", text: "学习了", date: ISODate("2024-01-02") }
]
}
// 引用模型
// articles 集合
{ _id: ObjectId("..."), title: "MongoDB 教程" }
// comments 集合
{ article_id: ObjectId("..."), user: "Alice", text: "好文章!" }嵌套的评论有上限——单个文档最大 16MB,如果评论可能非常多,老老实实用引用模型。
04. 数据库与集合的管理
MongoDB 的数据库和集合是懒创建的——你不用去建库建表,直接用就行。第一次往 database.collection 插入数据时,数据库和集合自动就长出来了。
但有些高级选项需要显式创建:
固定集合——大小固定的集合,写满了自动覆盖最老的数据,适合存日志。
文档校验——虽然 MongoDB 是 schema-less 的,但你可以在集合上定义校验规则,拒绝不符合格式的文档插入。
过期索引——给某个日期字段建 TTL 索引,时间一到文档自动删除,适合存临时数据。
javascript
// 固定集合(最大 1MB,最多存 1000 条)
db.createCollection("logs", { capped: true, size: 1048576, max: 1000 });
// 定义集合校验规则
db.createCollection("users", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["name", "email"],
properties: {
name: { bsonType: "string" },
email: { bsonType: "string", pattern: "^.+@.+$" },
age: { bsonType: "int", minimum: 0, maximum: 150 }
}
}
}
});dropDatabase() 没有确认对话框,执行直接删,不会问第二遍。生产环境操作前再三确认。
05. 数据类型与 ObjectId
MongoDB 的 BSON 支持丰富的数据类型:String、Integer、Boolean、Double、Array、Object、Date、ObjectId、Binary Data、Null、Regular Expression、Timestamp。
ObjectId 是最特殊的一个——默认主键 _id 的类型。12 字节的结构:前 4 字节是时间戳(精确到秒),中间 5 字节是随机值(机器 ID 加进程 ID),后 3 字节是计数器。这意味着从 ObjectId 就能反解出文档是什么时候创建的。
Date 类型存的是 UTC 时间,mongosh 里显示会转成本地时间。数字分 Integer(32 位)、Long(64 位)、Double(浮点)。默认输入数字是 Double,想用 Integer 得用 NumberInt()。
javascript
// ObjectId 里提取时间
ObjectId("669c8a1234567890abcdef12").getTimestamp()
// 返回 ISODate 对象
// 数字类型
NumberInt(42) // 32 位整数
NumberLong(99) // 64 位整数
NumberDecimal("123.45") // 高精度小数
// 日期
db.events.insertOne({ name: "Conference", date: new Date("2024-12-01") });
db.events.insertOne({ name: "Webinar", date: ISODate("2024-12-15") });ObjectId 不需要你手动生成,插入时不提供 _id 系统自动创建。但也可以用 ObjectId() 手动创建。
知识测验
第 1/5 题正确 0
MongoDB 的集合相当于 SQL 的什么?
下一节
下一节 CRUD 操作