MongoDB 教程
MongoDB 是一个基于分布式文件存储的开源 NoSQL 数据库,由 C++ 编写,为 Web 应用提供可扩展的高性能数据存储方案。与关系型数据库不同,MongoDB 使用 文档(Document)作为数据模型,数据以 BSON(二进制 JSON)格式存储,天然贴近开发者的对象思维。
MongoDB 的核心特性
| 特性 | 说明 |
|---|---|
| 文档模型 | 数据以类 JSON 的文档存储,无需预定义表结构 |
| 高可扩展 | 支持分片(Sharding)水平扩展 |
| 高可用 | 副本集(Replica Set)自动故障转移 |
| 丰富查询 | 支持索引、聚合管道、地理位置查询 |
| 多语言驱动 | 官方支持 Java/Python/Node.js/PHP 等 |
适用场景
内容管理系统、日志与监控数据、实时分析、物联网传感器数据等写多读多、结构灵活的场景。
https://www.mongodb.com/docs,中文社区也提供了大量教程。NoSQL 简介
NoSQL(Not Only SQL)是一类非关系型数据库的总称,用于解决传统关系型数据库在大数据、高并发场景下的瓶颈。它放弃了部分 ACID 约束,换取更高的扩展性与性能。
NoSQL 四大类型
| 类型 | 代表产品 | 特点 |
|---|---|---|
| 键值存储 | Redis、Memcached | 读写极快,适合缓存、会话 |
| 文档存储 | MongoDB、CouchDB | 类 JSON 文档,结构灵活 |
| 列族存储 | HBase、Cassandra | 适合海量写入与分析 |
| 图数据库 | Neo4j、JanusGraph | 擅长关系与网络分析 |
MongoDB 简介
MongoDB 于 2009 年发布,由 MongoDB Inc.(原 10gen)开发,是目前最流行的文档型 NoSQL 数据库。它的基本概念包括:
| 概念 | MySQL 类比 | 说明 |
|---|---|---|
| 数据库(database) | database | 一个服务可含多个数据库 |
| 集合(collection) | table | 类似表,但无固定结构 |
| 文档(document) | row | 一条数据,BSON 格式 |
| 字段(field) | column | 文档中的键值对 |
// 一个典型 MongoDB 文档
{
"_id": ObjectId("5f8c9a1b2c3d4e5f6a7b8c9d"),
"name": "张三",
"age": 25,
"tags": ["前端", "篮球"],
"address": { "city": "北京", "street": "中关村大街" }
}_id 是每个文档必有的主键字段,默认类型为 ObjectId。Windows MongoDB
在 Windows 上安装 MongoDB 有两种常见方式:MSI 安装包 或 压缩包。
MSI 方式(推荐)
从官网下载 mongodb-windows-x86_64.msi,双击安装。安装时建议勾选 Install MongoDB Compass(图形化管理工具),并选择将 MongoDB 作为 Windows 服务安装,开机自启。
压缩包方式
:: 解压后进入 bin 目录,创建数据目录并启动
mkdir C:\data\db
cd C:\mongodb\bin
mongod --dbpath C:\data\db
:: 另开一个终端连接
mongoLinux MongoDB
Linux 上最常用 apt / yum 包管理器安装 MongoDB。以 Ubuntu 为例:
# 导入 MongoDB 公钥并添加官方源
wget -qO - https://www.mongodb.org/static/pgp/server-7.0.asc | sudo apt-key add -
echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/7.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list
sudo apt-get update
sudo apt-get install -y mongodb-org
# 启动并设置开机自启
sudo systemctl start mongod
sudo systemctl enable mongodOSX MongoDB
macOS 上推荐用 Homebrew 安装 MongoDB:
# 添加 MongoDB 官方 tap
brew tap mongodb/brew
# 安装
brew install mongodb-community
# 启动服务(后台运行)
brew services start mongodb-community
# 连接测试
mongoshMongoDB Shell
mongosh 是 MongoDB 官方的交互式 Shell(旧版为 mongo),用于执行数据库操作命令。
// 连接本地 MongoDB
mongosh
// 显示所有数据库
show dbs
// 切换/创建数据库(切换即创建)
use shop
// 查看当前数据库
db
// 查看集合
show collectionsMongoDB 概念解析
深入理解 MongoDB 的核心概念有助于正确建模:
| 概念 | 含义 |
|---|---|
| _id | 主键,唯一标识文档,默认 ObjectId |
| ObjectId | 12 字节:4 时间戳 + 5 机器标识 + 3 自增计数器 |
| BSON | 二进制 JSON,支持嵌套文档与数组 |
| Cursor | 查询结果游标,遍历式读取 |
| 索引 | 类似 MySQL 索引,加速查询 |
// ObjectId 结构
// 5f8c9a1b 2c3d4e5f 6a7b8c9d
// 时间戳 机器/进程 计数器MongoDB 用户管理
生产环境必须为 MongoDB 开启认证并创建用户。
// 切换到 admin 库创建管理员
use admin
db.createUser({
user: "admin",
pwd: "admin123",
roles: [{ role: "root", db: "admin" }]
})
// 创建业务库专用用户
use shop
db.createUser({
user: "app",
pwd: "app123",
roles: [{ role: "readWrite", db: "shop" }]
})security.authorization: enabled,重启服务后生效。MongoDB 连接
MongoDB 连接字符串(URI)格式统一,主流驱动均支持:
// 格式:mongodb://[用户:密码@]主机[:端口]/数据库
mongodb://localhost:27017/shop
// 带认证
mongodb://app:app123@localhost:27017/shop?authSource=admin
// 副本集
mongodb://node1:27017,node2:27017,node3:27017/shop?replicaSet=rs0# Python 连接示例
from pymongo import MongoClient
client = MongoClient("mongodb://localhost:27017/")
db = client["shop"]MongoDB 创建集合
集合(collection)类似关系型数据库的表,但不需要预定义字段结构。
// 隐式创建:插入第一条文档时自动创建集合
db.users.insertOne({ name: "张三" })
// 显式创建:可设置选项
db.createCollection("orders", {
capped: true, // 固定大小集合
size: 100000, // 最大字节数
max: 1000 // 最大文档数
})
// 查看集合
show collectionsMongoDB 更新集合名
可以使用 renameCollection 重命名集合:
// 语法:db.旧名.renameCollection("新名")
db.users.renameCollection("members")
// 通过管理命令
db.adminCommand({
renameCollection: "shop.users",
to: "shop.members"
})MongoDB 删除集合
删除集合使用 drop() 方法,集合连同其中的文档将永久删除:
// 删除集合
db.orders.drop()
// 返回 true 表示删除成功
// 注意:该操作不可恢复!MongoDB 插入文档
MongoDB 提供三种插入方法:
// 插入单条
db.users.insertOne({
name: "李四", age: 30, tags: ["技术"]
})
// 插入多条
db.users.insertMany([
{ name: "王五", age: 22 },
{ name: "赵六", age: 28 }
])
// 传统方法(可插入单条或数组)
db.users.insert({ name: "老七" })_id 时自动生成 ObjectId。MongoDB 更新文档
更新文档需配合更新操作符使用,否则整个文档会被替换。
// 更新单条(匹配第一条)
db.users.updateOne(
{ name: "李四" },
{ $set: { age: 31, city: "上海" } }
)
// 更新多条
db.users.updateMany(
{ age: { $lt: 25 } },
{ $set: { level: "young" } }
)
// 常用操作符
// $set 设置字段 $inc 自增 $unset 删除字段
// $push 数组追加 $addToSet 数组去重追加
db.orders.updateOne(
{ _id: ObjectId("...") },
{ $inc: { total: 100 }, $push: { items: "商品A" } }
)MongoDB 删除文档
删除文档使用 deleteOne / deleteMany:
// 删除匹配的第一条
db.users.deleteOne({ name: "王五" })
// 删除所有匹配
db.users.deleteMany({ age: { $lt: 18 } })
// 清空集合所有文档(保留集合)
db.users.deleteMany({})
// 返回 { deletedCount: n }MongoDB 查询文档
查询是 MongoDB 最常用的操作,核心方法是 find():
// 查询全部
db.users.find()
// 条件查询
db.users.find({ age: { $gt: 25 } })
// 投影:只返回指定字段(1 显示 0 隐藏)
db.users.find({}, { name: 1, age: 1, _id: 0 })
// 链式操作:排序 + 限制 + 跳过
db.users.find()
.sort({ age: -1 }) // 1 升序 -1 降序
.limit(10)
.skip(20)
// 计数
db.users.countDocuments({ age: { $gt: 20 } })MongoDB 条件操作符
MongoDB 提供丰富的比较与逻辑操作符:
| 操作符 | 含义 | 示例 |
|---|---|---|
| $eq / $ne | 等于 / 不等于 | { age: { $eq: 25 } } |
| $gt / $gte | 大于 / 大于等于 | { age: { $gt: 25 } } |
| $lt / $lte | 小于 / 小于等于 | { age: { $lt: 30 } } |
| $in / $nin | 在/不在数组中 | { city: { $in: ["北京","上海"] } } |
| $and / $or | 与 / 或 | { $or: [{a:1},{b:2}] } |
| $not | 取反 | { age: { $not: { $gt: 50 } } } |
// 组合查询示例
db.orders.find({
$and: [
{ total: { $gte: 100 } },
{ status: { $in: ["paid", "shipped"] } }
]
})MongoDB $type 操作符
$type 用于按字段类型过滤文档:
// 查询 age 字段为 double 类型的文档
db.users.find({ age: { $type: "double" } })
// 查询 age 为 int 或 long
db.users.find({ age: { $type: ["int", "long"] } })| BSON 类型 | 别名 | 编号 |
|---|---|---|
| Double | double | 1 |
| String | string | 2 |
| Object | object | 3 |
| Array | array | 4 |
| ObjectId | objectId | 7 |
| Boolean | bool | 8 |
| Date | date | 9 |
| Null | null | 10 |
| Int32 | int | 16 |
MongoDB Limit 与 Skip 方法
limit() 限制返回条数,skip() 跳过前 N 条,常用于分页:
// 每页 20 条,取第 3 页
db.users.find()
.sort({ _id: 1 })
.skip(40) // (页码-1) * 页大小
.limit(20)
// 只取前 5 条
db.users.find().limit(5)_id > 上一页最后一条 的游标分页,性能远优于 skip。MongoDB 排序
sort() 对查询结果排序,1 升序、-1 降序:
// 按年龄降序,再按姓名升序
db.users.find().sort({ age: -1, name: 1 })
// 中文排序需注意 collation
db.users.find().sort({ name: 1 })
.collation({ locale: "zh" })MongoDB 索引
索引是 MongoDB 高性能查询的关键。
// 创建单字段索引
db.users.createIndex({ age: 1 })
// 复合索引
db.users.createIndex({ city: 1, age: -1 })
// 唯一索引
db.users.createIndex({ email: 1 }, { unique: true })
// 查看索引
db.users.getIndexes()
// 删除索引
db.users.dropIndex("age_1")city_1_age_-1。MongoDB 聚合
聚合管道(Aggregation Pipeline)用于复杂的数据处理与分析,类似 SQL 的 GROUP BY + 多表关联:
// 按城市分组统计平均年龄
db.users.aggregate([
{ $match: { age: { $gt: 0 } } }, // 过滤
{ $group: { // 分组
_id: "$city",
avgAge: { $avg: "$age" },
total: { $sum: 1 }
} },
{ $sort: { total: -1 } }, // 排序
{ $limit: 10 } // 取前 10
])| 阶段 | 作用 |
|---|---|
| $match | 过滤文档 |
| $group | 分组聚合($sum/$avg/$max/$min/$push) |
| $project | 字段投影/计算 |
| $lookup | 类似 LEFT JOIN 关联集合 |
| $unwind | 展开数组 |
| $sort / $limit | 排序 / 限制 |
MongoDB 复制(副本集)
副本集(Replica Set)是一组 mongod 实例,提供数据冗余与自动故障转移:
| 角色 | 职责 |
|---|---|
| Primary(主) | 接受所有写操作,定期写 oplog |
| Secondary(从) | 同步主节点数据,可读(需配置) |
| Arbiter(仲裁) | 只参与选举,不存数据 |
// 初始化副本集(任意节点执行一次)
rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "node1:27017" },
{ _id: 1, host: "node2:27017" },
{ _id: 2, host: "node3:27017", arbiterOnly: true }
]
})
// 查看副本集状态
rs.status()MongoDB 分片
分片(Sharding)将数据水平拆分到多台服务器,突破单机容量与性能上限。MongoDB 分片集群包含:
| 组件 | 作用 |
|---|---|
| mongos | 路由,客户端唯一入口 |
| Config Server | 保存集群元数据 |
| Shard | 存储数据分片(每片通常是一个副本集) |
// 启用分片并选择片键
sh.enableSharding("shop")
sh.shardCollection("shop.orders", { user_id: "hashed" })MongoDB 备份与恢复
MongoDB 提供 mongodump / mongorestore 逻辑备份工具:
# 备份整个数据库
mongodump --db shop --out /backup/shop_$(date +%F)
# 备份单个集合
mongodump --db shop --collection users --out /backup/
# 恢复
mongorestore --db shop /backup/shop_2026-08-12/shop/MongoDB 监控
监控 MongoDB 健康状态常用内置命令与工具:
// 查看服务器状态
db.serverStatus()
// 查看当前操作
db.currentOp()
// 连接数统计
db.serverStatus().connections
// 慢查询阈值(默认 100ms)
db.setProfilingLevel(1, { slowms: 50 })
db.system.profile.find().sort({ ts: -1 }).limit(5)官方图形化监控工具为 Atlas / Ops Manager,社区常用 MongoDB Compass 与 Prometheus exporter。
MongoDB Java
Java 使用官方 mongodb-driver-sync 驱动:
// Maven 依赖
// <dependency>
// <groupId>org.mongodb</groupId>
// <artifactId>mongodb-driver-sync</artifactId>
// <version>4.11.0</version>
// </dependency>
MongoClient client = MongoClients.create(
"mongodb://localhost:27017");
MongoDatabase db = client.getDatabase("shop");
MongoCollection<Document> users = db.getCollection("users");
// 插入
users.insertOne(new Document("name", "张三")
.append("age", 25));
// 查询
Document d = users.find(Filters.gt("age", 20)).first();
System.out.println(d.toJson());
client.close();MongoDB PHP
PHP 使用官方 mongodb 扩展(配合 mongodb/mongodb 库):
<?php
require 'vendor/autoload.php';
use MongoDB\Client;
$client = new Client("mongodb://localhost:27017");
$users = $client->shop->users;
// 插入
$users->insertOne(['name' => '张三', 'age' => 25]);
// 查询
$cursor = $users->find(['age' => ['$gt' => 20]]);
foreach ($cursor as $doc) {
echo $doc['name'], PHP_EOL;
}MongoDB 关系
MongoDB 中处理实体关系有两种策略:
| 策略 | 做法 | 适用场景 |
|---|---|---|
| 嵌入式(Embedded) | 把子文档直接放进父文档 | 一对一、少量子记录 |
| 引用式(Reference) | 存 _id 关联另一集合 | 一对多、多对多 |
// 嵌入式:订单含多个商品条目
{
_id: ObjectId("..."),
user: "张三",
items: [
{ sku: "A1", qty: 2, price: 10 },
{ sku: "B2", qty: 1, price: 50 }
]
}
// 引用式:文章关联作者
{ title: "教程", authorId: ObjectId("...") }MongoDB 数据库引用
DBRef 是一种标准化的引用格式,包含集合名与 _id,便于跨集合解析:
// DBRef 格式
{
author: {
$ref: "users", // 目标集合
$id: ObjectId("..."), // 文档 _id
$db: "shop" // 可选,目标数据库
}
}_id 引用即可,更高效。MongoDB 覆盖索引查询
覆盖索引查询(Covered Query)指查询所需字段全部在索引中,无需回表读取文档,速度极快:
// 建立复合索引
db.users.createIndex({ name: 1, age: 1 })
// 查询只投影索引字段(且 _id: 0)
db.users.find(
{ name: "张三" },
{ name: 1, age: 1, _id: 0 }
).explain().queryPlanner.winningPlan
// 若出现 "IXSCAN" 且无 FETCH,即为覆盖查询MongoDB 查询分析
用 explain() 分析查询执行计划,定位慢查询根因:
// 查看执行计划
db.users.find({ age: { $gt: 25 } }).explain("executionStats")
// 关键字段
// winningPlan: 选中的执行计划
// stage: COLLSCAN(全表扫描)或 IXSCAN(走索引)
// executionTimeMillis: 执行耗时
// nReturned / totalDocsExamined: 返回数/扫描数totalDocsExamined 远大于 nReturned 时,说明查询未走索引,需要创建合适的索引。MongoDB 原子操作
MongoDB 对单文档的更新是原子的。借助 findAndModify 可实现计数器、队列等原子操作:
// 原子自增计数器
db.counters.findOneAndUpdate(
{ _id: "visits" },
{ $inc: { seq: 1 } },
{ upsert: true, returnDocument: "after" }
)
// 返回 { _id: "visits", seq: 1024 }MongoDB 高级索引
MongoDB 支持多种高级索引类型:
// 文本索引(中文需指定分词器)
db.articles.createIndex(
{ content: "text" },
{ default_language: "zh" }
)
// 地理位置索引(2dsphere 支持球面查询)
db.places.createIndex({ loc: "2dsphere" })
db.places.find({
loc: {
$near: {
$geometry: { type: "Point", coordinates: [116.4, 39.9] },
$maxDistance: 5000
}
}
})
// 稀疏索引(只索引含该字段的文档)
db.users.createIndex({ vip: 1 }, { sparse: true })
// TTL 索引(自动过期删除,常用于日志/验证码)
db.logs.createIndex({ created_at: 1 }, { expireAfterSeconds: 86400 })MongoDB 索引限制
使用索引时有几个限制需要了解:
| 限制 | 说明 |
|---|---|
| 每个索引大小 | 不能超过 1KB 的索引键(高版本已放宽) |
| 索引总数 | 每个集合索引数量过多会拖慢写入 |
| 内存 | 索引必须能放入内存,否则性能骤降 |
| 正则 | 非前缀正则查询无法使用普通索引 |
| 取反操作 | $not/$nin 往往无法走索引 |
explain() 检查索引是否真正被使用。MongoDB ObjectId
ObjectId 是 MongoDB 默认的主键类型,12 字节结构如下:
| 字节 | 含义 |
|---|---|
| 4 字节 | Unix 时间戳(秒) |
| 5 字节 | 随机值(机器 + 进程) |
| 3 字节 | 自增计数器 |
// 生成 ObjectId
const id = ObjectId()
// 5f8c9a1b2c3d4e5f6a7b8c9d
// 提取时间戳(可用于按时间排序/查询)
id.getTimestamp()
// ISODate("2020-10-18T07:33:47Z")MongoDB Map Reduce
MapReduce 是 MongoDB 早期的分布式计算框架,现在更推荐用聚合管道替代:
// 统计每个城市的人数(MapReduce 写法)
db.users.mapReduce(
function() { emit(this.city, 1); }, // map
function(key, values) { // reduce
return Array.sum(values);
},
{ out: "city_stats" }
)
// 等价聚合管道写法(更高效)
db.users.aggregate([
{ $group: { _id: "$city", count: { $sum: 1 } } }
])MongoDB 全文检索
MongoDB 内置文本索引,支持对字符串字段做全文搜索:
// 创建文本索引(可多字段)
db.articles.createIndex(
{ title: "text", content: "text" },
{ default_language: "zh" }
)
// 全文搜索
db.articles.find({
$text: { $search: "MongoDB 教程" }
})
// 带评分排序
db.articles.find(
{ $text: { $search: "数据库" } },
{ score: { $meta: "textScore" } }
).sort({ score: { $meta: "textScore" } })MongoDB 正则表达式
MongoDB 查询支持 PCRE 正则,用于模糊匹配:
// 查询 name 以"张"开头的用户
db.users.find({ name: /^张/ })
// 等价写法(正则对象)
db.users.find({ name: { $regex: "^张" } })
// 忽略大小写
db.users.find({ email: { $regex: "@qq.com$", $options: "i" } })MongoDB 管理工具
MongoDB 生态常用的管理工具:
| 工具 | 用途 |
|---|---|
| MongoDB Compass | 官方 GUI,可视化查看/编辑数据 |
| mongosh | 命令行 Shell |
| MongoDB Atlas | 官方云托管服务 |
| Robo 3T / Studio 3T | 第三方图形化工具 |
| mongostat / mongotop | 命令行实时监控 |
# 实时监控读写频率
mongostat --host localhost:27017
# 查看集合操作耗时 TOP
mongotop --host localhost:27017MongoDB GridFS
GridFS 用于在 MongoDB 中存储超过 16MB 的大文件,它将文件拆分为多个 chunks:
| 集合 | 作用 |
|---|---|
| fs.files | 文件元数据(名称、大小、md5) |
| fs.chunks | 文件内容分块(默认每块 255KB) |
# 命令行上传/下载文件
mongofiles --db mydb put video.mp4
mongofiles --db mydb get video.mp4
# 列出文件
mongofiles --db mydb listMongoDB 固定集合
固定集合(Capped Collection)是大小固定的循环队列式集合,自动淘汰最旧数据:
// 创建 1MB 固定集合
db.createCollection("logs", {
capped: true,
size: 1048576,
max: 5000
})
// 插入不受限制,满了自动覆盖最旧
db.logs.insertOne({ level: "info", msg: "hello" })MongoDB 自动增长
MongoDB 没有内置自增 ID(默认 ObjectId),若业务需要自增序列(如订单号),需自行实现计数器:
// 维护一个计数器集合
db.counters.insertOne({ _id: "orderNo", seq: 0 })
// 原子获取下一个序列值
function nextSeq(name) {
const doc = db.counters.findOneAndUpdate(
{ _id: name },
{ $inc: { seq: 1 } },
{ upsert: true, returnDocument: "after" }
);
return doc.seq;
}
// 使用
const no = nextSeq("orderNo") // 1, 2, 3 ...