MongoDB 教程
MongoDB 是一个基于分布式文件存储的开源 NoSQL 数据库系统,由 C++ 编写,旨在为 Web 应用提供可扩展的高性能数据存储解决方案。本教程将帮助你掌握 MongoDB 的基础知识和实用技能。
MongoDB 简介
什么是 MongoDB?
MongoDB 是一个面向文档的数据库,它将数据存储在类似 JSON 的文档中,而不是传统关系数据库的表格中。这种灵活的数据模型使得 MongoDB 能够存储结构复杂的数据,并且可以随着应用需求的变化而轻松调整数据模式。
MongoDB 的主要特点
- 文档数据模型:数据以 BSON(二进制 JSON)格式存储,支持嵌套、数组等复杂结构
- 高性能:支持索引、分片、复制集,提供高吞吐量和低延迟
- 高可用性:通过复制集实现数据冗余和自动故障转移
- 水平扩展:通过分片实现数据分布式存储,支持大规模数据集
- 灵活的查询语言:强大的查询功能,支持聚合、全文搜索等
- 无模式设计:集合中的文档可以有不同的字段,无需预定义结构
MongoDB 与关系型数据库的比较
SQL 术语/概念 | MongoDB 术语/概念 |
---|---|
数据库 (Database) | 数据库 (Database) |
表 (Table) | 集合 (Collection) |
行 (Row) | 文档 (Document) |
列 (Column) | 字段 (Field) |
主键 (Primary Key) | _id 字段 |
索引 (Index) | 索引 (Index) |
表连接 (Table Joins) | $lookup, 嵌入文档 |
外键 (Foreign Key) | 引用 (References) |
安装 MongoDB
Windows 安装
- 访问 MongoDB 下载页面
- 选择 Windows 版本并下载 MSI 安装包
- 运行安装程序,按照向导完成安装
- 可选择安装 MongoDB Compass(图形化管理工具)
- 安装完成后,MongoDB 服务会自动启动
macOS 安装
使用 Homebrew 安装:
# 安装 Homebrew(如果尚未安装)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# 安装 MongoDB
brew tap mongodb/brew
brew install mongodb-community
# 启动 MongoDB 服务
brew services start mongodb-community
Linux 安装 (Ubuntu)
# 导入公钥
wget -qO - https://www.mongodb.org/static/pgp/server-5.0.asc | sudo apt-key add -
# 创建源列表文件
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/5.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-5.0.list
# 更新包数据库
sudo apt-get update
# 安装 MongoDB
sudo apt-get install -y mongodb-org
# 启动 MongoDB 服务
sudo systemctl start mongod
# 设置开机自启
sudo systemctl enable mongod
Docker 安装
# 拉取 MongoDB 镜像
docker pull mongo:latest
# 运行 MongoDB 容器
docker run -d --name mongodb -p 27017:27017 -v mongodb_data:/data/db mongo:latest
验证安装
安装完成后,可以通过以下命令验证 MongoDB 是否正常运行:
# 连接到 MongoDB Shell
mongo
# 或使用新版客户端
mongosh
# 显示版本信息
db.version()
MongoDB 基本概念
数据库、集合和文档
- 数据库 (Database):MongoDB 中的数据库是集合的容器,一个 MongoDB 服务器可以有多个数据库
- 集合 (Collection):集合是 MongoDB 文档的分组,类似于关系数据库中的表
- 文档 (Document):文档是 MongoDB 中的基本数据单元,由键值对组成,类似于 JSON 对象
文档结构
MongoDB 文档使用 BSON(Binary JSON)格式存储,示例如下:
{
"_id": ObjectId("60a6e5e91d0d7f2e8c3e4b7a"),
"name": "张三",
"age": 30,
"email": "zhangsan@example.com",
"address": {
"city": "北京",
"postcode": "100000"
},
"tags": ["开发", "设计", "数据库"],
"active": true,
"created_at": ISODate("2021-05-20T12:30:45.000Z")
}
数据类型
MongoDB 支持多种数据类型:
- String:字符串,UTF-8 编码
- Integer:32 位或 64 位整数
- Boolean:true 或 false
- Double:浮点数
- Array:值的列表或数组
- Object:嵌入式文档
- ObjectId:12 字节的唯一标识符,常用作 _id
- Date:日期时间,以 UNIX 时间格式存储
- Null:空值
- Regular Expression:正则表达式
- Binary Data:二进制数据
- Code:JavaScript 代码
MongoDB Shell 基本操作
连接数据库
# 连接本地 MongoDB 服务器
mongo
# 或使用新版客户端
mongosh
# 连接远程 MongoDB 服务器
mongo mongodb://username:password@hostname:port/database
# 或使用新版客户端
mongosh mongodb://username:password@hostname:port/database
数据库操作
// 显示所有数据库
show dbs
// 使用/创建数据库(如果不存在)
use mydb
// 显示当前数据库
db
// 删除当前数据库
db.dropDatabase()
集合操作
// 创建集合
db.createCollection("users")
// 显示所有集合
show collections
// 删除集合
db.users.drop()
文档操作
插入文档
// 插入单个文档
db.users.insertOne({
name: "张三",
age: 30,
email: "zhangsan@example.com"
})
// 插入多个文档
db.users.insertMany([
{ name: "李四", age: 25, email: "lisi@example.com" },
{ name: "王五", age: 35, email: "wangwu@example.com" }
])
查询文档
// 查询所有文档
db.users.find()
// 格式化显示结果
db.users.find().pretty()
// 条件查询
db.users.find({ age: 30 })
// 使用查询操作符
db.users.find({ age: { $gt: 25 } }) // 大于 25 岁
// AND 条件
db.users.find({ age: { $gt: 25 }, name: "张三" })
// OR 条件
db.users.find({ $or: [{ age: 30 }, { name: "李四" }] })
// 投影(只返回指定字段)
db.users.find({}, { name: 1, email: 1, _id: 0 })
// 限制结果数量
db.users.find().limit(2)
// 跳过结果
db.users.find().skip(1)
// 排序(1 升序,-1 降序)
db.users.find().sort({ age: 1 })
// 统计文档数量
db.users.countDocuments({ age: { $gt: 25 } })
更新文档
// 更新单个文档
db.users.updateOne(
{ name: "张三" },
{ $set: { age: 31, "address.city": "上海" } }
)
// 更新多个文档
db.users.updateMany(
{ age: { $lt: 30 } },
{ $inc: { age: 1 } }
)
// 替换整个文档
db.users.replaceOne(
{ name: "张三" },
{ name: "张三", age: 32, email: "zhangsan@example.com" }
)
删除文档
// 删除单个文档
db.users.deleteOne({ name: "张三" })
// 删除多个文档
db.users.deleteMany({ age: { $lt: 30 } })
// 删除所有文档
db.users.deleteMany({})
高级查询
查询操作符
// 比较操作符
db.users.find({ age: { $eq: 30 } }) // 等于
db.users.find({ age: { $ne: 30 } }) // 不等于
db.users.find({ age: { $gt: 30 } }) // 大于
db.users.find({ age: { $gte: 30 } }) // 大于等于
db.users.find({ age: { $lt: 30 } }) // 小于
db.users.find({ age: { $lte: 30 } }) // 小于等于
db.users.find({ age: { $in: [25, 30, 35] } }) // 在指定数组中
db.users.find({ age: { $nin: [25, 30] } }) // 不在指定数组中
// 逻辑操作符
db.users.find({ $and: [{ age: { $gt: 25 } }, { age: { $lt: 35 } }] })
db.users.find({ $or: [{ age: 25 }, { age: 35 }] })
db.users.find({ age: { $not: { $gt: 30 } } })
db.users.find({ $nor: [{ age: 25 }, { age: 35 }] })
// 元素操作符
db.users.find({ age: { $exists: true } }) // 字段存在
db.users.find({ age: { $type: "number" } }) // 字段类型
// 数组操作符
db.users.find({ tags: { $all: ["开发", "设计"] } }) // 包含所有指定元素
db.users.find({ tags: { $size: 3 } }) // 数组大小
db.users.find({ tags: { $elemMatch: { $eq: "开发" } } }) // 数组元素匹配
正则表达式查询
// 使用正则表达式查询
db.users.find({ name: /^张/ }) // 以"张"开头的名字
db.users.find({ email: /example\.com$/ }) // 以"example.com"结尾的邮箱
嵌套文档查询
// 精确匹配嵌套文档
db.users.find({ address: { city: "北京", postcode: "100000" } })
// 查询嵌套字段
db.users.find({ "address.city": "北京" })
数组查询
// 精确匹配数组
db.users.find({ tags: ["开发", "设计", "数据库"] })
// 查询数组包含元素
db.users.find({ tags: "开发" })
// 查询数组中的特定元素
db.users.find({ "tags.0": "开发" }) // 第一个元素是"开发"
索引
创建索引
// 创建单字段索引
db.users.createIndex({ name: 1 }) // 1 表示升序索引,-1 表示降序索引
// 创建复合索引
db.users.createIndex({ name: 1, age: -1 })
// 创建唯一索引
db.users.createIndex({ email: 1 }, { unique: true })
// 创建 TTL 索引(文档自动过期)
db.sessions.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 })
// 创建文本索引
db.articles.createIndex({ content: "text" })
// 创建地理空间索引
db.places.createIndex({ location: "2dsphere" })
查看索引
// 查看集合的所有索引
db.users.getIndexes()
删除索引
// 删除特定索引
db.users.dropIndex("name_1")
// 删除所有索引(除了 _id 索引)
db.users.dropIndexes()
聚合操作
MongoDB 的聚合框架提供了强大的数据处理能力,可以对文档进行转换和组合。
聚合管道
// 基本聚合示例
db.orders.aggregate([
{ $match: { status: "completed" } },
{ $group: { _id: "$customer_id", totalAmount: { $sum: "$amount" } } },
{ $sort: { totalAmount: -1 } }
])
常用聚合操作符
// $match:筛选文档
db.users.aggregate([
{ $match: { age: { $gt: 25 } } }
])
// $group:分组
db.users.aggregate([
{ $group: { _id: "$age", count: { $sum: 1 } } }
])
// $project:投影
db.users.aggregate([
{ $project: { name: 1, email: 1, _id: 0 } }
])
// $sort:排序
db.users.aggregate([
{ $sort: { age: -1 } }
])
// $limit:限制结果数量
db.users.aggregate([
{ $limit: 5 }
])
// $skip:跳过结果
db.users.aggregate([
{ $skip: 5 }
])
// $unwind:展开数组
db.users.aggregate([
{ $unwind: "$tags" }
])
// $lookup:连接操作(类似 SQL JOIN)
db.orders.aggregate([
{
$lookup: {
from: "customers",
localField: "customer_id",
foreignField: "_id",
as: "customer_info"
}
}
])
聚合表达式
// 算术表达式
db.products.aggregate([
{
$project: {
name: 1,
discountPrice: { $multiply: ["$price", 0.9] }
}
}
])
// 日期表达式
db.sales.aggregate([
{
$project: {
year: { $year: "$date" },
month: { $month: "$date" },
day: { $dayOfMonth: "$date" }
}
}
])
// 字符串表达式
db.users.aggregate([
{
$project: {
fullName: { $concat: ["$firstName", " ", "$lastName"] },
nameLength: { $strLenCP: "$name" }
}
}
])
// 条件表达式
db.users.aggregate([
{
$project: {
category: {
$cond: { if: { $gte: ["$age", 30] }, then: "成年人", else: "青年" }
}
}
}
])
事务
从 MongoDB 4.0 开始,支持多文档事务,允许对多个文档的操作作为一个单元执行。
事务示例
// 启动会话
const session = db.getMongo().startSession();
// 开始事务
session.startTransaction();
try {
// 执行操作
const usersCollection = session.getDatabase("mydb").getCollection("users");
const ordersCollection = session.getDatabase("mydb").getCollection("orders");
usersCollection.updateOne(
{ _id: ObjectId("60a6e5e91d0d7f2e8c3e4b7a") },
{ $inc: { balance: -100 } }
);
ordersCollection.insertOne({
user_id: ObjectId("60a6e5e91d0d7f2e8c3e4b7a"),
amount: 100,
status: "completed"
});
// 提交事务
session.commitTransaction();
} catch (error) {
// 出错时回滚事务
session.abortTransaction();
print("事务失败:", error);
} finally {
// 结束会话
session.endSession();
}
数据建模
嵌入式数据模型
嵌入式数据模型将相关数据嵌入到单个文档中,适合一对一和一对多关系。
// 嵌入式数据模型示例
db.users.insertOne({
name: "张三",
email: "zhangsan@example.com",
address: {
street: "中关村大街",
city: "北京",
postcode: "100000"
},
orders: [
{ id: "ORD001", amount: 100, date: ISODate("2021-05-20") },
{ id: "ORD002", amount: 200, date: ISODate("2021-05-25") }
]
})
引用式数据模型
引用式数据模型通过引用(类似外键)连接相关文档,适合多对多关系或大型嵌套文档。
// 引用式数据模型示例
db.users.insertOne({
_id: ObjectId("60a6e5e91d0d7f2e8c3e4b7a"),
name: "张三",
email: "zhangsan@example.com"
})
db.orders.insertMany([
{
_id: ObjectId("60a6e5e91d0d7f2e8c3e4b7b"),
user_id: ObjectId("60a6e5e91d0d7f2e8c3e4b7a"),
amount: 100,
date: ISODate("2021-05-20")
},
{
_id: ObjectId("60a6e5e91d0d7f2e8c3e4b7c"),
user_id: ObjectId("60a6e5e91d0d7f2e8c3e4b7a"),
amount: 200,
date: ISODate("2021-05-25")
}
])
数据模型设计考虑因素
- 读写比例:读操作多的场景倾向于嵌入式模型,写操作多的场景倾向于引用式模型
- 数据一致性:嵌入式模型提供文档级别的原子性
- 数据增长:如果嵌入文档可能无限增长,考虑使用引用式模型
- 数据访问模式:考虑数据如何被查询和更新
- 文档大小限制:MongoDB 文档大小限制为 16MB
MongoDB 安全
启用身份验证
// 创建管理员用户
use admin
db.createUser({
user: "adminUser",
pwd: "securePassword",
roles: [{ role: "userAdminAnyDatabase", db: "admin" }]
})
// 创建数据库用户
use mydb
db.createUser({
user: "appUser",
pwd: "appPassword",
roles: [{ role: "readWrite", db: "mydb" }]
})
配置文件启用身份验证
在 MongoDB 配置文件中添加:
security:
authorization: enabled
角色管理
MongoDB 内置角色:
- read:允许读取指定数据库中的任何数据
- readWrite:允许读写指定数据库中的任何数据
- dbAdmin:允许在指定数据库上执行管理操作
- userAdmin:允许在指定数据库上创建和修改用户
- clusterAdmin:允许对整个集群执行管理操作
- readAnyDatabase:允许读取所有数据库
- readWriteAnyDatabase:允许读写所有数据库
- userAdminAnyDatabase:允许在所有数据库上创建和修改用户
- dbAdminAnyDatabase:允许在所有数据库上执行管理操作
- root:超级用户,拥有所有权限
网络安全
- 绑定 IP 地址:限制 MongoDB 只接受特定 IP 地址的连接
- 使用 TLS/SSL:加密客户端和服务器之间的通信
- 使用 VPN 或专用网络:将 MongoDB 部署在安全的网络环境中
MongoDB 复制集
复制集是一组维护相同数据集的 MongoDB 服务器,提供冗余和高可用性。
复制集架构
典型的复制集包含:
- 一个主节点(Primary):接收所有写操作
- 多个从节点(Secondary):复制主节点的数据
- 可选的仲裁节点(Arbiter):参与选举但不存储数据
设置复制集
- 启动多个 MongoDB 实例:
# 启动第一个实例
mongod --replSet "rs0" --port 27017 --dbpath /data/db1
# 启动第二个实例
mongod --replSet "rs0" --port 27018 --dbpath /data/db2
# 启动第三个实例
mongod --replSet "rs0" --port 27019 --dbpath /data/db3
- 初始化复制集:
// 连接到其中一个实例
mongo --port 27017
// 初始化复制集
rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "localhost:27017" },
{ _id: 1, host: "localhost:27018" },
{ _id: 2, host: "localhost:27019" }
]
})
// 查看复制集状态
rs.status()
复制集操作
// 查看复制集配置
rs.conf()
// 添加成员
rs.add("localhost:27020")
// 添加仲裁节点
rs.addArb("localhost:27021")
// 删除成员
rs.remove("localhost:27020")
// 强制重新选举
rs.stepDown()
MongoDB 分片
分片是 MongoDB 实现水平扩展的方法,通过将数据分布到多个服务器上来支持大型数据集和高吞吐量操作。
分片架构
MongoDB 分片集群包含:
- 分片(Shard):存储数据的 MongoDB 实例或复制集
- 配置服务器(Config Server):存储集群元数据
- 路由服务(mongos):路由客户端请求到适当的分片
分片键选择
选择分片键时需要考虑:
- 基数:高基数的字段(如 UUID)分布更均匀
- 写分布:避免写操作集中在少数分片
- 查询隔离:常用查询应该只需访问少数分片
- 数据增长:考虑数据如何随时间增长
MongoDB Atlas
MongoDB Atlas 是 MongoDB 官方提供的数据库即服务(DBaaS)解决方案,提供自动化部署、扩展和管理 MongoDB 数据库的功能。
Atlas 主要特点
- 全托管服务:无需管理服务器、备份或安全
- 多云支持:可在 AWS、Azure 或 GCP 上部署
- 自动扩展:根据需求自动调整资源
- 内置监控:实时性能监控和警报
- 自动备份:定期备份和时间点恢复
- 高级安全:网络隔离、加密、访问控制
MongoDB 与应用程序集成
Node.js 集成
使用官方 MongoDB Node.js 驱动程序:
// 安装驱动程序
// npm install mongodb
// 连接到 MongoDB
const { MongoClient } = require('mongodb');
const uri = "mongodb://localhost:27017";
const client = new MongoClient(uri);
async function run() {
try {
await client.connect();
const database = client.db("mydb");
const users = database.collection("users");
// 插入文档
const insertResult = await users.insertOne({
name: "张三",
age: 30,
email: "zhangsan@example.com"
});
console.log(`插入的文档 ID: ${insertResult.insertedId}`);
// 查询文档
const query = { age: { $gt: 25 } };
const cursor = users.find(query);
await cursor.forEach(doc => {
console.log(doc);
});
} finally {
await client.close();
}
}
run().catch(console.dir);
Python 集成
使用 PyMongo 驱动程序:
# 安装驱动程序
# pip install pymongo
from pymongo import MongoClient
# 连接到 MongoDB
client = MongoClient('mongodb://localhost:27017/')
db = client['mydb']
users = db['users']
# 插入文档
user_data = {
"name": "张三",
"age": 30,
"email": "zhangsan@example.com"
}
result = users.insert_one(user_data)
print(f"插入的文档 ID: {result.inserted_id}")
# 查询文档
for user in users.find({"age": {"$gt": 25}}):
print(user)
Java 集成
使用 MongoDB Java 驱动程序:
// 添加依赖
// implementation 'org.mongodb:mongodb-driver-sync:4.3.0'
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
public class MongoDBExample {
public static void main(String[] args) {
// 连接到 MongoDB
try (MongoClient mongoClient = MongoClients.create("mongodb://localhost:27017")) {
MongoDatabase database = mongoClient.getDatabase("mydb");
MongoCollection<Document> collection = database.getCollection("users");
// 插入文档
Document doc = new Document("name", "张三")
.append("age", 30)
.append("email", "zhangsan@example.com");
collection.insertOne(doc);
System.out.println("文档插入成功");
// 查询文档
Document query = new Document("age", new Document("$gt", 25));
collection.find(query).forEach(document -> {
System.out.println(document.toJson());
});
}
}
}
MongoDB 最佳实践
性能优化
- 使用适当的索引:为常用查询创建索引,但避免过多索引
- 批量操作:使用批量插入、更新和删除操作
- 投影:只返回需要的字段,减少网络传输
- 限制结果集大小:使用 limit() 和分页
- 使用覆盖查询:查询只包含索引字段
- 避免大型文档:保持文档大小合理,避免接近 16MB 限制
- 使用适当的读写关注:根据应用需求选择合适的读写关注级别
数据库管理
- 定期备份:实施定期备份策略
- 监控数据库性能:使用 MongoDB Atlas 或其他监控工具
- 计划容量:预测数据增长并相应扩展
- 实施适当的安全措施:身份验证、授权和加密
- 保持 MongoDB 版本更新:定期升级到最新稳定版本
常见问题解决
- 慢查询:使用
explain()
分析查询性能,创建适当的索引 - 内存使用:确保 MongoDB 有足够的内存,避免频繁的页面交换
- 连接问题:检查网络配置、防火墙规则和身份验证设置
- 数据一致性:使用适当的写关注级别和事务
- 磁盘空间:监控磁盘使用情况,及时清理不需要的数据
相关资源
本文档将持续更新,如有问题请通过 GitHub Issues 反馈。
MongoDB 基本概念
数据库、集合和文档
- 数据库 (Database):MongoDB 中的数据库是集合的容器,一个 MongoDB 服务器可以有多个数据库
- 集合 (Collection):集合是 MongoDB 文档的分组,类似于关系数据库中的表
- 文档 (Document):文档是 MongoDB 中的基本数据单元,由键值对组成,类似于 JSON 对象
文档结构
MongoDB 文档使用 BSON(Binary JSON)格式存储,示例如下:
{
"_id": ObjectId("60a6e5e91d0d7f2e8c3e4b7a"),
"name": "张三",
"age": 30,
"email": "zhangsan@example.com",
"address": {
"city": "北京",
"postcode": "100000"
},
"tags": ["开发", "设计", "数据库"],
"active": true,
"created_at": ISODate("2021-05-20T12:30:45.000Z")
}
数据类型
MongoDB 支持多种数据类型:
- String:字符串,UTF-8 编码
- Integer:32 位或 64 位整数
- Boolean:true 或 false
- Double:浮点数
- Array:值的列表或数组
- Object:嵌入式文档
- ObjectId:12 字节的唯一标识符,常用作 _id
- Date:日期时间,以 UNIX 时间格式存储
- Null:空值
- Regular Expression:正则表达式
- Binary Data:二进制数据
- Code:JavaScript 代码
MongoDB Shell 基本操作
连接数据库
# 连接本地 MongoDB 服务器
mongo
# 或使用新版客户端
mongosh
# 连接远程 MongoDB 服务器
mongo mongodb://username:password@hostname:port/database
# 或使用新版客户端
mongosh mongodb://username:password@hostname:port/database
数据库操作
// 显示所有数据库
show dbs
// 使用/创建数据库(如果不存在)
use mydb
// 显示当前数据库
db
// 删除当前数据库
db.dropDatabase()
集合操作
// 创建集合
db.createCollection("users")
// 显示所有集合
show collections
// 删除集合
db.users.drop()
文档操作
插入文档
// 插入单个文档
db.users.insertOne({
name: "张三",
age: 30,
email: "zhangsan@example.com"
})
// 插入多个文档
db.users.insertMany([
{ name: "李四", age: 25, email: "lisi@example.com" },
{ name: "王五", age: 35, email: "wangwu@example.com" }
])
查询文档
// 查询所有文档
db.users.find()
// 格式化显示结果
db.users.find().pretty()
// 条件查询
db.users.find({ age: 30 })
// 使用查询操作符
db.users.find({ age: { $gt: 25 } }) // 大于 25 岁
// AND 条件
db.users.find({ age: { $gt: 25 }, name: "张三" })
// OR 条件
db.users.find({ $or: [{ age: 30 }, { name: "李四" }] })
// 投影(只返回指定字段)
db.users.find({}, { name: 1, email: 1, _id: 0 })
// 限制结果数量
db.users.find().limit(2)
// 跳过结果
db.users.find().skip(1)
// 排序(1 升序,-1 降序)
db.users.find().sort({ age: 1 })
// 统计文档数量
db.users.countDocuments({ age: { $gt: 25 } })
更新文档
// 更新单个文档
db.users.updateOne(
{ name: "张三" },
{ $set: { age: 31, "address.city": "上海" } }
)
// 更新多个文档
db.users.updateMany(
{ age: { $lt: 30 } },
{ $inc: { age: 1 } }
)
// 替换整个文档
db.users.replaceOne(
{ name: "张三" },
{ name: "张三", age: 32, email: "zhangsan@example.com" }
)
删除文档
// 删除单个文档
db.users.deleteOne({ name: "张三" })
// 删除多个文档
db.users.deleteMany({ age: { $lt: 30 } })
// 删除所有文档
db.users.deleteMany({})
高级查询
查询操作符
// 比较操作符
db.users.find({ age: { $eq: 30 } }) // 等于
db.users.find({ age: { $ne: 30 } }) // 不等于
db.users.find({ age: { $gt: 30 } }) // 大于
db.users.find({ age: { $gte: 30 } }) // 大于等于
db.users.find({ age: { $lt: 30 } }) // 小于
db.users.find({ age: { $lte: 30 } }) // 小于等于
db.users.find({ age: { $in: [25, 30, 35] } }) // 在指定数组中
db.users.find({ age: { $nin: [25, 30] } }) // 不在指定数组中
// 逻辑操作符
db.users.find({ $and: [{ age: { $gt: 25 } }, { age: { $lt: 35 } }] })
db.users.find({ $or: [{ age: 25 }, { age: 35 }] })
db.users.find({ age: { $not: { $gt: 30 } } })
db.users.find({ $nor: [{ age: 25 }, { age: 35 }] })
// 元素操作符
db.users.find({ age: { $exists: true } }) // 字段存在
db.users.find({ age: { $type: "number" } }) // 字段类型
// 数组操作符
db.users.find({ tags: { $all: ["开发", "设计"] } }) // 包含所有指定元素
db.users.find({ tags: { $size: 3 } }) // 数组大小
db.users.find({ tags: { $elemMatch: { $eq: "开发" } } }) // 数组元素匹配
正则表达式查询
// 使用正则表达式查询
db.users.find({ name: /^张/ }) // 以"张"开头的名字
db.users.find({ email: /example\.com$/ }) // 以"example.com"结尾的邮箱
嵌套文档查询
// 精确匹配嵌套文档
db.users.find({ address: { city: "北京", postcode: "100000" } })
// 查询嵌套字段
db.users.find({ "address.city": "北京" })
数组查询
// 精确匹配数组
db.users.find({ tags: ["开发", "设计", "数据库"] })
// 查询数组包含元素
db.users.find({ tags: "开发" })
// 查询数组中的特定元素
db.users.find({ "tags.0": "开发" }) // 第一个元素是"开发"
索引
创建索引
// 创建单字段索引
db.users.createIndex({ name: 1 }) // 1 表示升序索引,-1 表示降序索引
// 创建复合索引
db.users.createIndex({ name: 1, age: -1 })
// 创建唯一索引
db.users.createIndex({ email: 1 }, { unique: true })
// 创建 TTL 索引(文档自动过期)
db.sessions.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 })
// 创建文本索引
db.articles.createIndex({ content: "text" })
// 创建地理空间索引
db.places.createIndex({ location: "2dsphere" })
查看索引
// 查看集合的所有索引
db.users.getIndexes()
删除索引
// 删除特定索引
db.users.dropIndex("name_1")
// 删除所有索引(除了 _id 索引)
db.users.dropIndexes()
聚合操作
MongoDB 的聚合框架提供了强大的数据处理能力,可以对文档进行转换和组合。
聚合管道
// 基本聚合示例
db.orders.aggregate([
{ $match: { status: "completed" } },
{ $group: { _id: "$customer_id", totalAmount: { $sum: "$amount" } } },
{ $sort: { totalAmount: -1 } }
])
常用聚合操作符
// $match:筛选文档
db.users.aggregate([
{ $match: { age: { $gt: 25 } } }
])
// $group:分组
db.users.aggregate([
{ $group: { _id: "$age", count: { $sum: 1 } } }
])
// $project:投影
db.users.aggregate([
{ $project: { name: 1, email: 1, _id: 0 } }
])
// $sort:排序
db.users.aggregate([
{ $sort: { age: -1 } }
])
// $limit:限制结果数量
db.users.aggregate([
{ $limit: 5 }
])
// $skip:跳过结果
db.users.aggregate([
{ $skip: 5 }
])
// $unwind:展开数组
db.users.aggregate([
{ $unwind: "$tags" }
])
// $lookup:连接操作(类似 SQL JOIN)
db.orders.aggregate([
{
$lookup: {
from: "customers",
localField: "customer_id",
foreignField: "_id",
as: "customer_info"
}
}
])
聚合表达式
// 算术表达式
db.products.aggregate([
{
$project: {
name: 1,
discountPrice: { $multiply: ["$price", 0.9] }
}
}
])
// 日期表达式
db.sales.aggregate([
{
$project: {
year: { $year: "$date" },
month: { $month: "$date" },
day: { $dayOfMonth: "$date" }
}
}
])
// 字符串表达式
db.users.aggregate([
{
$project: {
fullName: { $concat: ["$firstName", " ", "$lastName"] },
nameLength: { $strLenCP: "$name" }
}
}
])
// 条件表达式
db.users.aggregate([
{
$project: {
category: {
$cond: { if: { $gte: ["$age", 30] }, then: "成年人", else: "青年" }
}
}
}
])
事务
从 MongoDB 4.0 开始,支持多文档事务,允许对多个文档的操作作为一个单元执行。
事务示例
// 启动会话
const session = db.getMongo().startSession();
// 开始事务
session.startTransaction();
try {
// 执行操作
const usersCollection = session.getDatabase("mydb").getCollection("users");
const ordersCollection = session.getDatabase("mydb").getCollection("orders");
usersCollection.updateOne(
{ _id: ObjectId("60a6e5e91d0d7f2e8c3e4b7a") },
{ $inc: { balance: -100 } }
);
ordersCollection.insertOne({
user_id: ObjectId("60a6e5e91d0d7f2e8c3e4b7a"),
amount: 100,
status: "completed"
});
// 提交事务
session.commitTransaction();
} catch (error) {
// 出错时回滚事务
session.abortTransaction();
print("事务失败:", error);
} finally {
// 结束会话
session.endSession();
}
数据建模
嵌入式数据模型
嵌入式数据模型将相关数据嵌入到单个文档中,适合一对一和一对多关系。
// 嵌入式数据模型示例
db.users.insertOne({
name: "张三",
email: "zhangsan@example.com",
address: {
street: "中关村大街",
city: "北京",
postcode: "100000"
},
orders: [
{ id: "ORD001", amount: 100, date: ISODate("2021-05-20") },
{ id: "ORD002", amount: 200, date: ISODate("2021-05-25") }
]
})
引用式数据模型
引用式数据模型通过引用(类似外键)连接相关文档,适合多对多关系或大型嵌套文档。
// 引用式数据模型示例
db.users.insertOne({
_id: ObjectId("60a6e5e91d0d7f2e8c3e4b7a"),
name: "张三",
email: "zhangsan@example.com"
})
db.orders.insertMany([
{
_id: ObjectId("60a6e5e91d0d7f2e8c3e4b7b"),
user_id: ObjectId("60a6e5e91d0d7f2e8c3e4b7a"),
amount: 100,
date: ISODate("2021-05-20")
},
{
_id: ObjectId("60a6e5e91d0d7f2e8c3e4b7c"),
user_id: ObjectId("60a6e5e91d0d7f2e8c3e4b7a"),
amount: 200,
date: ISODate("2021-05-25")
}
])
数据模型设计考虑因素
- 读写比例:读操作多的场景倾向于嵌入式模型,写操作多的场景倾向于引用式模型
- 数据一致性:嵌入式模型提供文档级别的原子性
- 数据增长:如果嵌入文档可能无限增长,考虑使用引用式模型
- 数据访问模式:考虑数据如何被查询和更新
- 文档大小限制:MongoDB 文档大小限制为 16MB
MongoDB 安全
启用身份验证
// 创建管理员用户
use admin
db.createUser({
user: "adminUser",
pwd: "securePassword",
roles: [{ role: "userAdminAnyDatabase", db: "admin" }]
})
// 创建数据库用户
use mydb
db.createUser({
user: "appUser",
pwd: "appPassword",
roles: [{ role: "readWrite", db: "mydb" }]
})
配置文件启用身份验证
在 MongoDB 配置文件中添加:
security:
authorization: enabled
角色管理
MongoDB 内置角色:
- read:允许读取指定数据库中的任何数据
- readWrite:允许读写指定数据库中的任何数据
- dbAdmin:允许在指定数据库上执行管理操作
- userAdmin:允许在指定数据库上创建和修改用户
- clusterAdmin:允许对整个集群执行管理操作
- readAnyDatabase:允许读取所有数据库
- readWriteAnyDatabase:允许读写所有数据库
- userAdminAnyDatabase:允许在所有数据库上创建和修改用户
- dbAdminAnyDatabase:允许在所有数据库上执行管理操作
- root:超级用户,拥有所有权限
网络安全
- 绑定 IP 地址:限制 MongoDB 只接受特定 IP 地址的连接
- 使用 TLS/SSL:加密客户端和服务器之间的通信
- 使用 VPN 或专用网络:将 MongoDB 部署在安全的网络环境中
MongoDB 复制集
复制集是一组维护相同数据集的 MongoDB 服务器,提供冗余和高可用性。
复制集架构
典型的复制集包含:
- 一个主节点(Primary):接收所有写操作
- 多个从节点(Secondary):复制主节点的数据
- 可选的仲裁节点(Arbiter):参与选举但不存储数据
设置复制集
- 启动多个 MongoDB 实例:
# 启动第一个实例
mongod --replSet "rs0" --port 27017 --dbpath /data/db1
# 启动第二个实例
mongod --replSet "rs0" --port 27018 --dbpath /data/db2
# 启动第三个实例
mongod --replSet "rs0" --port 27019 --dbpath /data/db3
- 初始化复制集:
// 连接到其中一个实例
mongo --port 27017
// 初始化复制集
rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "localhost:27017" },
{ _id: 1, host: "localhost:27018" },
{ _id: 2, host: "localhost:27019" }
]
})
// 查看复制集状态
rs.status()
复制集操作
// 查看复制集配置
rs.conf()
// 添加成员
rs.add("localhost:27020")
// 添加仲裁节点
rs.addArb("localhost:27021")
// 删除成员
rs.remove("localhost:27020")
// 强制重新选举
rs.stepDown()
MongoDB 分片
分片是 MongoDB 实现水平扩展的方法,通过将数据分布到多个服务器上来支持大型数据集和高吞吐量操作。
分片架构
MongoDB 分片集群包含:
- 分片(Shard):存储数据的 MongoDB 实例或复制集
- 配置服务器(Config Server):存储集群元数据
- 路由服务(mongos):路由客户端请求到适当的分片
设置分片集群
设置分片集群的步骤较为复杂,通常包括:
- 设置配置服务器复制集
- 设置多个分片(每个分片通常是一个复制集)
- 设置 mongos 路由服务
- 添加分片到集群
- 启用数据库分片
- 对集合进行分片
分片键选择
选择分片键时需要考虑:
- 基数:高基数的字段(如 UUID)分布更均匀
- 写分布:避免写操作集中在少数分片
- 查询隔离:常用查询应该只需访问少数分片
- 数据增长:考虑数据如何随时间增长
MongoDB Atlas
MongoDB Atlas 是 MongoDB 官方提供的数据库即服务(DBaaS)解决方案,提供自动化部署、扩展和管理 MongoDB 数据库的功能。
Atlas 主要特点
- 全托管服务:无需管理服务器、备份或安全
- 多云支持:可在 AWS、Azure 或 GCP 上部署
- 自动扩展:根据需求自动调整资源
- 内置监控:实时性能监控和警报
- 自动备份:定期备份和时间点恢复
- 高级安全:网络隔离、加密、访问控制
使用 Atlas 的步骤
- 创建 MongoDB Atlas 账户
- 创建新集群
- 配置网络访问和数据库用户
- 连接到集群
- 导入数据并开始使用
MongoDB 与应用程序集成
Node.js 集成
使用官方 MongoDB Node.js 驱动程序:
// 安装驱动程序
// npm install mongodb
// 连接到 MongoDB
const { MongoClient } = require('mongodb');
const uri = "mongodb://localhost:27017";
const client = new MongoClient(uri);
async function run() {
try {
await client.connect();
const database = client.db("mydb");
const users = database.collection("users");
// 插入文档
const insertResult = await users.insertOne({
name: "张三",
age: 30,
email: "zhangsan@example.com"
});
console.log(`插入的文档 ID: ${insertResult.insertedId}`);
// 查询文档
const query = { age: { $gt: 25 } };
const cursor = users.find(query);
await cursor.forEach(doc => {
console.log(doc);
});
} finally {
await client.close();
}
}
run().catch(console.dir);
Python 集成
使用 PyMongo 驱动程序:
# 安装驱动程序
# pip install pymongo
from pymongo import MongoClient
# 连接到 MongoDB
client = MongoClient('mongodb://localhost:27017/')
db = client['mydb']
users = db['users']
# 插入文档
user_data = {
"name": "张三",
"age": 30,
"email": "zhangsan@example.com"
}
result = users.insert_one(user_data)
print(f"插入的文档 ID: {result.inserted_id}")
# 查询文档
for user in users.find({"age": {"$gt": 25}}):
print(user)
Java 集成
使用 MongoDB Java 驱动程序:
// 添加依赖
// implementation 'org.mongodb:mongodb-driver-sync:4.3.0'
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
public class MongoDBExample {
public static void main(String[] args) {
// 连接到 MongoDB
try (MongoClient mongoClient = MongoClients.create("mongodb://localhost:27017")) {
MongoDatabase database = mongoClient.getDatabase("mydb");
MongoCollection<Document> collection = database.getCollection("users");
// 插入文档
Document doc = new Document("name", "张三")
.append("age", 30)
.append("email", "zhangsan@example.com");
collection.insertOne(doc);
System
# MongoDB 教程
MongoDB 是一个基于分布式文件存储的开源 NoSQL 数据库系统,由 C++ 编写,旨在为 Web 应用提供可扩展的高性能数据存储解决方案。本教程将帮助你掌握 MongoDB 的基础知识和实用技能。
## MongoDB 简介
### 什么是 MongoDB?
MongoDB 是一个面向文档的数据库,它将数据存储在类似 JSON 的文档中,而不是传统关系数据库的表格中。这种灵活的数据模型使得 MongoDB 能够存储结构复杂的数据,并且可以随着应用需求的变化而轻松调整数据模式。
### MongoDB 的主要特点
- **文档数据模型**:数据以 BSON(二进制 JSON)格式存储,支持嵌套、数组等复杂结构
- **高性能**:支持索引、分片、复制集,提供高吞吐量和低延迟
- **高可用性**:通过复制集实现数据冗余和自动故障转移
- **水平扩展**:通过分片实现数据分布式存储,支持大规模数据集
- **灵活的查询语言**:强大的查询功能,支持聚合、全文搜索等
- **无模式设计**:集合中的文档可以有不同的字段,无需预定义结构
### MongoDB 与关系型数据库的比较
| SQL 术语/概念 | MongoDB 术语/概念 |
|--------------|-----------------|
| 数据库 (Database) | 数据库 (Database) |
| 表 (Table) | 集合 (Collection) |
| 行 (Row) | 文档 (Document) |
| 列 (Column) | 字段 (Field) |
| 主键 (Primary Key) | _id 字段 |
| 索引 (Index) | 索引 (Index) |
| 表连接 (Table Joins) | $lookup, 嵌入文档 |
| 外键 (Foreign Key) | 引用 (References) |
## 安装 MongoDB
### Windows 安装
1. 访问 [MongoDB 下载页面](https://www.mongodb.com/try/download/community)
2. 选择 Windows 版本并下载 MSI 安装包
3. 运行安装程序,按照向导完成安装
4. 可选择安装 MongoDB Compass(图形化管理工具)
5. 安装完成后,MongoDB 服务会自动启动
### macOS 安装
使用 Homebrew 安装:
```bash
# 安装 Homebrew(如果尚未安装)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# 安装 MongoDB
brew tap mongodb/brew
brew install mongodb-community
# 启动 MongoDB 服务
brew services start mongodb-community
Linux 安装 (Ubuntu)
# 导入公钥
wget -qO - https://www.mongodb.org/static/pgp/server-5.0.asc | sudo apt-key add -
# 创建源列表文件
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/5.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-5.0.list
# 更新包数据库
sudo apt-get update
# 安装 MongoDB
sudo apt-get install -y mongodb-org
# 启动 MongoDB 服务
sudo systemctl start mongod
# 设置开机自启
sudo systemctl enable mongod
Docker 安装
# 拉取 MongoDB 镜像
docker pull mongo:latest
# 运行 MongoDB 容器
docker run -d --name mongodb -p 27017:27017 -v mongodb_data:/data/db mongo:latest
验证安装
安装完成后,可以通过以下命令验证 MongoDB 是否正常运行:
# 连接到 MongoDB Shell
mongo
# 或使用新版客户端
mongosh
# 显示版本信息
db.version()