1. 首页 > MongoDB教程 > 正文

MongoDB教程FG016-MongoDB执行计划与SQL调优实战

本文档风哥主要介绍MongoDB执行计划与SQL调优相关知识,包括MongoDB执行计划的概念、查询执行流程、执行计划阶段、规划、查询调优原则、性能指标、执行计划分析、查询优化、索引优化以及生产案例等内容,风哥教程参考MongoDB官方文档Execution Plans内容编写,适合DBA人员在学习和测试中使用,如果要应用于生产环境则需要自行确认。

Part01-基础概念与理论知识

1.1 MongoDB执行计划的概念

MongoDB执行计划是指MongoDB查询引擎为执行查询而生成的详细执行步骤和策略。执行计划包含了查询的各个阶段、使用的索引、扫描的文档数量、执行时间等信息,是分析和优化查询性能的重要工具。更多视频教程www.fgedu.net.cn

MongoDB执行计划的特点:

  • 提供查询执行的详细步骤
  • 显示使用的索引和扫描的文档数量
  • 帮助识别性能瓶颈
  • 支持不同级别的分析详细程度
  • 可以通过explain()方法获取

1.2 MongoDB查询执行流程

MongoDB查询执行流程主要包括以下步骤:

  1. 解析查询:解析查询语句,生成查询树
  2. 生成执行计划:根据查询树和可用索引生成执行计划
  3. 选择最佳执行计划:评估不同执行计划的成本,选择最佳计划
  4. 执行查询:按照执行计划执行查询操作
  5. 返回结果:将查询结果返回给客户端

1.3 MongoDB执行计划阶段

MongoDB执行计划包含多个执行阶段,主要包括:

# 1. 扫描阶段
– COLLSCAN:全集合扫描,不使用索引
– IXSCAN:索引扫描,使用索引
– FETCH:从集合中获取文档
– SHARD_MERGE:合并分片结果

# 2. 聚合阶段
– AGGREGATE:聚合操作
– GROUP:分组操作
– SORT:排序操作
– LIMIT:限制结果数量
– SKIP:跳过结果数量
– PROJECTION:投影操作

# 3. 其他阶段
– AND:逻辑与操作
– OR:逻辑或操作
– NOT:逻辑非操作
– TEXT:文本搜索
– GEO_NEAR:地理空间查询

风哥提示:MongoDB执行计划是分析查询性能的重要工具,通过分析执行计划可以识别查询瓶颈,优化查询性能。学习交流加群风哥微信: itpux-com

Part02-生产环境规划与建议

2.1 MongoDB执行计划规划

MongoDB执行计划规划要点:

# 1. 查询模式分析
– 识别常用查询
– 分析查询条件和排序需求
– 评估数据量和增长趋势

# 2. 索引策略规划
– 为常用查询创建合适的索引
– 考虑复合索引的顺序
– 避免过度索引

# 3. 执行计划分析
– 定期分析查询执行计划
– 识别性能瓶颈
– 优化查询语句和索引

# 4. 性能目标设定
– 设定查询响应时间目标
– 评估系统的查询吞吐量
– 制定监控和优化策略

# 5. 测试计划
– 制定查询性能测试计划
– 模拟真实负载测试
– 分析测试结果,优化系统

2.2 MongoDB查询调优原则

MongoDB查询调优原则:

  • 使用索引:为常用查询创建合适的索引
  • 减少扫描文档数:使用索引和合适的查询条件减少扫描的文档数
  • 限制返回字段:使用投影减少返回数据量
  • 限制结果数量:使用limit限制返回结果
  • 合理使用排序:为排序字段创建索引
  • 避免全集合扫描:确保查询使用索引
  • 使用聚合管道:对于复杂查询使用聚合
  • 监控查询性能:定期分析慢查询

2.3 MongoDB性能指标

MongoDB性能指标:

# 1. 查询性能指标
– executionTimeMillis:查询执行时间(毫秒)
– docsScanned:扫描的文档数
– keysExamined:检查的索引键数
– nReturned:返回的文档数
– totalKeysExamined:总共检查的索引键数
– totalDocsExamined:总共扫描的文档数

