索引
索引是加速查询的一种数据结构
查看索引
> db.users.getIndexes()
[
{
"v" : 1,
"key" : {
"_id" : 1 //1是升序,-1是降序
},
"name" : "_id_",
"ns" : "test2.users"
}
]
增加索引
> db.users.ensureIndex({"uid":1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
> db.users.getIndexes()
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test2.users"
},
{
"v" : 1,
"key" : {
"uid" : 1
},
"name" : "uid_1",
"ns" : "test2.users"
}
]
增加联合索引
> db.users.ensureIndex({"uid":1,uname:1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 2,
"numIndexesAfter" : 3,
"ok" : 1
}
> db.users.getIndexes()
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test2.users"
},
{
"v" : 1,
"key" : {
"uid" : 1
},
"name" : "uid_1",
"ns" : "test2.users"
},
{
"v" : 1,
"key" : {
"uid" : 1,
"uname" : 1
},
"name" : "uid_1_uname_1",
"ns" : "test2.users"
}
]
db.users.createIndex({"uname":1})//和ensureIndex的效果一样
索引命名
db.user2.ensureIndex({"uuid":1},{"name":"uid"})
什么时候需要索引
db.users.find({}).sort({})
创建索引的依据:
(1).Find命令中有条件时,根据条件字段创建索引
(2).Sort中有排序字段时,根据排序字段创建索引
索引的考虑的因素:
(1)产品需求决定查询,查询条件决定索引
(2)查询键的方向性,联合索引需要考虑键的方向问题
(3)扩展性
索引的分类
主键索引:_id键索引,系统保证和维护键值的唯一性。不能被删除。主键可以为null
普通索引:分为单键和复合索引。
唯一索引:也叫唯一约束,能保证和维护键值的唯一性。
地理空间索引:查找和定位地理空间
唯一索引
单键唯一索引:db.users2.createIndex({"uuid":1},{"unique":true})
复合唯一索引:db.users2.createIndex({"uuid":1,"uname":1},{"unique":true})
> db.users2.getIndexes()
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test2.users2"
},
{
"v" : 1,
"unique" : true,
"key" : {
"uuid" : 1
},
"name" : "uuid_1",
"ns" : "test2.users2"
},
{
"v" : 1,
"unique" : true,
"key" : {
"uuid" : 1,
"uname" : 1
},
"name" : "uuid_1_uname_1",
"ns" : "test2.users2"
}
]
删除索引
db.users2.dropIndex({"uuid":1,"uname":1},{"unique" : true}) //删除唯一索引
db.users.dropIndex("uname":1) //删除普通索引
db.runCommand({"dropIndexes":"users","index":"uid_1_uname_1"})
//users为集合的名字,uid_1_uname_1为索引的名字
重建索引
数据不断插入、更新和删除,索引存储空间存在一定的碎片,此时需要重建索引,有优化作用
> db.users.reIndex({"uid":1})
{
"nIndexesWas" : 2,
"nIndexes" : 2,
"indexes" : [
{
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test2.users"
},
{
"key" : {
"uid" : 1
},
"name" : "uid_1",
"ns" : "test2.users"
}
],
"ok" : 1
}
地理空间索引
查找和定位地理坐标的专门索引,而地理坐标是由经度和纬度组成的
Mongodb把地理空间位置映射为平面数据进行处理
创建地理空间索引:
> db.coffee.find()
{ "_id" : ObjectId("5c7cc9c38af7c0243bb66af7"), "gps" : [ 100, 100 ] }
{ "_id" : ObjectId("5c7cc9cc8af7c0243bb66af8"), "gps" : [ 90, 90 ] }
{ "_id" : ObjectId("5c7cc9de8af7c0243bb66af9"), "gps" : [ 80, 80 ] }
db.coffee.createIndex({"gps":"2d"}) //2d是映射为平面数据
]
> db.coffee.find({"gps":{"$near":[90,90]} }).limit(2)
{ "_id" : ObjectId("5c7cc9cc8af7c0243bb66af8"), "gps" : [ 90, 90 ] }
{ "_id" : ObjectId("5c7cc9de8af7c0243bb66af9"), "gps" : [ 80, 80 ] }
//离[90,90]最近的2家咖啡馆
索引优化的原则
索引特点:
加速了查询
减慢了插入、更新和删除操作
增加了存储空间,增加了磁盘IO和内存消耗等。
索引优化的原则:
索引并不是越多越好,但也不能没有索引,而是精而少最好------最少化原则。
索引是否生效
db.users.find({"uname":"hxf1"}).explain()
"stage" : "IXSCAN",
db.users.find({}).sort({"uname":1}).explain() //此时索引也会起作用,1和-1都一样
db.users.find({}).explain()
"stage" : "COLLSCAN", //全表扫描
索引优化步骤
(1). 慢在哪里?
用profile 追踪慢查询,它能解决的是定位慢的查询。
--slowms arg (=100) value of slow for profile and console
//指定大于多少毫秒为慢查询
--profile arg 0=off 1=slow, 2=all
//( 默认是0,1是记录慢查询,2是全部记录)
db.getProfilingLevel() 返回level等级,值为0|1|2,分别代表意思:0代表关闭,1代表记录慢命令,2代表全部
db.getProfilingLevel() 0
> db.setProfilingLevel(1,1) //设置慢查询超过1毫秒就是慢查询
{ "was" : 0, "slowms" : 1, "ok" : 1 } //生效
> db.getProfilingStatus()
{ "was" : 1, "slowms" : 1 }
> db.table1.find({"name":"hxf1000000"})
{ "_id" : ObjectId("5c7cdf3a6b56f6f181532852"), "id" : 1000000, "name" : "hxf1000000" }
db.system.profile.find({"ns":"test2.table1"}).sort({"ts":-1}).limit(1)
{ "op" : "query", "ns" : "test2.table1", "query" : { "find" : "table1", "filter" : { "name" : "hxf1000000" } }, "keysExamined" : 0, "docsExamined" : 1000000, "cursorExhausted" : true, "keyUpdates" : 0, "writeConflicts" : 0, "numYield" : 7813, "locks" : { "Global" : { "acquireCount" : { "r" : NumberLong(15628) } }, "Database" : { "acquireCount" : { "r" : NumberLong(7814) } }, "Collection" : { "acquireCount" : { "r" : NumberLong(7814) } } }, "nreturned" : 1, "responseLength" : 161, "protocol" : "op_command", "millis" : 547, "execStats" : { "stage" : "COLLSCAN", "filter" : { "name" : { "$eq" : "hxf1000000" } }, "nReturned" : 1, "executionTimeMillisEstimate" : 420, "works" : 1000002, "advanced" : 1, "needTime" : 1000000, "needYield" : 0, "saveState" : 7813, "restoreState" : 7813, "isEOF" : 1, "invalidates" : 0, "direction" : "forward", "docsExamined" : 1000000 }, "ts" : ISODate("2019-03-04T08:24:40.546Z"), "client" : "127.0.0.1", "allUsers" : [ ], "user" : "" }
filter:过滤的条件
"docsExamined" : 1000000, 文档扫描了1000000行
"millis" : 547:执行的毫秒
"execStats" : { "stage" : "COLLSCAN", "filter" : { "name" : { "$eq" : "hxf1000000" } },
(2)相应优化
> db.table1.createIndex({"name":1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
此时的速度会加快
db.table1.find({"name":"hxf1000000"}).explain()
"stage" : "IXSCAN",
Hint的使用
当索引已经存在,但优化器不能很好选择索引时,通过查询Hint 给查询优化器传递提示,以便查询使用更高效的索引
db.table1.find({"name":"hxf1000000"}).hint({"name":1}).explain()
索引前端效应
索引的第一列出现在条件中,或者没有条件时,按组合索引顺序出现在排序中
(1)查询条件依据:
{“uname”:”huangxifeng”}
{“uname”:” huangxifeng”,”salary”:” 2000}
find({“uname”:”huangxifeng”}),sort({“salary”:1})
Sort({“uname”:1”, salary”:1})
Sort({“uname”:1” })
(2)创建索引
db.users.createIndex({“uname”:1,”salary”:1})
db.users.createIndex({"uname":1,"salary":1})//索引生效
db.users.find({"uname":"hxf1","salary":1557}).explain()//索引生效
db.users.find({"uname":"hxf1","salary":{"$gt":1557}}).explain()
//此时这两个字段无论是取值还是取范围还是变化顺序,索引都生效
db.users.find({"uname":"hxf1"}).explain()//索引的第一列出现在条件中,索引生效
db.users.find({"salary":{"$gt":1557}}).explain() //索引不生效
db.users.find().sort({"uname":1}).explain() //索引生效
db.users.find().sort({"uname":1,"salary":1}).explain()//索引生效
db.users.find().sort({"salary":1}).explain()//索引不生效