MongoDB 最佳实践
目录
快速入门
安装与版本
当前使用版本为 MongoDB 8.2.6。
macOS
# 使用 Homebrew 安装
brew tap mongodb/brew
brew install [email protected]
# 启动服务
brew services start [email protected]
# 或手动启动
mongod --config /usr/local/etc/mongod.conf
# 连接
mongoshWindows
- 下载 MongoDB Community Server Windows 版本
- 运行安装程序
- MongoDB 默认安装在
C:\Program Files\MongoDB\Server\8.0\bin - 创建数据目录:
mkdir C:\data\db
mongod --dbpath C:\data\dbLinux (Ubuntu)
# 导入 MongoDB GPG 密钥
curl -fsSL https://www.mongodb.org/static/pgp/server-8.2.asc | sudo gpg -o /usr/share/keyrings/mongodb-server-8.2.gpg --dearmor
# 添加 MongoDB 仓库
echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-8.2.gpg ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/8.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-8.2.list
# 更新并安装
sudo apt update
sudo apt install -y mongodb-org
# 启动服务
sudo systemctl start mongod
sudo systemctl enable mongod
# 连接
mongoshDocker Compose 部署
services:
mongodb:
image: mongo:8.2.6
container_name: mongodb
restart: unless-stopped
ports:
- "27017:27017"
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: password123
volumes:
- mongodb_data:/data/db
healthcheck:
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
interval: 10s
timeout: 5s
retries: 5
volumes:
mongodb_data:启动:docker compose up -d
连接:mongosh mongodb://admin:password123@localhost:27017
基本操作
// 查看数据库
show dbs
// 使用数据库
use myapp
// 查看集合
show collections
// 基本 CRUD
db.users.insertOne({ name: "Alice", email: "[email protected]" })
db.users.find({ name: "Alice" })
db.users.updateOne({ _id: 1 }, { $set: { age: 30 } })
db.users.deleteOne({ _id: 1 })Schema 设计
原则
- 嵌入式文档 vs 引用
- 考虑查询模式设计
- 避免 unbounded 数组
嵌入式文档
适用于 1:1 或 1:few 关系:
// 好:嵌入式
{
_id: ObjectId("..."),
name: "Alice",
profile: {
bio: "Engineer",
location: "Beijing",
website: "https://alice.dev"
}
}
// 好:小型数组
{
name: "Order",
items: [
{ product: "Widget", quantity: 2 },
{ product: "Gadget", quantity: 1 }
]
}引用文档
适用于 1:many 或 many:many 关系:
// 用户集合
{
_id: ObjectId("..."),
name: "Alice",
email: "[email protected]"
}
// 订单集合(引用用户)
{
_id: ObjectId("..."),
user_id: ObjectId("..."), // 引用
items: [...],
total: 99.99
}Schema 验证
db.createCollection("users", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["name", "email"],
properties: {
name: {
bsonType: "string",
description: "must be a string"
},
email: {
bsonType: "string",
pattern: "@.+",
description: "must be a valid email"
},
age: {
bsonType: "int",
minimum: 0,
maximum: 150
}
}
}
}
})索引策略
创建索引
// 单字段索引
db.users.createIndex({ email: 1 }, { unique: true })
// 复合索引
db.orders.createIndex({ user_id: 1, created_at: -1 })
// 多键索引(数组字段)
db.products.createIndex({ tags: 1 })
// 文本索引
db.articles.createIndex({ title: "text", content: "text" })
// 地理空间索引
db.locations.createIndex({ coordinates: "2dsphere" })索引原则
| 原则 | 说明 |
|---|---|
| 选择性 | 基数高的字段 |
| 查询模式 | 匹配查询条件的字段顺序 |
| 避免过多样本 | 避免在小集合上建索引 |
| 考虑写开销 | 索引影响写入性能 |
索引类型
// 唯一索引
db.users.createIndex({ email: 1 }, { unique: true })
// 部分索引
db.logs.createIndex(
{ timestamp: 1 },
{ partialFilterExpression: { level: "error" } }
)
// 稀疏索引
db.sessions.createIndex(
{ token: 1 },
{ sparse: true }
)
// TTL 索引(自动删除)
db.logs.createIndex(
{ created_at: 1 },
{ expireAfterSeconds: 60 * 60 * 24 * 7 } // 7 天
)查看和删除索引
// 查看索引
db.users.getIndexes()
// 删除索引
db.users.dropIndex("email_1")
// 删除所有非 _id 索引
db.users.dropIndexes()聚合管道
基本结构
db.orders.aggregate([
{ $match: { status: "completed" } },
{ $group: { _id: "$user_id", total: { $sum: "$amount" } } },
{ $sort: { total: -1 } },
{ $limit: 10 }
])常用阶段
// $match - 过滤
{ $match: { status: "active", age: { $gte: 18 } } }
// $group - 分组
{ $group: {
_id: "$category",
count: { $sum: 1 },
avgPrice: { $avg: "$price" }
}
}
// $project - 选择字段
{ $project: { name: 1, email: 1, _id: 0 } }
// $sort - 排序
{ $sort: { created_at: -1 } }
// $limit - 限制数量
{ $limit: 100 }
// $skip - 跳过
{ $skip: 50 }
// $lookup - JOIN
{ $lookup: {
from: "users",
localField: "user_id",
foreignField: "_id",
as: "user"
}
}常见模式
// 用户订单统计
db.orders.aggregate([
{ $match: { user_id: ObjectId("...") } },
{ $group: {
_id: "$user_id",
orderCount: { $sum: 1 },
totalSpent: { $sum: "$amount" },
avgOrderValue: { $avg: "$amount" }
}
}
])
// 日期分组
{ $group: {
_id: {
year: { $year: "$created_at" },
month: { $month: "$created_at" }
},
count: { $sum: 1 }
}
}性能优化
Explain
// 分析查询
db.orders.find({ user_id: ObjectId("...") }).explain("executionStats")
// 关键指标
// - executionTimeMillis: 执行时间
// - totalDocsExamined: 检查的文档数
// - nReturned: 返回的文档数
覆盖查询
// 好:索引覆盖查询
db.users.createIndex({ email: 1, name: 1 })
db.users.find({ email: "[email protected]" }, { name: 1, _id: 0 })
// 查询计划使用索引
// executionStats.nReturned === totalDocsExamined
避免全集合扫描
// 不好:全集合扫描
db.users.find({ name: /.*alice.*/i })
// 好:使用索引
db.users.find({ email: "[email protected]" })
// 使用正则前缀
db.users.find({ name: /^alice/ }) // 可以使用索引
副本集
复制原理
- Primary: 接受写操作
- Secondary: 复制 Primary 的 oplog
- 投票节点: 参与选举
配置副本集
// 初始化副本集
rs.initiate({
_id: "myapp",
members: [
{ _id: 0, host: "localhost:27017" },
{ _id: 1, host: "localhost:27018" },
{ _id: 2, host: "localhost:27019", arbiterOnly: true }
]
})
// 状态检查
rs.status()
// 查看主节点
db.hello()分片
分片策略
// 哈希分片(均匀分布)
sh.shardCollection("myapp.orders", { _id: "hashed" })
// 范围分片(按范围分布)
sh.shardCollection("myapp.users", { user_id: 1 })分片注意事项
- 选择好的 shard key(高基数、避免单调递增)
- 使用
mongos路由 - 考虑
zone sharding按地区分布
安全
认证
// 创建用户管理员
use admin
db.createUser({
user: "admin",
pwd: "secure_password",
roles: [
{ role: "userAdminAnyDatabase", db: "admin" },
{ role: "readWriteAnyDatabase", db: "admin" }
]
})
// 连接时认证
mongosh -u admin -p --authenticationDatabase admin授权
// 为应用创建用户
use myapp
db.createUser({
user: "myapp_user",
pwd: "app_password",
roles: [
{ role: "readWrite", db: "myapp" }
]
})