# 2. 系统性能指标
– connections:连接数
– mem.resident: resident内存使用量
– mem.virtual:虚拟内存使用量
– opcounters.query:查询操作计数
– opcounters.update:更新操作计数
– opcounters.insert:插入操作计数
– opcounters.delete:删除操作计数
– opcounters.getmore:getMore操作计数
– opcounters.command:命令操作计数

# 3. 存储性能指标
– wiredTiger.cache.maximum bytes configured:WiredTiger缓存大小
– wiredTiger.cache.bytes currently in the cache:当前缓存使用量
– wiredTiger.cache.tracked dirty bytes in the cache:缓存中的脏字节数
– wiredTiger.cache.pages read into cache:读入缓存的页数
– wiredTiger.cache.pages written from cache:从缓存写入的页数

生产环境建议:MongoDB执行计划规划应结合业务需求和数据模型,选择合适的查询策略和索引设计,确保查询性能满足业务要求。学习交流加群风哥QQ113257174

Part03-生产环境项目实施方案

3.1 MongoDB执行计划分析

3.1.1 使用explain()方法

# 基本语法
db.collection.find().explain()

# 示例:获取执行计划
fgedudb> db.fgedu_users.find({ age: { $gt: 25 } }).sort({ created_at: -1 }).explain()

# 示例:获取详细执行计划
fgedudb> db.fgedu_users.find({ age: { $gt: 25 } }).sort({ created_at: -1 }).explain(“executionStats”)

# 示例:获取所有执行计划
fgedudb> db.fgedu_users.find({ age: { $gt: 25 } }).sort({ created_at: -1 }).explain(“allPlansExecution”)

3.1.2 分析执行计划输出

# 执行计划输出示例
{
“queryPlanner”: {
“plannerVersion”: 1,
“namespace”: “fgedudb.fgedu_users”,
“indexFilterSet”: false,
“parsedQuery”: {
“age”: {
“$gt”: 25
}
},
“winningPlan”: {
“stage”: “SORT”,
“sortPattern”: {
“created_at”: -1
},
“inputStage”: {
“stage”: “FETCH”,
“inputStage”: {
“stage”: “IXSCAN”,
“keyPattern”: {
“age”: 1
},
“indexName”: “age_1”,
“isMultiKey”: false,
“multiKeyPaths”: {
“age”: []
},
“isUnique”: false,
“isSparse”: false,
“isPartial”: false,
“indexVersion”: 2,
“direction”: “forward”,
“indexBounds”: {
“age”: [
“(25, inf.)”
]
}
}
}
},
“rejectedPlans”: []
},
“executionStats”: {
“executionSuccess”: true,
“nReturned”: 2,
“executionTimeMillis”: 1,
“totalKeysExamined”: 2,
“totalDocsExamined”: 2,
“executionStages”: {
“stage”: “SORT”,
“nReturned”: 2,
“executionTimeMillisEstimate”: 0,
“works”: 3,
“advanced”: 2,
“needTime”: 0,
“needYield”: 0,
“saveState”: 0,
“restoreState”: 0,
“isEOF”: 1,
“sortPattern”: {
“created_at”: -1
},
“memLimit”: 33554432,
“type”: “memory”,
“totalDataSizeSorted”: 1024,
“usedDisk”: false,
“inputStage”: {
“stage”: “FETCH”,
“nReturned”: 2,
“executionTimeMillisEstimate”: 0,
“works”: 3,
“advanced”: 2,
“needTime”: 0,
“needYield”: 0,
“saveState”: 0,
“restoreState”: 0,
“isEOF”: 1,
“docsExamined”: 2,
“inputStage”: {
“stage”: “IXSCAN”,
“nReturned”: 2,
“executionTimeMillisEstimate”: 0,
“works”: 3,
“advanced”: 2,
“needTime”: 0,
“needYield”: 0,
“saveState”: 0,
“restoreState”: 0,
“isEOF”: 1,
“keyPattern”: {
“age”: 1
},
“indexName”: “age_1”,
“isMultiKey”: false,
“multiKeyPaths”: {
“age”: []
},
“isUnique”: false,
“isSparse”: false,
“isPartial”: false,
“indexVersion”: 2,
“direction”: “forward”,
“indexBounds”: {
“age”: [
“(25, inf.)”
]
},
“keysExamined”: 2,
“seeks”: 1
}
}
}
},
“serverInfo”: {
“host”: “fgedu.net.cn”,
“port”: 27017,
“version”: “4.4.10”,
“gitVersion”: “587e960b79c6953179b0f5a35305894544e1c05b”
},
“ok”: 1
}

