本文档风哥主要介绍MongoDB索引优化与查询性能调优相关知识,包括MongoDB索引的概念、索引类型、索引原理、索引规划、索引设计、索引最佳实践、实现方法、查询调优、索引管理以及生产案例等内容,风哥教程参考MongoDB官方文档索引和查询优化相关内容编写,适合DBA人员在学习和测试中使用,如果要应用于生产环境则需要自行确认。
Part01-基础概念与理论知识
1.1 MongoDB索引的概念
MongoDB索引是一种数据结构,用于提高查询性能。索引可以帮助MongoDB快速定位和访问数据,避免全集合扫描。索引类似于书籍的目录,通过索引可以快速找到需要的内容。更多视频教程www.fgedu.net.cn
- 提高查询性能:通过索引可以快速定位数据,避免全集合扫描
- 加速排序操作:索引可以加速排序操作
- 支持唯一性约束:可以通过唯一索引确保数据的唯一性
- 支持地理空间查询:通过地理空间索引支持地理空间查询
1.2 MongoDB索引类型
MongoDB支持多种索引类型,包括:
# 1. 单字段索引
– 基于单个字段创建的索引
– 适用于单个字段的查询
# 2. 复合索引
– 基于多个字段创建的索引
– 适用于多个字段的组合查询
– 索引顺序很重要
# 3. 唯一索引
– 确保索引字段的值唯一
– 可以基于单个字段或多个字段
# 4. 文本索引
– 用于全文搜索
– 支持文本查询操作符
# 5. 地理空间索引
– 用于地理空间查询
– 支持2d和2dsphere索引
# 6. 哈希索引
– 基于字段的哈希值创建索引
– 适用于基于哈希值的分布
# 7. 稀疏索引
– 只包含包含索引字段的文档
– 减少索引大小
# 8. 部分索引
– 基于过滤条件创建的索引
– 只包含满足条件的文档
# 9. TTL索引
– 自动过期并删除文档
– 适用于临时数据
1.3 MongoDB索引原理
MongoDB索引基于B树数据结构,B树是一种自平衡的搜索树,具有以下特点:
- 平衡结构:B树是平衡的,所有叶子节点都在同一层
- 多路搜索:每个节点可以有多个子节点,提高搜索效率
- 有序存储:数据按顺序存储,支持范围查询
- 磁盘友好:节点大小通常与磁盘块大小一致,减少磁盘I/O
MongoDB索引的工作原理:
- 当创建索引时,MongoDB会为索引字段创建B树结构
- 当执行查询时,MongoDB会使用索引快速定位数据
- 索引可以减少查询所需的磁盘I/O,提高查询性能
- 索引会占用额外的存储空间,需要权衡存储空间和查询性能
Part02-生产环境规划与建议
2.1 MongoDB索引规划
MongoDB索引规划要点:
# 1. 分析查询模式
– 分析应用的查询模式
– 确定频繁使用的查询条件
– 识别需要排序的字段
# 2. 选择索引字段
– 选择查询条件中频繁使用的字段
– 选择排序操作中使用的字段
– 选择唯一性约束需要的字段
# 3. 确定索引类型
– 根据查询模式选择合适的索引类型
– 单字段查询使用单字段索引
– 多字段查询使用复合索引
– 全文搜索使用文本索引
– 地理空间查询使用地理空间索引
# 4. 评估索引成本
– 存储空间成本:索引会占用额外的存储空间
– 写操作成本:每次写操作都需要更新索引
– 内存成本:索引需要加载到内存中以提高性能
# 5. 制定索引策略
– 平衡查询性能和写操作性能
– 避免创建过多索引
– 定期评估和优化索引
# 6. 索引命名规范
– 使用有意义的索引名称
– 遵循一致的命名约定
– 便于管理和维护
2.2 MongoDB索引设计
MongoDB索引设计要点:
- 复合索引顺序:将最常用的字段放在前面
- 选择性:选择高选择性的字段作为索引字段
- 覆盖查询:设计索引以覆盖常见查询
- 索引大小:考虑索引的大小,避免过大的索引
- 索引前缀:利用索引前缀优化查询
- 避免过度索引:只创建必要的索引
2.3 MongoDB索引最佳实践
MongoDB索引最佳实践:
- 只为必要的查询创建索引:避免创建过多索引
- 使用复合索引覆盖多个查询:合理设计复合索引
- 考虑索引顺序:将最常用的字段放在前面
- 使用覆盖索引:减少磁盘I/O
- 监控索引使用情况:定期分析索引使用情况
- 定期重建索引:保持索引的有效性
- 避免使用过多的单字段索引:优先使用复合索引
- 考虑数据分布:根据数据分布设计索引
Part03-生产环境项目实施方案
3.1 MongoDB索引实现
3.1.1 创建索引
# 1. 创建单字段索引
fgedudb> db.fgedu_users.createIndex({ name: 1 })
{ “createdCollectionAutomatically” : false, “numIndexesBefore” : 1, “numIndexesAfter” : 2, “ok” : 1 }
# 2. 创建复合索引
fgedudb> db.fgedu_users.createIndex({ name: 1, age: -1 })
{ “createdCollectionAutomatically” : false, “numIndexesBefore” : 2, “numIndexesAfter” : 3, “ok” : 1 }
# 3. 创建唯一索引
fgedudb> db.fgedu_users.createIndex({ email: 1 }, { unique: true })
{ “createdCollectionAutomatically” : false, “numIndexesBefore” : 3, “numIndexesAfter” : 4, “ok” : 1 }
# 4. 创建文本索引
fgedudb> db.fgedu_articles.createIndex({ content: “text” })
{ “createdCollectionAutomatically” : false, “numIndexesBefore” : 1, “numIndexesAfter” : 2, “ok” : 1 }
# 5. 创建地理空间索引
fgedudb> db.fgedu_locations.createIndex({ location: “2dsphere” })
{ “createdCollectionAutomatically” : false, “numIndexesBefore” : 1, “numIndexesAfter” : 2, “ok” : 1 }
# 6. 创建哈希索引
fgedudb> db.fgedu_users.createIndex({ _id: “hashed” })
{ “createdCollectionAutomatically” : false, “numIndexesBefore” : 4, “numIndexesAfter” : 5, “ok” : 1 }
# 7. 创建稀疏索引
fgedudb> db.fgedu_users.createIndex({ phone: 1 }, { sparse: true })
{ “createdCollectionAutomatically” : false, “numIndexesBefore” : 5, “numIndexesAfter” : 6, “ok” : 1 }
# 8. 创建部分索引
fgedudb> db.fgedu_users.createIndex({ age: 1 }, { partialFilterExpression: { age: { $gte: 18 } } })
{ “createdCollectionAutomatically” : false, “numIndexesBefore” : 6, “numIndexesAfter” : 7, “ok” : 1 }
# 9. 创建TTL索引
fgedudb> db.fgedu_sessions.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 })
{ “createdCollectionAutomatically” : false, “numIndexesBefore” : 1, “numIndexesAfter” : 2, “ok” : 1 }
3.1.2 查看索引
# 1. 查看集合的所有索引
fgedudb> db.fgedu_users.getIndexes()
[
{ “v” : 2, “key” : { “_id” : 1 }, “name” : “_id_” },
{ “v” : 2, “key” : { “name” : 1 }, “name” : “name_1” },
{ “v” : 2, “key” : { “name” : 1, “age” : -1 }, “name” : “name_1_age_-1” },
{ “v” : 2, “key” : { “email” : 1 }, “name” : “email_1”, “unique” : true },
{ “v” : 2, “key” : { “_id” : “hashed” }, “name” : “_id_hashed” },
{ “v” : 2, “key” : { “phone” : 1 }, “name” : “phone_1”, “sparse” : true },
{ “v” : 2, “key” : { “age” : 1 }, “name” : “age_1”, “partialFilterExpression” : { “age” : { “$gte” : 18 } } }
]
# 2. 查看索引大小
fgedudb> db.fgedu_users.totalIndexSize()
1048576
# 3. 查看集合的索引统计信息
fgedudb> db.fgedu_users.stats()
{
“ns” : “fgedudb.fgedu_users”,
“size” : 4096,
“count” : 10,
“avgObjSize” : 409.6,
“storageSize” : 8192,
“capped” : false,
“wiredTiger” : {…},
“nindexes” : 7,
“indexBuilds” : [],
“totalIndexSize” : 1048576,
“indexSizes” : {
“_id_” : 4096,
“name_1” : 4096,
“name_1_age_-1” : 4096,
“email_1” : 4096,
“_id_hashed” : 4096,
“phone_1” : 4096,
“age_1” : 4096
},
“ok” : 1
}
3.2 MongoDB查询调优
3.2.1 分析执行计划
# 1. 查看执行计划(默认模式)
fgedudb> db.fgedu_users.find({ name: “fgedu01”, age: { $gt: 20 } }).explain()
# 2. 查看执行计划(执行统计模式)
fgedudb> db.fgedu_users.find({ name: “fgedu01”, age: { $gt: 20 } }).explain(“executionStats”)
# 3. 查看执行计划(全量模式)
fgedudb> db.fgedu_users.find({ name: “fgedu01”, age: { $gt: 20 } }).explain(“allPlansExecution”)
# 4. 分析执行计划结果
{
“executionStats” : {
“executionSuccess” : true,
“nReturned” : 1,
“executionTimeMillis” : 1,
“totalKeysExamined” : 1,
“totalDocsExamined” : 1,
“executionStages” : {
“stage” : “FETCH”,
“nReturned” : 1,
“executionTimeMillisEstimate” : 0,
“works” : 2,
“advanced” : 1,
“needTime” : 0,
“needYield” : 0,
“saveState” : 0,
“restoreState” : 0,
“isEOF” : 1,
“docsExamined” : 1,
“alreadyHasObj” : 0,
“inputStage” : {
“stage” : “IXSCAN”,
“nReturned” : 1,
“executionTimeMillisEstimate” : 0,
“works” : 2,
“advanced” : 1,
“needTime” : 0,
“needYield” : 0,
“saveState” : 0,
“restoreState” : 0,
“isEOF” : 1,
“keyPattern” : { “name” : 1, “age” : -1 },
“indexName” : “name_1_age_-1”,
“isMultiKey” : false,
“multiKeyPaths” : { “name” : [], “age” : [] },
“isUnique” : false,
“isSparse” : false,
“isPartial” : false,
“indexVersion” : 2,
“direction” : “forward”,
“indexBounds” : {
“name” : [“[\”fgedu01\”, \”fgedu01\”]”],
“age” : [[“inf”, 20]]
},
“keysExamined” : 1,
“seeks” : 1,
“dupsTested” : 0,
“dupsDropped” : 0,
“seenInvalidated” : 0
}
}
}
}
3.2.2 优化查询
# 1. 使用索引覆盖查询
fgedudb> db.fgedu_users.find({ name: “fgedu01” }, { name: 1, age: 1, _id: 0 })
# 2. 避免全集合扫描
fgedudb> db.fgedu_users.find({ name: “fgedu01” })
# 使用索引
fgedudb> db.fgedu_users.find({ name: { $regex: “fgedu.*” } })
# 可能使用索引
fgedudb> db.fgedu_users.find({ name: { $regex: “.*fgedu” } })
# 不使用索引
# 3. 优化排序操作
fgedudb> db.fgedu_users.find().sort({ name: 1 })
# 使用索引
fgedudb> db.fgedu_users.find().sort({ name: 1, age: -1 })
# 使用复合索引
# 4. 优化聚合操作
fgedudb> db.fgedu_orders.aggregate([
{ $match: { status: “completed” } },
{ $group: { _id: “$user_id”, total: { $sum: “$amount” } } },
{ $sort: { total: -1 } }
])
# 5. 使用hint指定索引
fgedudb> db.fgedu_users.find({ name: “fgedu01”, age: { $gt: 20 } }).hint({ name: 1, age: -1 })
# 6. 限制返回字段
fgedudb> db.fgedu_users.find({ name: “fgedu01” }, { name: 1, email: 1, _id: 0 })
# 7. 限制结果数量
fgedudb> db.fgedu_users.find().limit(10)
3.3 MongoDB索引管理
3.3.1 管理索引
# 1. 删除索引
fgedudb> db.fgedu_users.dropIndex({ name: 1 })
{ “nIndexesWas” : 7, “ok” : 1 }
# 2. 删除所有索引(保留_id索引)
fgedudb> db.fgedu_users.dropIndexes()
{ “nIndexesWas” : 6, “ok” : 1 }
# 3. 重建索引
fgedudb> db.fgedu_users.reIndex()
{
“nIndexesWas” : 1,
“nIndexesAfter” : 1,
“indexes” : [
{ “key” : { “_id” : 1 }, “name” : “_id_” }
],
“ok” : 1
}
# 4. 监控索引使用情况
fgedudb> db.fgedu_users.aggregate([
{ $indexStats: {} }
])
# 5. 分析索引性能
fgedudb> db.runCommand({
analyze: “fgedu_users”,
keyPattern: { name: 1, age: -1 },
query: { name: “fgedu01”, age: { $gt: 20 } }
})
3.3.2 索引维护
# 1. 定期重建索引
#!/bin/bash
# index_maintenance.sh
# from:www.itpux.com.qq113257174.wx:itpux-com
# web: `http://www.fgedu.net.cn`
# 连接到MongoDB
mongosh –host 192.168.1.100 –port 27017 –username fgedu –password fgedu123 –authenticationDatabase admin << EOF
# 切换到目标数据库
use fgedudb
# 重建所有集合的索引
for (let collection of db.getCollectionNames()) {
print("Rebuilding indexes for collection: " + collection);
db[collection].reIndex();
}
# 查看索引状态
db.fgedu_users.getIndexes();
EOF
# 2. 监控索引大小
fgedudb> db.fgedu_users.totalIndexSize()
# 3. 分析索引使用情况
fgedudb> db.fgedu_users.aggregate([
{ $indexStats: {} }
])
# 4. 优化索引
fgedudb> db.runCommand({
collStats: “fgedu_users”,
scale: 1024
})
Part04-生产案例与实战讲解
4.1 MongoDB索引案例一:创建索引
4.1.1 需求分析
需要为MongoDB集合创建合适的索引,以提高查询性能。
4.1.2 解决方案
# 1. 环境准备
# MongoDB版本:4.4+
# 集合:fgedu_users,包含name、age、email、phone等字段
# 2. 步骤一:分析查询模式
# 常见查询:
# 1. 根据name查询用户
# 2. 根据name和age查询用户
# 3. 根据email查询用户(需要唯一性约束)
# 4. 根据phone查询用户(部分用户有phone字段)
# 3. 步骤二:创建索引
# 创建单字段索引
fgedudb> db.fgedu_users.createIndex({ name: 1 })
{ “createdCollectionAutomatically” : false, “numIndexesBefore” : 1, “numIndexesAfter” : 2, “ok” : 1 }
# 创建复合索引
fgedudb> db.fgedu_users.createIndex({ name: 1, age: -1 })
{ “createdCollectionAutomatically” : false, “numIndexesBefore” : 2, “numIndexesAfter” : 3, “ok” : 1 }
# 创建唯一索引
fgedudb> db.fgedu_users.createIndex({ email: 1 }, { unique: true })
{ “createdCollectionAutomatically” : false, “numIndexesBefore” : 3, “numIndexesAfter” : 4, “ok” : 1 }
# 创建稀疏索引
fgedudb> db.fgedu_users.createIndex({ phone: 1 }, { sparse: true })
{ “createdCollectionAutomatically” : false, “numIndexesBefore” : 4, “numIndexesAfter” : 5, “ok” : 1 }
# 4. 步骤三:验证索引
fgedudb> db.fgedu_users.getIndexes()
[
{ “v” : 2, “key” : { “_id” : 1 }, “name” : “_id_” },
{ “v” : 2, “key” : { “name” : 1 }, “name” : “name_1” },
{ “v” : 2, “key” : { “name” : 1, “age” : -1 }, “name” : “name_1_age_-1” },
{ “v” : 2, “key” : { “email” : 1 }, “name” : “email_1”, “unique” : true },
{ “v” : 2, “key” : { “phone” : 1 }, “name” : “phone_1”, “sparse” : true }
]
# 5. 步骤四:测试查询性能
# 测试name查询
fgedudb> db.fgedu_users.find({ name: “fgedu01” }).explain(“executionStats”)
# 测试name和age查询
fgedudb> db.fgedu_users.find({ name: “fgedu01”, age: { $gt: 20 } }).explain(“executionStats”)
# 测试email查询
fgedudb> db.fgedu_users.find({ email: “fgedu01@fgedu.net.cn” }).explain(“executionStats”)
# 测试phone查询
fgedudb> db.fgedu_users.find({ phone: “13800138000” }).explain(“executionStats”)
4.2 MongoDB索引案例二:查询优化
4.2.1 需求分析
需要优化MongoDB查询,提高查询性能。
4.2.2 解决方案
# 1. 环境准备
# MongoDB版本:4.4+
# 集合:fgedu_orders,包含user_id、status、amount、created_at等字段
# 2. 步骤一:分析查询性能
# 查看查询执行计划
fgedudb> db.fgedu_orders.find({ user_id: ObjectId(“6614f8a0a1b2c3d4e5f6g7h8”), status: “completed” }).sort({ created_at: -1 }).explain(“executionStats”)
# 3. 步骤二:创建合适的索引
# 创建复合索引
fgedudb> db.fgedu_orders.createIndex({ user_id: 1, status: 1, created_at: -1 })
{ “createdCollectionAutomatically” : false, “numIndexesBefore” : 1, “numIndexesAfter” : 2, “ok” : 1 }
# 4. 步骤三:测试查询性能
# 再次查看查询执行计划
fgedudb> db.fgedu_orders.find({ user_id: ObjectId(“6614f8a0a1b2c3d4e5f6g7h8”), status: “completed” }).sort({ created_at: -1 }).explain(“executionStats”)
# 5. 步骤四:优化聚合查询
# 查看聚合查询执行计划
fgedudb> db.fgedu_orders.aggregate([
{ $match: { status: “completed” } },
{ $group: { _id: “$user_id”, total: { $sum: “$amount” } } },
{ $sort: { total: -1 } }
]).explain(“executionStats”)
# 创建索引
fgedudb> db.fgedu_orders.createIndex({ status: 1 })
{ “createdCollectionAutomatically” : false, “numIndexesBefore” : 2, “numIndexesAfter” : 3, “ok” : 1 }
# 再次测试聚合查询
fgedudb> db.fgedu_orders.aggregate([
{ $match: { status: “completed” } },
{ $group: { _id: “$user_id”, total: { $sum: “$amount” } } },
{ $sort: { total: -1 } }
]).explain(“executionStats”)
4.3 MongoDB索引案例三:性能分析
4.3.1 需求分析
需要分析MongoDB索引性能,找出优化空间。
4.3.2 解决方案
# 1. 环境准备
# MongoDB版本:4.4+
# 集合:fgedu_users,包含多个索引
# 2. 步骤一:分析索引使用情况
fgedudb> db.fgedu_users.aggregate([
{ $indexStats: {} }
])
# 3. 步骤二:分析集合统计信息
fgedudb> db.fgedu_users.stats()
# 4. 步骤三:分析查询性能
# 查看慢查询日志
$ mongod –logpath /mongodb/log/mongod.log –slowms 100
# 分析慢查询
fgedudb> db.setProfilingLevel(1, { slowms: 100 })
fgedudb> db.system.profile.find({ millis: { $gt: 100 } }).sort({ ts: -1 })
# 5. 步骤四:优化索引
# 删除未使用的索引
fgedudb> db.fgedu_users.dropIndex({ phone: 1 })
# 重建索引
fgedudb> db.fgedu_users.reIndex()
# 6. 步骤五:验证优化效果
# 再次分析索引使用情况
fgedudb> db.fgedu_users.aggregate([
{ $indexStats: {} }
])
# 测试查询性能
fgedudb> db.fgedu_users.find({ name: “fgedu01”, age: { $gt: 20 } }).explain(“executionStats”)
Part05-风哥经验总结与分享
5.1 MongoDB索引技巧
MongoDB索引技巧:
- 选择合适的索引类型:根据查询模式选择合适的索引类型
- 合理设计复合索引:将最常用的字段放在前面
- 使用覆盖索引:减少磁盘I/O
- 避免过度索引:只创建必要的索引
- 定期重建索引:保持索引的有效性
- 监控索引使用情况:及时发现未使用的索引
- 考虑数据分布:根据数据分布设计索引
- 使用部分索引:减少索引大小
- 使用稀疏索引:减少索引大小
- 使用TTL索引:自动清理过期数据
5.2 MongoDB性能调优
MongoDB性能调优建议:
- 优化查询:使用索引,限制返回字段,限制结果数量
- 优化聚合操作:使用管道操作,避免使用map-reduce
- 优化写操作:使用批量操作,调整写关注级别
- 优化连接:使用连接池,减少连接开销
- 优化存储:使用WiredTiger存储引擎,调整缓存大小
- 优化硬件:使用SSD,增加内存,提高网络带宽
- 监控性能:使用监控工具,及时发现性能问题
- 定期维护:定期备份,定期重建索引,定期清理数据
5.3 MongoDB索引故障排查
MongoDB索引故障排查建议:
- 查看索引状态:使用db.collection.getIndexes()查看索引状态
- 分析执行计划:使用explain()分析查询执行计划
- 查看索引使用情况:使用$indexStats聚合查看索引使用情况
- 检查索引大小:使用db.collection.totalIndexSize()检查索引大小
- 重建索引:如果索引损坏,使用reIndex()重建索引
- 删除未使用的索引:删除未使用的索引,减少存储开销
- 检查磁盘空间:确保有足够的磁盘空间存储索引
- 检查内存使用:确保有足够的内存加载索引
本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html
