mongodb 使用索引优化查询

数据库 > tmp

集合 > app_down

 

> use tmp

switched to db tmp

R:使用tmp数据库

 

> db.createCollection("app_down");

{ "ok" : 1 }

R:创建集合

 

> db.app_down.save({"app_id":1002,"count_down":33,"stat_date":"2014-01-01"});

> db.app_down.save({"app_id":1102,"count_down":33,"stat_date":"2014-01-02"});

> db.app_down.save({"app_id":1100,"count_down":33,"stat_date":"2014-01-02"});

R:模拟测试数据

 

> db.app_down.find();

{ "_id" : ObjectId("534f72c0c7f347b9e5b44efc"), "app_id" : 1002, "count_down" : 33, "stat_date" : "2014-01-01" }

{ "_id" : ObjectId("534f72cac7f347b9e5b44efd"), "app_id" : 1102, "count_down" : 33, "stat_date" : "2014-01-02" }

{ "_id" : ObjectId("534f72cec7f347b9e5b44efe"), "app_id" : 1100, "count_down" : 33, "stat_date" : "2014-01-02" }

R:查询所有数据

 

> db.app_down.getIndexes();

[

{

"v" : 1,

"key" : {

"_id" : 1

},

"ns" : "tmp.app_down",

"name" : "_id_"

}

]

R: 查询集合索引列表,插入数据时默认会给_id创建"升序"索引,在find()中不排序情况下默认是按照_id"升序" 排序

 

> db.app_down.find({app_id:1100}).explain();

{

"cursor" : "BasicCursor",

"isMultiKey" : false,

"n" : 1,

"nscannedObjects" : 3,

"nscanned" : 3,

"nscannedObjectsAllPlans" : 3,

"nscannedAllPlans" : 3,

"scanAndOrder" : false,

"indexOnly" : false,

"nYields" : 0,

"nChunkSkips" : 0,

"millis" : 0,

"indexBounds" : {

 

},

"server" : "tts-0001:27017"

}

R:查询分析 cursor索引类型,BasicCursor没有使用索引,

 

> db.app_down.ensureIndex({app_id:1});

R:给字段app_id创建升序索引

 

> db.app_down.getIndexes();

[

{

"v" : 1,

"key" : {

"_id" : 1

},

"ns" : "tmp.app_down",

"name" : "_id_"

},

{

"v" : 1,

"key" : {

"app_id" : 1

},

"ns" : "tmp.app_down",

"name" : "app_id_1"

}

]

R:索引名字以字段+升序/降序组成,_id_默认升序,app_id_1指按照app_id升序

 

> db.app_down.find({app_id:1100}).explain();

{

"cursor" : "BtreeCursor app_id_1",

"isMultiKey" : false,

"n" : 1,

"nscannedObjects" : 1,

"nscanned" : 1,

"nscannedObjectsAllPlans" : 1,

"nscannedAllPlans" : 1,

"scanAndOrder" : false,

"indexOnly" : false,

"nYields" : 0,

"nChunkSkips" : 0,

"millis" : 0,

"indexBounds" : {

"app_id" : [

[

1100,

1100

]

]

},

"server" : "tts-0001:27017"

}

R:创建索引后在执行查询分析,明显看到提速

 

> db.app_down.save({"app_id":1002,"count_down":33,"stat_date":"2014-01-01"});

R:继续插入测试数据

 

> db.app_down.find();

{ "_id" : ObjectId("534f72c0c7f347b9e5b44efc"), "app_id" : 1002, "count_down" : 33, "stat_date" : "2014-01-01" }

{ "_id" : ObjectId("534f72cac7f347b9e5b44efd"), "app_id" : 1102, "count_down" : 33, "stat_date" : "2014-01-02" }

{ "_id" : ObjectId("534f72cec7f347b9e5b44efe"), "app_id" : 1100, "count_down" : 33, "stat_date" : "2014-01-02" }

{ "_id" : ObjectId("534f7383c7f347b9e5b44f00"), "app_id" : 1002, "count_down" : 33, "stat_date" : "2014-01-01" }

R:可以看到刚刚插入的数据

 

> db.app_down.ensureIndex({app_id:1,stat_date:1},{unique:true});

Thu Apr 17 14:26:23.559 JavaScript execution failed: ReferenceError: date is not defined

R:按照业务逻辑app_id和stat_date必须是唯一的,此时创建唯一索引,而原始数据并不唯一,此时创建索引失败

 

> db.app_down.ensureIndex({app_id:1,stat_date:1},{unique:true,dropDups:true});

R:创建唯一索引,同时删除重复的记录,遵循先近后出,后创建的会被先删除

 

> db.app_down.find();

{ "_id" : ObjectId("534f72c0c7f347b9e5b44efc"), "app_id" : 1002, "count_down" : 33, "stat_date" : "2014-01-01" }

{ "_id" : ObjectId("534f72cac7f347b9e5b44efd"), "app_id" : 1102, "count_down" : 33, "stat_date" : "2014-01-02" }