3.2 MongoDB查询优化

3.2.1 查询语句优化

# 1. 优化查询条件
# 不推荐
db.fgedu_users.find({ $where: “this.age > 25” })
# 推荐
db.fgedu_users.find({ age: { $gt: 25 } })

# 2. 优化排序
# 不推荐
db.fgedu_users.find({ age: { $gt: 25 } }).sort({ name: 1 })
# 推荐
db.fgedu_users.createIndex({ age: 1, name: 1 })
db.fgedu_users.find({ age: { $gt: 25 } }).sort({ name: 1 })

# 3. 优化投影
# 不推荐
db.fgedu_users.find({ age: { $gt: 25 } })
# 推荐
db.fgedu_users.find({ age: { $gt: 25 } }, { name: 1, email: 1, _id: 0 })

# 4. 优化limit和skip
# 不推荐
db.fgedu_users.find().skip(10000).limit(10)
# 推荐
db.fgedu_users.find({ _id: { $gt: lastId } }).limit(10)

# 5. 优化in操作符
# 不推荐
db.fgedu_users.find({ age: { $in: [25, 26, 27, …, 100] } })
# 推荐
db.fgedu_users.find({ age: { $gte: 25, $lte: 100 } })

3.2.2 聚合查询优化

# 1. 使用索引加速聚合
fgedu_db> db.fgedu_orders.createIndex({ user_id: 1, status: 1 })
fgedu_db> db.fgedu_orders.aggregate([
{ $match: { user_id: ObjectId(“6614f8a0a1b2c3d4e5f6g7h8”), status: “completed” } },
{ $group: { _id: “$product_id”, total: { $sum: “$amount” } } }
])

# 2. 使用$match减少文档数
fgedu_db> db.fgedu_orders.aggregate([
{ $match: { status: “completed” } }, // 先过滤,减少文档数
{ $group: { _id: “$user_id”, total: { $sum: “$amount” } } },
{ $sort: { total: -1 } }
])

# 3. 使用$project减少字段数
fgedu_db> db.fgedu_orders.aggregate([
{ $match: { status: “completed” } },
{ $project: { user_id: 1, amount: 1, _id: 0 } }, // 减少字段数
{ $group: { _id: “$user_id”, total: { $sum: “$amount” } } }
])

# 4. 使用$sortWithCount优化排序
fgedu_db> db.fgedu_orders.aggregate([
{ $match: { status: “completed” } },
{ $group: { _id: “$user_id”, total: { $sum: “$amount” } } },
{ $sortWithCount: { total: -1 } }
])

3.3 MongoDB索引优化

3.3.1 索引设计优化

# 1. 选择合适的索引类型
# 单字段索引
db.fgedu_users.createIndex({ name: 1 })

# 复合索引
db.fgedu_orders.createIndex({ user_id: 1, created_at: -1 })

# 文本索引
db.fgedu_products.createIndex({ name: “text”, description: “text” })

# 地理空间索引
db.fgedu_locations.createIndex({ location: “2dsphere” })

# 2. 优化复合索引顺序
# 好的顺序:高基数字段在前
db.fgedu_users.createIndex({ email: 1, age: 1 })

# 差的顺序:低基数字段在前
db.fgedu_users.createIndex({ age: 1, email: 1 })

