MongoDB的查询优化及索引

索引

索引是加速查询的一种数据结构

查看索引

> db.users.getIndexes()

[

        {

                "v" : 1,

                "key" : {

                        "_id" ://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

//( 默认是01是记录慢查询,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()//索引不生效

猜你喜欢

转载自blog.csdn.net/lql_h/article/details/88126759