{ "_id" : ObjectId("534f72cec7f347b9e5b44efe"), "app_id" : 1100, "count_down" : 33, "stat_date" : "2014-01-02" }

R:看到最后添加的一条重复数据在创建唯一索引时被删掉了

 

> db.app_down.save({"app_id":1002,"count_down":33,"stat_date":"2014-01-01"});

E11000 duplicate key error index: tmp.app_down.$app_id_1_stat_date_1  dup key: { : 1002.0, : "2014-01-01" }

R:在创建唯一索引的集合上继续添加重复数据,无法添加成功

 

> db.app_down.save({"app_id":1112,"count_down":33,"stat_date":"2014-01-01"});

R:继续插入测试数据(不违反唯一索引约束)

 

> db.app_down.find();

{ "_id" : ObjectId("534f72c0c7f347b9e5b44efc"), "app_id" : 1002, "count_down" : 33, "stat_date" : "2014-01-01" }

{ "_id" : ObjectId("534f72cac7f347b9e5b44efd"), "app_id" : 1102, "count_down" : 33, "stat_date" : "2014-01-02" }

{ "_id" : ObjectId("534f72cec7f347b9e5b44efe"), "app_id" : 1100, "count_down" : 33, "stat_date" : "2014-01-02" }

{ "_id" : ObjectId("534f74fbc7f347b9e5b44f09"), "app_id" : 1112, "count_down" : 33, "stat_date" : "2014-01-01" }

R:可以看到刚刚插入的数据

 

> db.app_down.save({"app_id":1112,"count_down":33});

R:继续插入测试数据

 

> db.app_down.find();

{ "_id" : ObjectId("534f72c0c7f347b9e5b44efc"), "app_id" : 1002, "count_down" : 33, "stat_date" : "2014-01-01" }

{ "_id" : ObjectId("534f72cac7f347b9e5b44efd"), "app_id" : 1102, "count_down" : 33, "stat_date" : "2014-01-02" }

{ "_id" : ObjectId("534f72cec7f347b9e5b44efe"), "app_id" : 1100, "count_down" : 33, "stat_date" : "2014-01-02" }

{ "_id" : ObjectId("534f74fbc7f347b9e5b44f09"), "app_id" : 1112, "count_down" : 33, "stat_date" : "2014-01-01" }

{ "_id" : ObjectId("534f751bc7f347b9e5b44f0a"), "app_id" : 1112, "count_down" : 33 }

R:可以看到刚刚插入的数据

 

> db.app_down.getIndexes();

[

{

"v" : 1,

"key" : {

"_id" : 1

},

"ns" : "tmp.app_down",

"name" : "_id_"

},

{

"v" : 1,

"key" : {

"app_id" : 1

},

"ns" : "tmp.app_down",

"name" : "app_id_1"

},

{

"v" : 1,

"key" : {

"app_id" : 1,

"stat_date" : 1

},

"unique" : true,

"ns" : "tmp.app_down",

"name" : "app_id_1_stat_date_1",

"dropDups" : true

}

]

R:查询现在的索引列表

 

> db.app_down.ensureIndex({app_id:1,stat_date:1},{unique:true});

R:创建重复索引(重复只包含字段,ensureIndex函数的第一个参数)

 

> db.app_down.getIndexes();

[

{

"v" : 1,

"key" : {

"_id" : 1

},

"ns" : "tmp.app_down",

"name" : "_id_"

},

{

"v" : 1,

"key" : {

"app_id" : 1

},

"ns" : "tmp.app_down",

"name" : "app_id_1"

},

{

"v" : 1,

"key" : {

"app_id" : 1,

"stat_date" : 1

},

"unique" : true,

"ns" : "tmp.app_down",

"name" : "app_id_1_stat_date_1",

"dropDups" : true

}

]

R:查询集合的索引列表

 

> db.app_down.dropIndex("app_id_1_stat_date_1");

{ "nIndexesWas" : 3, "ok" : 1 }

R:删除索引

 

> db.app_down.getIndexes();

[

{

"v" : 1,

"key" : {

"_id" : 1

},

"ns" : "tmp.app_down",

"name" : "_id_"

},

{

"v" : 1,

"key" : {

"app_id" : 1

},

"ns" : "tmp.app_down",

"name" : "app_id_1"

}

]

R:查询集合的索引列表

 

> db.app_down.ensureIndex({app_id:1,stat_date:1},{unique:true,dropDups:true,sparse:true});

R:sparse参数指稀疏索引,索引字段值为空时不参与索引检索,为节省空间,这些记录不参与到索引中

 

> db.app_down.getIndexes();

[

{

"v" : 1,

"key" : {

"_id" : 1

},

"ns" : "tmp.app_down",

"name" : "_id_"

},

{

"v" : 1,

"key" : {

"app_id" : 1

},

"ns" : "tmp.app_down",

"name" : "app_id_1"

},

{

"v" : 1,

"key" : {

"app_id" : 1,

"stat_date" : 1

},

"unique" : true,

"ns" : "tmp.app_down",

"name" : "app_id_1_stat_date_1",

"dropDups" : true,

"sparse" : true

}

]