# 3. 使用部分索引
fgedu_db> db.fgedu_users.createIndex(
{ age: 1 },
{ partialFilterExpression: { age: { $gte: 18 } } }
)

# 4. 使用稀疏索引
fgedu_db> db.fgedu_users.createIndex({ email: 1 }, { sparse: true })

3.3.2 索引使用优化

# 1. 查看索引使用情况
fgedu_db> db.fgedu_users.aggregate([{ $indexStats: {} }])

# 2. 重建索引
fgedu_db> db.fgedu_users.reIndex()

# 3. 移除未使用的索引
fgedu_db> db.fgedu_users.dropIndex(“name_1”)

# 4. 监控索引性能
fgedu_db> db.fgedu_users.find({ name: “fgedu01” }).explain(“executionStats”)

# 5. 优化索引存储
fgedu_db> db.adminCommand({ compact: “fgedu_users” })

风哥提示:MongoDB索引优化是提高查询性能的关键,通过合理设计和使用索引,可以显著提升查询速度。更多学习教程公众号风哥教程itpux_com

Part04-生产案例与实战讲解

4.1 MongoDB执行计划分析案例

4.1.1 需求分析

需要分析一个查询的执行计划,识别性能瓶颈并进行优化。

4.1.2 解决方案

# 1. 执行查询并获取执行计划
fgedu_db> db.fgedu_orders.find({ user_id: ObjectId(“6614f8a0a1b2c3d4e5f6g7h8”), status: “completed” }).sort({ created_at: -1 }).explain(“executionStats”)

# 2. 分析执行计划输出
{
“executionStats”: {
“executionSuccess”: true,
“nReturned”: 5,
“executionTimeMillis”: 100,
“totalKeysExamined”: 1000,
“totalDocsExamined”: 1000,
“executionStages”: {
“stage”: “SORT”,
“nReturned”: 5,
“executionTimeMillisEstimate”: 80,
“works”: 1001,
“advanced”: 5,
“needTime”: 995,
“needYield”: 0,
“saveState”: 0,
“restoreState”: 0,
“isEOF”: 1,
“sortPattern”: {
“created_at”: -1
},
“memLimit”: 33554432,
“type”: “memory”,
“totalDataSizeSorted”: 20480,
“usedDisk”: false,
“inputStage”: {
“stage”: “FETCH”,
“nReturned”: 1000,
“executionTimeMillisEstimate”: 20,
“works”: 1001,
“advanced”: 1000,
“needTime”: 0,
“needYield”: 0,
“saveState”: 0,
“restoreState”: 0,
“isEOF”: 1,
“docsExamined”: 1000,
“inputStage”: {
“stage”: “COLLSCAN”,
“nReturned”: 1000,
“executionTimeMillisEstimate”: 10,
“works”: 1001,
“advanced”: 1000,
“needTime”: 0,
“needYield”: 0,
“saveState”: 0,
“restoreState”: 0,
“isEOF”: 1,
“filter”: {
“$and”: [
{ “user_id”: { “$eq”: ObjectId(“6614f8a0a1b2c3d4e5f6g7h8”) } },
{ “status”: { “$eq”: “completed” } }
]
},
“docsExamined”: 1000
}
}
}
}
}

# 3. 识别性能瓶颈
# 问题:
# – 执行时间100毫秒,较长
# – 扫描了1000个文档,只返回5个
# – 使用了全集合扫描(COLLSCAN),没有使用索引

# 4. 优化方案
# 创建复合索引
fgedu_db> db.fgedu_orders.createIndex({ user_id: 1, status: 1, created_at: -1 })

# 5. 再次执行查询并分析执行计划
fgedu_db> db.fgedu_orders.find({ user_id: ObjectId(“6614f8a0a1b2c3d4e5f6g7h8”), status: “completed” }).sort({ created_at: -1 }).explain(“executionStats”)

