
MongoDB 从入门到实战全教程
MongoDB 是目前最主流的非关系型(NoSQL)数据库之一,以「文档型存储、高可用、易扩展」为核心特点,广泛应用于互联网、大数据、微服务等场景。本教程从环境搭建到高级实战,覆盖 MongoDB 核心语法、索引优化、集群部署等内容,适合零基础入门,也可作为开发参考手册。
一、MongoDB 核心概念(必懂)
1.1 为什么选择 MongoDB?
传统关系型数据库(MySQL/PostgreSQL)基于「表 - 行 - 列」存储,适合结构化数据,但面对高频读写、海量数据、灵活字段的场景(如社交 APP 动态、电商订单、日志存储),存在扩展难、改表成本高的问题。
MongoDB 作为文档型数据库,核心优势:
- 灵活的文档模型:无需预定义表结构,字段可动态扩展;
- 高性能:支持内存映射、索引优化,读写速度快;
- 易扩展:原生支持分片集群,可横向扩展;
- 高可用:副本集机制保证数据不丢失;
- 支持复杂查询:内置丰富的查询语法、聚合管道,满足业务需求。
1.2 核心概念对比(与 MySQL)
MongoDB 没有「表、行、列、主键、外键」等概念,对应关系如下:
| MySQL 概念 | MongoDB 概念 | 说明 |
|---|---|---|
| 数据库(Database) | 数据库(Database) | 逻辑隔离的存储单元,一个 MongoDB 实例可包含多个数据库 |
| 表(Table) | 集合(Collection) | 存储文档的容器,相当于 MySQL 的表,无需定义结构 |
| 行(Row) | 文档(Document) | 最小数据单元,以 BSON(二进制 JSON)格式存储,字段键值对形式 |
| 列(Column) | 字段(Field) | 文档中的键值对,字段类型灵活(字符串、数字、数组、嵌套文档等) |
| 主键(Primary Key) | _id 字段 | 每个文档默认的唯一标识,MongoDB 自动生成,也可自定义 |
| 索引(Index) | 索引(Index) | 加速查询,支持单字段、复合、地理空间、文本等多种索引类型 |
| 外键(Foreign Key) | 无原生支持 | 可通过应用层或聚合管道实现关联查询 |
1.3 BSON 数据类型
MongoDB 以 BSON 存储数据,支持比 JSON 更丰富的类型,核心类型:
| 类型 | 说明 | 示例 |
|---|---|---|
| String | 字符串,UTF-8 编码 | "name": "张三" |
| Number | 数字(int32/int64/double) | "age": 20, "score": 98.5 |
| Boolean | 布尔值 | "is_vip": true |
| Array | 数组,可包含任意类型 | "hobbies": ["篮球", "编程"] |
| Object | 嵌套文档 | "address": {"city": "北京"} |
| ObjectId | 唯一标识,默认 _id 类型 | "_id": ObjectId("65ed...") |
| Date | 日期时间 | "create_time": ISODate("2026-02-08T00:00:00Z") |
| Null | 空值 | "remark": null |
| Binary Data | 二进制数据(存储文件、图片等) | - |
二、环境搭建
2.1 系统要求
- 操作系统:Windows 10+/Mac OS 10.15+/Linux(CentOS 7+/Ubuntu 18.04+);
- 内存:建议 2GB 以上;
- 权限:Mac/Linux 需要管理员权限(sudo)。
2.2 安装 MongoDB(以社区版为例)
方式 1:官网下载安装包(推荐新手)
-
访问 MongoDB 官网下载页;
-
选择对应系统版本,下载后安装:
-
Windows:双击安装包,勾选「Complete」,取消「Install MongoDB Compass」(可视化工具可单独装);
-
Mac:解压后将
mongodb/bin加入系统环境变量; -
Linux: # CentOS 安装示例 sudo yum install -y mongodb-org # Ubuntu 安装示例 sudo apt-get install -y mongodb-org
-
方式 2:包管理器安装(Mac/Linux)
bash
# Mac(Homebrew)
brew tap mongodb/brew
brew install mongodb-community@7.0
# Linux(Ubuntu)
wget -qO - https://www.mongodb.org/static/pgp/server-7.0.asc | sudo apt-key add -
echo "deb [ arch=amd64,arm64 ] 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
2.3 配置环境变量(关键)
确保 mongod(服务端)和 mongo/mongosh(客户端)命令可全局执行:
-
Windows:将
MongoDB\Server\7.0\bin加入系统「环境变量 - Path」; -
Mac/Linux:编辑
~/.bashrc或~/.zshrc,添加: export PATH=/usr/local/mongodb/bin:$PATH source ~/.zshrc # 生效配置
2.4 启动 MongoDB 服务
步骤 1:创建数据存储目录
MongoDB 默认数据存储路径为 /data/db(Mac/Linux)或 C:\data\db(Windows),需手动创建:
bash
# Mac/Linux
sudo mkdir -p /data/db
sudo chmod 777 /data/db # 赋予读写权限
# Windows(管理员命令行)
md C:\data\db
步骤 2:启动服务
perl
# 前台启动(测试用,关闭终端则服务停止)
mongod
# 后台启动(生产环境)
# Mac/Linux
sudo mongod --fork --logpath /var/log/mongodb/mongod.log
# Windows 服务启动
net start MongoDB
步骤 3:连接 MongoDB
shell
# 新版用 mongosh(推荐)
mongosh
# 旧版用 mongo
mongo
# 成功连接会显示:MongoDB shell version v7.0.0
# 提示符变为:test>(test 是默认数据库)
2.5 可视化工具(可选)
新手推荐使用 MongoDB Compass(官方免费),下载地址:MongoDB Compass,支持可视化操作数据库、执行查询、查看索引等。
三、基础操作(数据库 / 集合 / 文档)
3.1 数据库操作
3.1.1 查看所有数据库
bash
show dbs # 显示有数据的数据库(空数据库不显示)
3.1.2 切换 / 创建数据库
perl
use test_db # 切换到 test_db,若不存在则创建(需插入数据后才会显示)
3.1.3 删除数据库
perl
use test_db # 先切换到目标数据库
db.dropDatabase() # 删除当前数据库
3.2 集合操作
集合相当于 MySQL 的表,无需提前定义结构。
3.2.1 创建集合(可选)
yaml
# 方式 1:直接插入文档,自动创建集合
db.user.insertOne({ name: "张三", age: 20 }) # 自动创建 user 集合
# 方式 2:手动创建(可指定配置,如大小、索引)
db.createCollection("product", {
capped: true, # 固定大小集合
size: 1024 * 1024, # 大小 1MB
max: 1000 # 最多存储 1000 个文档
})
3.2.2 查看所有集合
sql
show collections # 或 show tables
3.2.3 删除集合
bash
db.user.drop() # 删除 user 集合
3.3 文档操作(核心)
文档是 MongoDB 最小数据单元,以下以 user 集合为例,讲解增删改查。
3.3.1 插入文档
插入单个文档(insertOne)
php
db.user.insertOne({
name: "张三",
age: 20,
gender: "男",
hobbies: ["篮球", "编程"],
address: {
city: "北京",
district: "朝阳区"
},
create_time: new Date()
})
返回结果:
json
json
{
"acknowledged": true,
"insertedId": ObjectId("65ed8b0f1234567890abcdef") // 自动生成的 _id
}
插入多个文档(insertMany)
php
db.user.insertMany([
{ name: "李四", age: 22, gender: "女", hobbies: ["读书", "旅游"] },
{ name: "王五", age: 25, gender: "男", hobbies: ["游戏", "音乐"] },
{ name: "赵六", age: 18, gender: "男", hobbies: ["运动", "摄影"] }
])
3.3.2 查询文档
查询所有文档(find)
scss
db.user.find() # 返回所有文档(默认显示 20 条,输入 it 查看下一页)
db.user.find().pretty() # 格式化输出,更易读
条件查询(等于 / 大于 / 小于)
php
# 1. 等于:查询性别为男的文档
db.user.find({ gender: "男" }).pretty()
# 2. 大于:查询年龄大于 20 的文档
db.user.find({ age: { $gt: 20 } }).pretty()
# 3. 小于等于:查询年龄小于等于 18 的文档
db.user.find({ age: { $lte: 18 } }).pretty()
# 4. 多条件(且):年龄>20 且性别为女
db.user.find({ age: { $gt: 20 }, gender: "女" }).pretty()
# 5. 多条件(或):年龄<20 或性别为男
db.user.find({ $or: [{ age: { $lt: 20 } }, { gender: "男" }] }).pretty()
字段过滤(只返回指定字段)
php
# 查询所有文档,只返回 name 和 age 字段(_id 默认为 1,需手动设为 0)
db.user.find({}, { name: 1, age: 1, _id: 0 }).pretty()
# 排除指定字段(返回除 hobbies 外的所有字段)
db.user.find({}, { hobbies: 0 }).pretty()
排序(sort)
scss
# 按年龄升序(1 升序,-1 降序)
db.user.find().sort({ age: 1 }).pretty()
# 多字段排序:按年龄降序,姓名升序
db.user.find().sort({ age: -1, name: 1 }).pretty()
分页(skip + limit)
scss
# 跳过前 1 条,取 2 条(第 2-3 条)
db.user.find().skip(1).limit(2).pretty()
统计数量(countDocuments)
php
# 统计所有文档数量
db.user.countDocuments()
# 统计条件文档数量:年龄>20 的文档数
db.user.countDocuments({ age: { $gt: 20 } })
3.3.3 更新文档
更新单个文档(updateOne)
php
# 将张三的年龄改为 21
db.user.updateOne(
{ name: "张三" }, # 查询条件
{ $set: { age: 21 } } # 更新操作($set 表示设置字段)
)
更新多个文档(updateMany)
css
# 将所有性别为男的文档,添加字段 is_adult: true
db.user.updateMany(
{ gender: "男" },
{ $set: { is_adult: true } }
)
替换文档(replaceOne)
php
# 替换张三的整个文档(仅保留 _id)
db.user.replaceOne(
{ name: "张三" },
{ name: "张三", age: 21, gender: "男", job: "程序员" }
)
3.3.4 删除文档
删除单个文档(deleteOne)
php
# 删除姓名为赵六的文档
db.user.deleteOne({ name: "赵六" })
删除多个文档(deleteMany)
php
# 删除所有年龄<20 的文档
db.user.deleteMany({ age: { $lt: 20 } })
删除所有文档(保留集合)
bash
db.user.deleteMany({}) # 清空集合,保留集合结构
四、高级查询(运算符 / 聚合 / 索引)
4.1 常用查询运算符
MongoDB 提供丰富的运算符,满足复杂查询需求,核心运算符分类:
4.1.1 比较运算符
| 运算符 | 说明 | 示例 |
|---|---|---|
| $eq | 等于 | { age: { $eq: 20 } } |
| $ne | 不等于 | { age: { $ne: 20 } } |
| $gt | 大于 | { age: { $gt: 20 } } |
| $gte | 大于等于 | { age: { $gte: 20 } } |
| $lt | 小于 | { age: { $lt: 20 } } |
| $lte | 小于等于 | { age: { $lte: 20 } } |
| $in | 包含在数组 | { age: { $in: [20, 22, 25] } } |
| $nin | 不包含 | { age: { $nin: [20, 22] } } |
4.1.2 逻辑运算符
| 运算符 | 说明 | 示例 |
|---|---|---|
| $and | 且 | { and: [{ age: { gt: 20 } }, {gender: "男"}] } |
| $or | 或 | { or: [{ age: { lt: 20 } }, {gender: "女"}] } |
| $not | 非 | { age: { not: { gt: 20 } } }(年龄 <=20) |
| $nor | 都不满足 | {$nor: [{ age: 20}, { gender: "男" }] }(年龄≠20 且性别≠男) |
4.1.3 数组运算符
| 运算符 | 说明 | 示例 |
|---|---|---|
| $all | 包含数组所有元素 | {hobbies: { $all: ["篮球", "编程"] } } |
| $size | 数组长度等于 | { hobbies: { $size: 2 } } |
| $elemMatch | 数组元素匹配条件 | { hobbies: { elemMatch: { eq: "篮球" } } } |
4.1.4 示例:复杂查询
php
# 查询:年龄在 20-25 之间,爱好包含篮球,地址城市为北京的男性用户
db.user.find({
age: { $gte: 20, $lte: 25 },
hobbies: { $all: ["篮球"] },
"address.city": "北京", # 嵌套字段查询
gender: "男"
}).pretty()
4.2 聚合管道(Aggregation Pipeline)
聚合管道是 MongoDB 高级查询核心,支持数据筛选、分组、排序、关联等操作,语法为 db.collection.aggregate([阶段1, 阶段2, ...])。
4.2.1 核心聚合阶段
| 阶段 | 说明 | 示例 |
|---|---|---|
| $match | 筛选文档(类似 where) | { match: { age: { gt: 20 } } } |
| $group | 分组(类似 group by) | { group: { _id: "gender", count: { $sum: 1 } } } |
| $sort | 排序 | { $sort: { age: -1 } } |
| $limit | 限制结果数 | { $limit: 10 } |
| $skip | 跳过文档数 | { $skip: 5 } |
| $project | 字段过滤 / 重命名 | { $project: { name: 1, age: 1, _id: 0 } } |
| $lookup | 关联查询(类似 join) | 关联 product 集合 |
4.2.2 聚合示例
示例 1:分组统计
bash
# 按性别分组,统计每组人数、平均年龄
db.user.aggregate([
{ $match: { age: { $gt: 18 } } }, # 先筛选成年用户
{
$group: {
_id: "$gender", # 分组字段(_id 是固定关键字)
total: { $sum: 1 }, # 统计数量
avg_age: { $avg: "$age" } # 计算平均年龄
}
},
{ $sort: { total: -1 } } # 按人数降序
])
示例 2:关联查询
假设有 order 集合(订单)和 user 集合(用户),关联查询用户及其订单:
php
# 先插入订单数据
db.order.insertMany([
{ user_id: ObjectId("65ed8b0f1234567890abcdef"), product: "手机", price: 2999 },
{ user_id: ObjectId("65ed8b0f1234567890abcdef"), product: "耳机", price: 199 }
])
# 关联查询:获取用户信息 + 订单列表
db.user.aggregate([
{
$lookup: {
from: "order", # 关联的集合
localField: "_id", # 当前集合的关联字段
foreignField: "user_id", # 关联集合的字段
as: "orders" # 关联结果的字段名
}
},
{ $project: { name: 1, age: 1, orders: 1, _id: 0 } } # 只显示指定字段
])
4.3 索引(性能优化核心)
索引能大幅提升查询效率,MongoDB 支持多种索引类型,核心原理与 MySQL 类似(B 树索引)。
4.3.1 查看索引
bash
db.user.getIndexes() # 查看 user 集合的所有索引(默认有 _id 索引)
4.3.2 创建索引
单字段索引
php
# 为 age 字段创建升序索引(1 升序,-1 降序)
db.user.createIndex({ age: 1 })
复合索引
php
# 为 gender 和 age 创建复合索引(先按 gender 排序,再按 age 排序)
db.user.createIndex({ gender: 1, age: -1 })
唯一索引
php
# 为 name 字段创建唯一索引(不允许重复)
db.user.createIndex({ name: 1 }, { unique: true })
文本索引(支持全文搜索)
php
# 为 name 和 hobbies 字段创建文本索引
db.user.createIndex({ name: "text", hobbies: "text" })
# 使用文本索引查询:包含「篮球」的文档
db.user.find({ $text: { $search: "篮球" } })
4.3.3 删除索引
scss
# 删除指定索引(索引名可通过 getIndexes() 获取)
db.user.dropIndex("age_1")
# 删除所有索引(保留 _id 索引)
db.user.dropIndexes()
4.3.4 索引使用建议
-
优先为查询条件、排序字段创建索引;
-
复合索引遵循「最左前缀原则」(查询条件需包含索引第一个字段);
-
避免创建过多索引(会降低插入 / 更新速度);
-
用
explain()分析查询是否使用索引: db.user.find({ age: { $gt: 20 } }).explain("executionStats") # 查看 executionStats -> executionStages -> stage: # IXSCAN 表示使用索引,COLLSCAN 表示全表扫描(未使用索引)
五、数据备份与恢复
5.1 备份(mongodump)
bash
# 备份所有数据库
mongodump --out /data/backup/mongodb/20260208
# 备份指定数据库
mongodump --db test_db --out /data/backup/mongodb/20260208
# 备份指定集合
mongodump --db test_db --collection user --out /data/backup/mongodb/20260208
5.2 恢复(mongorestore)
bash
# 恢复所有数据库
mongorestore /data/backup/mongodb/20260208
# 恢复指定数据库
mongorestore --db test_db /data/backup/mongodb/20260208/test_db
# 恢复指定集合
mongorestore --db test_db --collection user /data/backup/mongodb/20260208/test_db/user.bson
六、集群部署(副本集 / 分片)
6.1 副本集(Replica Set)
副本集是 MongoDB 高可用核心,由一个主节点(Primary)、多个从节点(Secondary)和可选的仲裁节点(Arbiter)组成,主节点负责读写,从节点同步数据,主节点故障时自动切换。
6.1.1 搭建副本集(3 节点示例)
css
# 1. 创建数据目录
mkdir -p /data/mongodb/{node1,node2,node3}
# 2. 启动 3 个节点
mongod --port 27017 --dbpath /data/mongodb/node1 --replSet rs0 --fork --logpath /var/log/mongodb/node1.log
mongod --port 27018 --dbpath /data/mongodb/node2 --replSet rs0 --fork --logpath /var/log/mongodb/node2.log
mongod --port 27019 --dbpath /data/mongodb/node3 --replSet rs0 --fork --logpath /var/log/mongodb/node3.log
# 3. 初始化副本集
mongosh --port 27017
rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "localhost:27017" },
{ _id: 1, host: "localhost:27018" },
{ _id: 2, host: "localhost:27019" }
]
})
# 4. 查看副本集状态
rs.status()
6.2 分片集群(Sharding)
分片集群用于海量数据存储,将数据分散到多个分片(Shard)节点,实现水平扩展,核心组件:
- 分片(Shard):存储数据的节点(通常是副本集);
- 配置服务器(Config Server):存储集群元数据;
- 路由服务器(Mongos):接收客户端请求,路由到对应分片。
6.2.1 分片集群搭建(简化版)
css
# 1. 启动配置服务器
mongod --port 27019 --dbpath /data/mongodb/config --configsvr --replSet configRS --fork --logpath /var/log/mongodb/config.log
# 2. 启动分片节点
mongod --port 27020 --dbpath /data/mongodb/shard1 --shardsvr --replSet shard1RS --fork --logpath /var/log/mongodb/shard1.log
mongod --port 27021 --dbpath /data/mongodb/shard2 --shardsvr --replSet shard2RS --fork --logpath /var/log/mongodb/shard2.log
# 3. 启动路由服务器
mongos --port 27017 --configdb configRS/localhost:27019 --fork --logpath /var/log/mongodb/mongos.log
# 4. 添加分片到集群
mongosh --port 27017
sh.addShard("shard1RS/localhost:27020")
sh.addShard("shard2RS/localhost:27021")
# 5. 启用数据库分片
sh.enableSharding("test_db")
# 6. 为集合指定分片键
sh.shardCollection("test_db.user", { "age": 1 })
七、Node.js 操作 MongoDB(实战)
7.1 安装依赖
bash
npm install mongoose # MongoDB ODM(对象文档映射),简化操作
7.2 连接 MongoDB
javascript
const mongoose = require('mongoose');
// 连接本地 MongoDB
mongoose.connect('mongodb://localhost:27017/test_db')
.then(() => console.log('MongoDB 连接成功'))
.catch(err => console.error('连接失败:', err));
// 连接副本集
// mongoose.connect('mongodb://localhost:27017,localhost:27018,localhost:27019/test_db?replicaSet=rs0');
7.3 定义 Schema 和 Model
php
// 定义用户 Schema(相当于表结构)
const userSchema = new mongoose.Schema({
name: { type: String, required: true }, // 必传
age: { type: Number, min: 0, max: 120 }, // 数值范围
gender: { type: String, enum: ["男", "女"] }, // 枚举值
hobbies: [String], // 字符串数组
address: {
city: String,
district: String
},
create_time: { type: Date, default: Date.now } // 默认值
});
// 创建 Model(对应 user 集合)
const User = mongoose.model('User', userSchema);
7.4 增删改查示例
javascript
// 1. 新增用户
async function createUser() {
const user = new User({
name: "张三",
age: 20,
gender: "男",
hobbies: ["篮球", "编程"],
address: { city: "北京", district: "朝阳区" }
});
await user.save();
console.log('用户创建成功:', user);
}
// 2. 查询用户
async function findUsers() {
// 查询所有男性用户
const users = await User.find({ gender: "男" });
console.log('查询结果:', users);
// 分页查询:第 2 页,每页 10 条
const pageUsers = await User.find()
.skip(10)
.limit(10)
.sort({ age: -1 });
}
// 3. 更新用户
async function updateUser() {
await User.updateOne({ name: "张三" }, { $set: { age: 21 } });
console.log('更新成功');
}
// 4. 删除用户
async function deleteUser() {
await User.deleteOne({ name: "张三" });
console.log('删除成功');
}
// 执行函数
createUser();
八、常见问题与优化
8.1 性能优化
- 合理创建索引:避免全表扫描;
- 限制返回字段:只查询需要的字段,减少数据传输;
- 批量操作:用 insertMany/updateMany 替代循环单条操作;
- 避免大文档:单个文档建议小于 16MB;
- 启用读写分离:副本集从节点只读,分担主节点压力。
8.2 常见错误
connect ECONNREFUSED:MongoDB 服务未启动,或端口错误;duplicate key error:唯一索引字段重复;BSONObjTooLarge:文档超过 16MB 限制;CursorNotFound:游标超时,需缩短查询时间或增加游标超时时间。
8.3 安全建议
-
设置访问权限:为数据库创建用户,禁止匿名访问; use admin db.createUser({ user: "admin", pwd: "123456", roles: [{ role: "root", db: "admin" }] })
-
启用认证:启动 MongoDB 时添加
--auth参数; -
限制 IP 访问:仅允许业务服务器访问 MongoDB 端口;
-
加密传输:启用 SSL/TLS,避免数据明文传输。
九、总结
MongoDB 作为文档型数据库,核心优势是「灵活、高性能、易扩展」,适合以下场景:
- 高频读写的互联网应用(如社交、电商);
- 海量日志 / 埋点数据存储;
- 微服务架构下的独立数据存储;
- 需快速迭代、字段灵活的业务。
本教程覆盖 MongoDB 从环境搭建到集群部署的全流程,核心要点:
- 核心概念:数据库→集合→文档,BSON 数据类型;
- 基础操作:增删改查(insert/find/update/delete);
- 高级特性:聚合管道、索引、副本集 / 分片;
- 实战:Node.js + Mongoose 操作 MongoDB;
- 优化:索引优化、性能调优、安全配置。