R:可以看到使用了松散索引

 

> db.app_down.ensureIndex({app_id:1,stat_date:-1},{unique:true,dropDups:true,sparse:true,background:true});

R:在给大数据创建索引时防止锁表发生,使用后台索引

 

> db.app_down.getIndexes();

[

{

"v" : 1,

"key" : {

"_id" : 1

},

"ns" : "tmp.app_down",

"name" : "_id_"

},

{

"v" : 1,

"key" : {

"app_id" : 1

},

"ns" : "tmp.app_down",

"name" : "app_id_1"

},

{

"v" : 1,

"key" : {

"app_id" : 1,

"stat_date" : 1

},

"unique" : true,

"ns" : "tmp.app_down",

"name" : "app_id_1_stat_date_1",

"dropDups" : true,

"sparse" : true

},

{

"v" : 1,

"key" : {

"app_id" : 1,

"stat_date" : -1

},

"unique" : true,

"ns" : "tmp.app_down",

"name" : "app_id_1_stat_date_-1",

"dropDups" : true,

"sparse" : true,

"background" : true

}

]

R:可以看到后台索引创建了,注意此时的索引字段排序不同上一个索引

 

> db.app_down.find({app_id:1002,stat_date:"2014-01-01"}).sort({count_down:1}).explain();

{

"cursor" : "BtreeCursor app_id_1",

"isMultiKey" : false,

"n" : 1,

"nscannedObjects" : 1,

"nscanned" : 1,

"nscannedObjectsAllPlans" : 2,

"nscannedAllPlans" : 4,

"scanAndOrder" : true,

"indexOnly" : false,

"nYields" : 0,

"nChunkSkips" : 0,

"millis" : 0,

"indexBounds" : {

"app_id" : [

[

1002,

1002

]

]

},

"server" : "tts-0001:27017"

}

R:看到 "scanAndOrder" : true 意味着悲剧发生了,这次查询是先进行了scan获取到数据,再进行了独立的排序操作的,如有必要把排序字段也加到索引中吧

> db.app_down.find({stat_date:"2014-01-01"}).explain();

{

"cursor" : "BasicCursor",

"isMultiKey" : false,

"n" : 2,

"nscannedObjects" : 5,

"nscanned" : 5,

"nscannedObjectsAllPlans" : 5,

"nscannedAllPlans" : 5,

"scanAndOrder" : false,

"indexOnly" : false,

"nYields" : 0,

"nChunkSkips" : 0,

"millis" : 0,

"indexBounds" : {

},

"server" : "tts-0001:27017"

}

> db.app_down.find({stat_date:"2014-01-01",app_id:1002}).explain();

{

"cursor" : "BtreeCursor app_id_1_stat_date_1",

"isMultiKey" : false,

"n" : 1,

"nscannedObjects" : 1,

"nscanned" : 1,

"nscannedObjectsAllPlans" : 1,

"nscannedAllPlans" : 1,

"scanAndOrder" : false,

"indexOnly" : false,

"nYields" : 0,

"nChunkSkips" : 0,

"millis" : 0,

"indexBounds" : {

"app_id" : [

[

1002,

1002

]

],

"stat_date" : [

[

"2014-01-01",

"2014-01-01"

]

]

},

"server" : "tts-0001:27017"

}

> db.app_down.dropIndex("app_id_1");

{ "nIndexesWas" : 4, "ok" : 1 }

> db.app_down.find({app_id:1002}).explain();

{

"cursor" : "BtreeCursor app_id_1_stat_date_1",

"isMultiKey" : false,

"n" : 1,

"nscannedObjects" : 1,

"nscanned" : 1,

"nscannedObjectsAllPlans" : 1,

"nscannedAllPlans" : 1,

"scanAndOrder" : false,

"indexOnly" : false,

"nYields" : 0,

"nChunkSkips" : 0,

"millis" : 0,

"indexBounds" : {

"app_id" : [

[

1002,

1002

]

],

"stat_date" : [

[

{

"$minElement" : 1

},

{

"$maxElement" : 1

}

]

]

},

"server" : "tts-0001:27017"

}

R:如果想用到复合索引,必须在查询条件中包含复合索引中的前N个索引列。然而如果查询条件中的键值顺序和复合索引中的创建顺序不一致的话,MongoDB可以智能的帮助我们调整该顺序,以便使复合索引可以为查询所用

关于explain函数返回字段的解释:

 

1 "cursor":如果没有使用索引,游标的类型是BasicCursor,查询使用了索引,MongoDB中索引存储在B树结构中,BtreeCursor类型的游标。

 

2 "nscanned"/"nscannedObjects":表明当前这次查询一共扫描了集合中多少个文档,让这个数值和返回文档的数量越接近越好

 

3 "n":当前查询返回的文档数量

 

4 "millis":当前查询所需时间,毫秒数

 

5 "indexBounds":当前查询具体使用的索引

猜你喜欢

转载自871421448.iteye.com/blog/2047860