# 优化后执行计划
{
“executionStats”: {
“executionSuccess”: true,
“nReturned”: 5,
“executionTimeMillis”: 1,
“totalKeysExamined”: 5,
“totalDocsExamined”: 5,
“executionStages”: {
“stage”: “FETCH”,
“nReturned”: 5,
“executionTimeMillisEstimate”: 0,
“works”: 6,
“advanced”: 5,
“needTime”: 0,
“needYield”: 0,
“saveState”: 0,
“restoreState”: 0,
“isEOF”: 1,
“docsExamined”: 5,
“inputStage”: {
“stage”: “IXSCAN”,
“nReturned”: 5,
“executionTimeMillisEstimate”: 0,
“works”: 6,
“advanced”: 5,
“needTime”: 0,
“needYield”: 0,
“saveState”: 0,
“restoreState”: 0,
“isEOF”: 1,
“keyPattern”: {
“user_id”: 1,
“status”: 1,
“created_at”: -1
},
“indexName”: “user_id_1_status_1_created_at_-1”,
“isMultiKey”: false,
“multiKeyPaths”: {
“user_id”: [],
“status”: [],
“created_at”: []
},
“isUnique”: false,
“isSparse”: false,
“isPartial”: false,
“indexVersion”: 2,
“direction”: “forward”,
“indexBounds”: {
“user_id”: [
“[ObjectId(‘6614f8a0a1b2c3d4e5f6g7h8’), ObjectId(‘6614f8a0a1b2c3d4e5f6g7h8’)]”
],
“status”: [
“[“completed”, “completed”]”
],
“created_at”: [
“[MaxKey, MinKey]”
]
},
“keysExamined”: 5,
“seeks”: 1
}
}
}
}

# 6. 优化效果
# – 执行时间从100毫秒减少到1毫秒
# – 扫描的文档数从1000个减少到5个
# – 使用了索引扫描(IXSCAN),而不是全集合扫描

4.2 MongoDB查询调优案例

4.2.1 需求分析

需要优化一个电商系统的商品查询性能,主要查询场景包括按分类和价格范围查询。

4.2.2 解决方案

# 1. 分析查询性能
fgedu_db> db.fgedu_products.find({ category: “electronics”, price: { $gte: 100, $lte: 1000 } }).sort({ sales: -1 }).explain(“executionStats”)

# 2. 识别性能瓶颈
# 问题:
# – 执行时间较长
# – 扫描了大量文档
# – 排序操作使用了内存

# 3. 优化方案
# 创建复合索引
fgedu_db> db.fgedu_products.createIndex({ category: 1, price: 1, sales: -1 })

# 4. 优化查询语句
# 使用投影减少返回字段
fgedu_db> db.fgedu_products.find(
{ category: “electronics”, price: { $gte: 100, $lte: 1000 } },
{ name: 1, price: 1, sales: 1, _id: 0 }
).sort({ sales: -1 })

# 5. 再次分析查询性能
fgedu_db> db.fgedu_products.find(
{ category: “electronics”, price: { $gte: 100, $lte: 1000 } },
{ name: 1, price: 1, sales: 1, _id: 0 }
).sort({ sales: -1 }).explain(“executionStats”)

# 6. 优化效果
# – 执行时间显著减少
# – 扫描的文档数减少
# – 排序操作使用了索引,而不是内存

4.3 MongoDB索引优化案例

4.3.1 需求分析

需要优化一个社交媒体应用的数据库索引,提高查询性能。

4.3.2 解决方案

# 1. 分析查询模式
# 常用查询:
# – 按用户ID查询帖子:db.fgedu_posts.find({ user_id: ObjectId(“…”) }).sort({ created_at: -1 })
# – 按帖子ID查询评论:db.fgedu_comments.find({ post_id: ObjectId(“…”) }).sort({ created_at: -1 })
# – 按用户ID查询关注者:db.fgedu_followers.find({ user_id: ObjectId(“…”) })

# 2. 优化索引设计
# 创建复合索引
fgedu_db> db.fgedu_posts.createIndex({ user_id: 1, created_at: -1 })
fgedu_db> db.fgedu_comments.createIndex({ post_id: 1, created_at: -1 })
fgedu_db> db.fgedu_followers.createIndex({ user_id: 1, followed_id: 1 })

# 3. 查看索引使用情况
fgedu_db> db.fgedu_posts.aggregate([{ $indexStats: {} }])
fgedu_db> db.fgedu_comments.aggregate([{ $indexStats: {} }])
fgedu_db> db.fgedu_followers.aggregate([{ $indexStats: {} }])

# 4. 移除未使用的索引
fgedu_db> db.fgedu_posts.dropIndex(“created_at_-1”)
fgedu_db> db.fgedu_comments.dropIndex(“created_at_-1”)

# 5. 测试查询性能
fgedu_db> db.fgedu_posts.find({ user_id: ObjectId(“6614f8a0a1b2c3d4e5f6g7h8”) }).sort({ created_at: -1 }).explain(“executionStats”)
fgedu_db> db.fgedu_comments.find({ post_id: ObjectId(“6614f8a0a1b2c3d4e5f6g7h9”) }).sort({ created_at: -1 }).explain(“executionStats”)
fgedu_db> db.fgedu_followers.find({ user_id: ObjectId(“6614f8a0a1b2c3d4e5f6g7h8”) }).explain(“executionStats”)

# 6. 优化效果
# – 查询执行时间显著减少
# – 扫描的文档数减少
# – 索引使用效率提高

生产环境建议:MongoDB执行计划与SQL调优是一个持续的过程,需要根据业务需求和数据变化不断调整和改进,以确保系统的高效运行。from MongoDB视频:www.itpux.com

Part05-风哥经验总结与分享

5.1 MongoDB执行计划技巧

MongoDB执行计划技巧:

  • 使用explain()方法:定期使用explain()方法分析查询执行计划
  • 关注扫描文档数:扫描的文档数越多,查询性能越差
  • 关注执行时间:执行时间长的查询需要优化
  • 关注索引使用:确保查询使用了合适的索引
  • 分析执行阶段:识别查询的瓶颈阶段
  • 比较不同执行计划:使用”allPlansExecution”查看所有执行计划
  • 监控慢查询:启用慢查询日志,定期分析慢查询
  • 优化查询语句:根据执行计划优化查询语句

5.2 MongoDB查询调优技巧

MongoDB查询调优技巧:

  • 使用索引:为常用查询创建合适的索引
  • 减少扫描文档数:使用索引和合适的查询条件减少扫描的文档数
  • 限制返回字段:使用投影减少返回数据量
  • 限制结果数量:使用limit限制返回结果
  • 合理使用排序:为排序字段创建索引
  • 避免全集合扫描:确保查询使用索引
  • 使用聚合管道:对于复杂查询使用聚合
  • 优化in操作符:对于大范围的in查询,考虑使用范围查询

5.3 MongoDB性能监控

MongoDB性能监控建议:

  • 启用慢查询日志:配置慢查询阈值,记录慢查询
  • 使用数据库分析器:启用数据库分析器,收集查询统计信息
  • 监控查询执行时间:定期检查查询执行时间,识别性能瓶颈
  • 分析执行计划:使用explain()分析查询执行计划
  • 监控索引使用情况:检查索引使用情况,优化索引设计
  • 使用MongoDB Atlas:如果使用MongoDB Atlas,利用其监控工具
  • 设置查询告警:为慢查询设置告警,及时发现问题
  • 定期性能分析:定期进行性能分析,优化查询语句
风哥提示:MongoDB性能监控是确保系统高效运行的重要手段,通过定期监控和分析,可以及时发现和解决性能问题。更多视频教程www.fgedu.net.cn

持续改进:MongoDB执行计划与SQL调优是一个持续的过程,需要根据业务需求和数据变化不断调整和改进,以确保系统的高效运行。

本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html

联系我们

在线咨询:点击这里给我发消息

微信号:itpux-com

工作日:9:30-18:30,节假日休息