最佳实践与常见问题排查
最佳实践
索引优化
在MongoDB中,合理的索引设计能显著提升查询性能。复合索引的顺序遵循ESR原则(Equality, Sort, Range):
- 等值查询字段放最前面
- 排序字段放在中间
- 范围查询字段放最后面
// 好的索引示例
db.orders.createIndex({
userId: 1, // Equality
createDate: -1, // Sort
amount: 1 // Range
})
避免过度索引,每个额外索引都会增加写入时的开销。使用explain()
分析查询执行计划:
db.orders.find({
userId: "user123",
createDate: { $gt: ISODate("2023-01-01") }
}).sort({ amount: 1 }).explain("executionStats")
文档设计
根据查询模式选择嵌入或引用:
- 一对少关系且频繁一起查询的数据适合嵌入
- 一对多关系或独立访问的数据适合引用
// 嵌入文档示例(适合博客文章和评论)
{
_id: "post1",
title: "MongoDB指南",
comments: [
{ user: "Alice", text: "好文章", date: ISODate(...) },
{ user: "Bob", text: "很有帮助", date: ISODate(...) }
]
}
// 引用示例(适合用户和订单)
// users集合
{ _id: "user1", name: "张三" }
// orders集合
{ _id: "order1", userId: "user1", items: [...] }
写入优化
批量操作使用bulkWrite()
替代单条写入:
const bulkOps = [
{ insertOne: { document: { name: "产品A", price: 100 } } },
{ updateOne: {
filter: { name: "产品B" },
update: { $set: { price: 200 } }
}},
{ deleteOne: { filter: { name: "产品C" } }}
];
db.products.bulkWrite(bulkOps);
设置合理的写关注级别(write concern),平衡数据安全性和性能:
// 多数节点确认写入
db.products.insertOne(
{ name: "新品", stock: 50 },
{ writeConcern: { w: "majority", j: true } }
)
常见问题排查
性能问题
慢查询通常由以下原因引起:
- 缺失索引或索引不当
- 全集合扫描
- 内存排序(无法使用索引的排序)
使用db.currentOp()
查看正在执行的操作:
// 查看执行时间超过3秒的操作
db.currentOp({
"active": true,
"secs_running": { "$gt": 3 }
})
通过mongotop
和mongostat
监控数据库活动:
mongotop --host localhost:27017
mongostat --host localhost:27017 --rowcount 5
连接问题
连接池耗尽表现为应用无法获取新连接。调整连接池大小:
// Node.js驱动示例
const client = new MongoClient(uri, {
poolSize: 50, // 连接池大小
connectTimeoutMS: 30000, // 连接超时
socketTimeoutMS: 60000 // 套接字超时
});
检查连接状态:
db.serverStatus().connections
// 输出示例
{
"current" : 25,
"available" : 475,
"totalCreated" : 42
}
内存使用
MongoDB倾向于使用所有可用内存作为缓存。监控内存使用:
db.serverStatus().mem
// 典型输出
{
"bits" : 64,
"resident" : 1024, // 常驻内存(MB)
"virtual" : 2048, // 虚拟内存(MB)
"supported" : true
}
当出现内存压力时:
- 检查工作集是否超过物理内存
- 添加更多RAM
- 优化查询减少内存使用
复制集问题
常见复制延迟原因:
- 网络带宽不足
- 从节点配置较低
- 主节点写入负载过高
检查复制状态:
rs.printReplicationInfo()
rs.printSecondaryReplicationInfo()
// 详细状态
rs.status()
处理复制延迟:
- 增加
oplog
大小 - 提升从节点配置
- 限制主节点写入速率
分片集群问题
分片集群常见问题包括:
- 数据分布不均
- 查询未包含分片键
- 配置服务器负载高
检查数据分布:
db.collection.getShardDistribution()
均衡器状态检查:
sh.isBalancerRunning()
sh.getBalancerState()
分片键选择原则:
- 基数高(大量不同值)
- 写分布均匀
- 匹配查询模式
锁竞争
MongoDB使用多粒度锁。查看锁状态:
db.currentOp({ "waitingForLock": true })
db.serverStatus().locks
减少锁竞争的方法:
- 缩短事务持续时间
- 避免全集合扫描
- 使用适当的写关注级别
事务处理
多文档事务注意事项:
- 事务最大运行时间60秒
- 事务中操作总数限制
- 避免在事务中创建集合
事务示例:
const session = client.startSession();
try {
session.startTransaction({
readConcern: { level: "snapshot" },
writeConcern: { w: "majority" }
});
await accounts.updateOne(
{ _id: "A" }, { $inc: { balance: -100 } },
{ session }
);
await accounts.updateOne(
{ _id: "B" }, { $inc: { balance: 100 } },
{ session }
);
await session.commitTransaction();
} catch (error) {
await session.abortTransaction();
throw error;
} finally {
session.endSession();
}
存储引擎问题
WiredTiger引擎调优:
// 查看存储引擎状态
db.serverStatus().wiredTiger
// 调整缓存大小(在配置文件中)
storage:
wiredTiger:
engineConfig:
cacheSizeGB: 2
压缩配置:
storage:
wiredTiger:
collectionConfig:
blockCompressor: zlib
indexConfig:
prefixCompression: true
查询优化器
MongoDB查询优化器可能选择非最优执行计划。强制使用特定索引:
db.orders.find({ userId: "123", status: "A" })
.hint({ userId: 1, status: 1 })
重建索引:
db.orders.reIndex()
日期处理
日期查询常见问题包括时区处理和格式转换:
// 查询特定日期的文档(UTC时间)
db.events.find({
date: {
$gte: ISODate("2023-01-01T00:00:00Z"),
$lt: ISODate("2023-01-02T00:00:00Z")
}
})
// 使用聚合转换时区
db.events.aggregate([
{
$project: {
localDate: {
$dateToString: {
format: "%Y-%m-%d %H:%M",
date: "$date",
timezone: "+08:00"
}
}
}
}
])
数组操作
数组查询和更新常见问题:
// 查询包含特定元素的数组
db.products.find({ tags: "electronics" })
// 更新数组中的特定元素
db.products.updateOne(
{ _id: 1, "variants.id": "v1" },
{ $set: { "variants.$.price": 19.99 } }
)
// 使用$elemMatch查询多个条件
db.survey.find({
results: {
$elemMatch: {
product: "xyz",
score: { $gte: 8 }
}
}
})
安全配置
常见安全配置问题:
- 未启用认证
- 使用弱密码
- 网络暴露
基本安全配置:
security:
authorization: enabled
keyFile: /path/to/keyfile
net:
bindIp: 127.0.0.1,192.168.1.100
port: 27017
创建用户:
db.createUser({
user: "admin",
pwd: passwordPrompt(), // 或明文密码
roles: [
{ role: "userAdminAnyDatabase", db: "admin" },
{ role: "readWriteAnyDatabase", db: "admin" }
]
})
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn