索引及索引限制
一.使用索引
1.单一字段(键)索引
语法:db.collection_name.createIndex({:})
> use test
switched to db test
> db.books.find()
{ "_id" : ObjectId("5e0cb711eb18ea3208bc19a8"), "name" : "C语言编程", "price" : 32
}
{ "_id" : ObjectId("5e0cb746eb18ea3208bc19a9"), "name" : "Python入门", "price" :
48 }
{ "_id" : ObjectId("5e0cb8e0eb18ea3208bc19aa"), "name" : "Java学习", "price" : 65
}
{ "_id" : ObjectId("5e0cbbd2eb18ea3208bc19b7"), "name" : "小学生教材", "price" :
20 }
{ "_id" : ObjectId("5e0cbbd2eb18ea3208bc19b8"), "name" : "初中生教材", "price" :
30 }
{ "_id" : ObjectId("5e0cbbd2eb18ea3208bc19b9"), "name" : "高中生教材", "price" :
40 }
{ "_id" : ObjectId("5e0cbc16eb18ea3208bc19ba"), "name" : "英文教材", "price" : 49
}
> db.books.createIndex({name:1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
> db.books.find({name:{$regex:/教材$/}})
{ "_id" : ObjectId("5e0cbbd2eb18ea3208bc19b8"), "name" : "初中生教材", "price" :
30 }
{ "_id" : ObjectId("5e0cbbd2eb18ea3208bc19b7"), "name" : "小学生教材", "price" :
20 }
{ "_id" : ObjectId("5e0cbc16eb18ea3208bc19ba"), "name" : "英文教材", "price" : 49
}
{ "_id" : ObjectId("5e0cbbd2eb18ea3208bc19b9"), "name" : "高中生教材", "price" :
40 }
> db.books.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test.books"
},
{
"v" : 2,
"key" : {
"name" : 1
},
"name" : "name_1",
"ns" : "test.books"
}
]
2.字段值唯一索引
语法:db.collection_name.createIndex({ : },{unique:true})
> db.books.createIndex({price:1},{unique:true})
{
"createdCollectionAutomatically" : true,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
3.多字段索引
语法:db.collection_name.createIndex({ : , : })
db.foo.insert( [ {name:"《a cat 故事》",price:20,color:"red"}, {name:"《crying brids故事》",price:20,color:"green"}, {name:"《big Dogs故事》",price:25,color:"blue"} ] )
BulkWriteResult({
"writeErrors" : [ ],
"writeConcernErrors" : [ ],
"nInserted" : 3,
"nUpserted" : 0,
"nMatched" : 0,
"nModified" : 0,
"nRemoved" : 0,
"upserted" : [ ]
})
> db.foo.find()
{ "_id" : ObjectId("6274a6327ba4c1a412060b0f"), "x" : 1, "y" : 1 }
{ "_id" : ObjectId("6274b5d1f81e82b239821b79"), "x" : 1, "y" : 1 }
{ "_id" : ObjectId("6274bb6ca530d1d413596095"), "x" : 1, "y" : 1 }
{ "_id" : ObjectId("6279adc1e36cad4f83df195e"), "x" : 1, "y" : 1 }
{ "_id" : ObjectId("627afe03dc33bf23d007892c"), "name" : "《a cat 故事》", "price" : 20, "color" : "red" }
{ "_id" : ObjectId("627afe03dc33bf23d007892d"), "name" : "《crying brids故事》", "price" : 20, "color" : "green" }
{ "_id" : ObjectId("627afe03dc33bf23d007892e"), "name" : "《big Dogs故事》", "price" : 25, "color" : "blue" }
> db.foo.createIndex(
... {price:1,color:-1}
... )
{
"numIndexesBefore" : 2,
"numIndexesAfter" : 3,
"createdCollectionAutomatically" : false,
"ok" : 1
}
#使用find()查找文档记录,然后对结果用sort派粗查询,先对price做升序,在price价格一样的情况下再做color降序排序
> db.foo.find({},{_id:0}).sort({price:1,color:-1})
{ "x" : 1, "y" : 1 }
{ "x" : 1, "y" : 1 }
{ "x" : 1, "y" : 1 }
{ "x" : 1, "y" : 1 }
{ "name" : "《a cat 故事》", "price" : 20, "color" : "red" }
{ "name" : "《crying brids故事》", "price" : 20, "color" : "green" }
{ "name" : "《big Dogs故事》", "price" : 25, "color" : "blue" }
#用sort组price降序排序,color升序排序
> db.foo.find({},{_id:0}).sort({price:-1,color:1})
{ "name" : "《big Dogs故事》", "price" : 25, "color" : "blue" }
{ "name" : "《crying brids故事》", "price" : 20, "color" : "green" }
{ "name" : "《a cat 故事》", "price" : 20, "color" : "red" }
{ "x" : 1, "y" : 1 }
{ "x" : 1, "y" : 1 }
{ "x" : 1, "y" : 1 }
{ "x" : 1, "y" : 1 }
#用sort组price升序排序,color升序排序
> db.foo.find({},{_id:0}).sort({price:1,color:1})
{ "x" : 1, "y" : 1 }
{ "x" : 1, "y" : 1 }
{ "x" : 1, "y" : 1 }
{ "x" : 1, "y" : 1 }
{ "name" : "《crying brids故事》", "price" : 20, "color" : "green" }
{ "name" : "《a cat 故事》", "price" : 20, "color" : "red" }
{ "name" : "《big Dogs故事》", "price" : 25, "color" : "blue" }
多字段唯一索引
> db.foo.createIndex({name:1,price:1},{unique:true})
{
"ok" : 0,
"errmsg" : "Index build failed: 7617cb93-a4d4-42ea-822f-1a935675ba9b: Collection test.foo ( 8f40f73c-3789-4bbb-b3d7-d539c1b586de ) :: caused by :: E11000 duplicate key error collection: test.foo index: name_1_price_1 dup key: { name: null, price: null }",
"code" : 11000,
"codeName" : "DuplicateKey",
"keyPattern" : {
"name" : 1,
"price" : 1
},
"keyValue" : {
"name" : null,
"price" : null
}
}
4.文本索引
语法:db.collection_name.createIndex({:“text”,:“text”,…})
> db.books.createIndex({name:"text"})
{
"numIndexesBefore" : 3,
"numIndexesAfter" : 4,
"createdCollectionAutomatically" : false,
"ok" : 1
}
**通配符文本索引
> db.books.createIndex({"$**":"text"})
{
"ok" : 0,
"errmsg" : "An equivalent index already exists with a different name and options. Requested index: { v: 2, key: { _fts: \"text\", _ftsx: 1 }, name: \"$**_text\", weights: { $**: 1 }, default_language: \"english\", language_override: \"language\", textIndexVersion: 3 }, existing index: { v: 2, key: { _fts: \"text\", _ftsx: 1 }, name: \"name_text\", weights: { name: 1 }, default_language: \"english\", language_override: \"language\", textIndexVersion: 3 }",
"code" : 85,
"codeName" : "IndexOptionsConflict"
}
5.哈希索引
语法:db.collection_name.createIndex({key:“hashed”})
> db.foo.createIndex({_id:"hashed"})
{
"numIndexesBefore" : 3,
"numIndexesAfter" : 3,
"note" : "all indexes already exist",
"ok" : 1
}
6.ensureIndex()索引
语法:db.collection_name.ensureIndex({:n,:n,…},option)
早期的MongoDB索引命令 MongoDB 3.0开始用createIndex命令代替ensureIndex。已经过期被淘汰的
7.与索引相关的其他方法
db.collection.dropIndex(index):移除集合指定的索引功能。index参数为指定需要删除的集合索 引名,可用getIndexes()函数获取集合的所有索引名称。
db.collection.dropIndexes():移除一个集合的所有索引功能。
db.colleciton.getIndexes():返回一个指定集合的现有索引描述信息的文档数组。
db.collection.reIndex():删除指定集合上所有索引,并重新构建所有现有索引。在具有大量数据 集合的情况下,该操作将大量消耗服务器的运行资源,引起运行性能急剧下降等问题的发生。
db.collection.totalIndexSize():提供指定集合索引大小的报告信息。
二.高级索引
1.子文档索引
语法:db.collection_name.createIndex({:,:,…})
> use eshops
switched to db eshops
> db.books.insert(
... [
... {name:"《生活百科故事1》",price:50,summary:{kind:"学前",content:"1-7岁用"}},
... {name:"《生活百科故事2》",price:50,summary:{kind:"少儿",content:"8-16岁用"}}
... ])
BulkWriteResult({
"writeErrors" : [ ],
"writeConcernErrors" : [ ],
"nInserted" : 2,
"nUpserted" : 0,
"nMatched" : 0,
"nModified" : 0,
"nRemoved" : 0,
"upserted" : [ ]
})
#对一个子文档的两个键的值进行索引。1位升序,-1为降序。
> db.books.createIndex( { "summary.kind":1,"summary.content":-1 } )
{
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"createdCollectionAutomatically" : false,
"ok" : 1
}
> db.books.find({"summary.kind":"少儿"}).pretty()
{
"_id" : ObjectId("627b05fcdc33bf23d0078930"),
"name" : "《生活百科故事2》",
"price" : 50,
"summary" : {
"kind" : "少儿",
"content" : "8-16岁用"
}
}
2.数组索引
语法:db.collection_name.createIndex({:,:,…})
#插入两个带数组的文档
> db.books.insert(
... [
... {
... name:"《e故事》",
... price:30,
... tags:[{no:1,press:"x出版社"},{no:2,press:"y出版社"},{no:3,press:"z出版社"}]
... },
... {
... name:"《f故事》",
... price:30,
... tags:[{no:11,press:"x出版社"},{no:4,press:"y出版社"},{no:2,press:"z出版社"}]
... }
... ]
)
#对一个数组的两个键的值进行索引。1位升序、-1位降序
> db.books.createIndex(
... { "tags.no":1,"tags.press":-1}
... )
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 2,
"numIndexesAfter" : 3,
"ok" : 1
}
> db.books.find({"tags.no":2}).pretty()
{
"_id" : ObjectId("5e2ff4989581899dc57c2981"),
"name" : "《e故事》",
"price" : 30,
"tags" : [
{
"no" : 1,
"press" : "x出版社"
},
{
"no" : 2,
"press" : "y出版社"
},
{
"no" : 3,
"press" : "z出版社"
}
]
}
{
"_id" : ObjectId("5e2ff4989581899dc57c2982"),
"name" : "《f故事》",
"price" : 30,
"tags" : [
{
"no" : 11,
"press" : "x出版社"
},
{
"no" : 4,
"press" : "y出版社"
},
{
"no" : 2,
"press" : "z出版社"
}
]
}
3.2dsphere(地理空间索引)
语法:db.collection_name.createIndex({:“2dsphere”})
#插入一条地理空间属性文档数据
> db.places.insert(
... {
... location:{type:"Point",coordinates:[ -29.32, 50.11 ]},
... name: "北海公园",
... category:"公园"
... }
... )
WriteResult({ "nInserted" : 1 })
#建立2dsphere索引
> db.places.createIndex({location:"2dsphere"})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
> db.places.find( { location: { $near:{$geometry:{type: "Point",coordinates:[-29,49]},$maxDistance:200000} } } ).pretty()
{
"_id" : ObjectId("5e2ff7f29581899dc57c2983"),
"location" : {
"type" : "Point",
"coordinates" : [
-29.32,
50.11
]
},
"name" : "北海公园",
"category" : "公园"
}
三.索引限制
1.索引额外开销
建立一个索引至少需要8KB的数据存储空间,也就是索引是需要消耗内存和磁盘的存储空间的。 另外,对集合做插入、更新和删除时,若相关字段建立了索引,同步也会引起对索引内容的更新操作 (锁独占排他性操作),这个过程是影响数据库的读写性能的,有时甚至会比较严重。所以,如果业务 系统所使用的集合很少对集合进行读取操作,则建议不使用索引。
2.内存使用限制
索引在使用时,是驻内存中持续运行的,所以索引大小不能超过内存的限制。MongoDB在索引大小超 过内存限制范围后,将会删除一些索引,这将导致系统运行性能下降。索引占用空间大小,可以通过 db.collection_name.totalIndexSize()方法来获取。
3.查询限制
索引不能被以下的查询使用:
正则表达式及非操作符,如 n i n 、 nin、 nin、not等
算术运算符,如$mod等
$where子句
说明:
查询语句能否使用索引功能,可以用find()的explain()来查看
对重要的集合进行索引查询操作,使用前建议进行严格的模拟测试
4.索引最大范围 集
集合中的索引不能超过64个;索引名的长度不能超过125个字符;一个多值索引最多可以有31个字段。 如果现有的索引字段的值超过索引键的限制,MongoDB中不会创建索引。
5.不应该使用索引场景
使用索引是否合适,主要看查询操作的使用场景,预先进行模拟测试非常重要。
如果查询要返回的结果超过集合文档记录的1/3,那么是否建立索引,需要慎 重考虑;
对于以写为主的集合,建议慎用索引,默认情况下_id够用了。
查询高级分析
Explain()分析
1.Explain()命令格式
语法:db.Collection.Command().explain(modes)
2.Explain()执行返回结果及分析
> use test
switched to db test
> db.books.find().explain("executionStats")
{
"explainVersion" : "1",
"queryPlanner" : {
"namespace" : "test.books",
"indexFilterSet" : false,
"parsedQuery" : {
},
"maxIndexedOrSolutionsReached" : false,
"maxIndexedAndSolutionsReached" : false,
"maxScansToExplodeReached" : false,
"winningPlan" : {
"stage" : "COLLSCAN",
"direction" : "forward"
},
"rejectedPlans" : [ ]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 7,
"executionTimeMillis" : 1,
"totalKeysExamined" : 0,
"totalDocsExamined" : 7,
"executionStages" : {
"stage" : "COLLSCAN",
"nReturned" : 7,
"executionTimeMillisEstimate" : 0,
"works" : 9,
"advanced" : 7,
"needTime" : 1,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"direction" : "forward",
"docsExamined" : 7
}
},
"command" : {
"find" : "books",
"filter" : {
},
"$db" : "test"
},
"serverInfo" : {
"host" : "localhost.localdomain",
"port" : 27017,
"version" : "5.0.8",
"gitVersion" : "c87e1c23421bf79614baf500fda6622bd90f674e"
},
"serverParameters" : {
"internalQueryFacetBufferSizeBytes" : 104857600,
"internalQueryFacetMaxOutputDocSizeBytes" : 104857600,
"internalLookupStageIntermediateDocumentMaxSizeBytes" : 104857600,
"internalDocumentSourceGroupMaxMemoryBytes" : 104857600,
"internalQueryMaxBlockingSortMemoryUsageBytes" : 104857600,
"internalQueryProhibitBlockingMergeOnMongoS" : 0,
"internalQueryMaxAddToSetBytes" : 104857600,
"internalDocumentSourceSetWindowFieldsMaxMemoryBytes" : 104857600
},
"ok" : 1
}
主要参数:
1.winningPlan.stage:最佳的计划阶段,其值为(含子阶段值):
SINGLE_SHARD:单一分片操作
SHARD_MERGE:多分片合并操作
IXSCAN:带索引查询
COLLSCAN:集合扫描。在处理海量数据时,尽量避免出现这种执行方式。因 为集合扫描过 程比较低效,甚至影响系统的运行性能。
AND_HASH:带哈希索引的操作
OR:索引操作的条件里带“$or”表达式
SHARDING_FILTER:分片索引
SORT:在内存中进行了排序,在处理大规模数据时,需要慎重考虑是否需要 这样做,因为它 消耗内存,影响系统运行性能
LIMIT:使用limit()命令限制返回数
SKIP:使用skip()进行跳过
IDHACK:针对_id进行查询
COUNT:使用count()进行了查询
TEXT:使用全文索引进行查询
COUNT_SCAN:查询时使用Index进行count()。
2.shardName:分片名,如“shard003”。
3.connectionString:分片所在的服务器地址,如“localhost:27023”。
4.serverInfo:服务器相关的信息,如host、port等
5.namespace:find()数据库空间,包含了数据库名和集合名
6.indexFilterSet:代表find()运行时,是否使用了带索引key条件
7.parsedQuery:解析查询条件,如" e q " : 1 、 " eq":1、" eq":1、"and":[]等
8.inputStage:嵌套文档操作类型
9.rejectedPlans:查询优化器考虑和拒绝的候选计划数组,如果没有候选计划,数组为空。
上述参数内容,在Explain()为“queryPlanner”模式下类似。下面为executionStats模式下的新增统计内容:
- nReturned:返回符合查询条件的文档数;
- executionTimeMillis:查询计划选择和查询执行所需的总时间(单位:毫秒),该时间越小,系统 响应越快,若超过2、3秒就应该引起重视;
- totalKeysExamined:扫描的索引条目数量;扫描索引条目的数量越少越好;
- totalDocsExamined:扫描文档的数量;越少越好
- executionStages.works:查询工作单元数,一个查询执行可以将其工作分成若干个小单元,如检 查单个索引字、从集合中获取单个文档,将投影应用于单个文档等;
- executionStages.advanced:优先返回的结果数目;
- executionStages.needTime:在子阶段,执行未优化的操作过程所需要的时间;
- executionStages.needYield:数据库查询时,产生的锁的次数;次数越少越好,越多说明查询性 能存在问题,并发查询冲突等问题严重;
- executionStages.isEOF:指定执行阶段是否结束,如果结束该值为true或1;如果没有结束该值为 false或0。
- executionStages.invalidates:执行字阶段无效的数量。
3.Hint()分析
db.users.find().hint({KaTeX parse error: Expected 'EOF', got '}' at position 10: natural:1}̲) //强制执行正向扫描 db…natural:-1}) //强制执行反向扫描
> use eshops
switched to db eshops
> db.books.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_"
},
{
"v" : 2,
"key" : {
"summary.kind" : 1,
"summary.content" : -1
},
"name" : "summary.kind_1_summary.content_-1"
},
{
"v" : 2,
"key" : {
"tags.no" : 1,
"tags.press" : -1
},
"name" : "tags.no_1_tags.press_-1"
}
]
#在hint()强制执行基于_id正向扫描时,其执行结果Explain()主要时间参数如下:
> db.books.find({"summary.kind":"少儿"}).hint({_id:1}).explain("executionStats")
{
#省略部分内容
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 1,
"executionTimeMillis" : 15,
"totalKeysExamined" : 4,
"totalDocsExamined" : 4,
#省略部分内容
}
在没有强制指定索引方式时,其执行结果Explain()主要时间参数如下:
> db.books.find({"summary.kind":"少儿"}).explain("executionStats")
{
#省略部分内容
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 1,
"executionTimeMillis" : 12,
"totalKeysExamined" : 1,
"totalDocsExamined" : 1,
#省略部分内容
}
两次虽然在总用时时间上差不多,但是在查找执行过程中,在强制方式下扫描了4个索引对象,并检查了 4个文档;在没有强制方式下,只扫描了一个索引对象,一个文档;这在大数据环境下,后者效率要高很 多。
可视化管理工具
MongoDB支持好多可视化管理工具:
Robomongo 管理工具
Rockmongo
Mongo Vue
Ops 管理工具(MongoDB Ops Manager)
Compass数据浏览和分析工具
Cloud管理工具(MongoDB Cloud Manager)
Robomongo
- 免费且开源,官方下载地址:https://robomongo.org/download
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wmNZMIXk-1652235940310)(https://p1.xywm.ltd/2022/05/11/627b1d4b243bb.png)]
2.安装后建立连接
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2O7aVvwe-1652235940311)(https://p1.xywm.ltd/2022/05/11/627b1d7572d45.png)]
3.建立连接后既可以进行操作
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n9y6s2a6-1652235940312)(https://p1.xywm.ltd/2022/05/11/627b1dab464d1.png)]
“nReturned” : 1,
“executionTimeMillis” : 12,
“totalKeysExamined” : 1,
“totalDocsExamined” : 1,
#省略部分内容
}
两次虽然在总用时时间上差不多,但是在查找执行过程中,在强制方式下扫描了4个索引对象,并检查了 4个文档;在没有强制方式下,只扫描了一个索引对象,一个文档;这在大数据环境下,后者效率要高很 多。
# **可视化管理工具**
MongoDB支持好多可视化管理工具:
Robomongo 管理工具
Rockmongo
Mongo Vue
Ops 管理工具(MongoDB Ops Manager)
Compass数据浏览和分析工具
Cloud管理工具(MongoDB Cloud Manager)
## **Robomongo**
1. 免费且开源,官方下载地址:https://robomongo.org/download
[外链图片转存中...(img-wmNZMIXk-1652235940310)]
2.安装后建立连接
[外链图片转存中...(img-2O7aVvwe-1652235940311)]
3.建立连接后既可以进行操作
[外链图片转存中...(img-n9y6s2a6-1652235940312)]