MongoDB数据库中索引和explain的示例分析

73次阅读
没有评论

共计 7411 个字符,预计需要花费 19 分钟才能阅读完成。

这篇文章主要介绍了 MongoDB 数据库中索引和 explain 的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让丸趣 TV 小编带着大家一起了解一下。

mongodb 索引使用

作用

索引通常能够极大的提高查询。

索引是一种数据结构,他搜集一个集合中文档特定字段的值。

B-Tree 索引来实现。

创建索引

db.collection.createIndex(keys, options)

keys

keys 由文档字段和索引类型组成。如 {name :1}

key 表示字段 value 1,-1  1 表示升序,- 1 降序

options

options 创建索引的选项。

参数类型描述 backgroundboolean 创建索引在后台运行,不会阻止其他对数据库操作 uniqueboolean 创建唯一索引,文档的值不会重复 namestring 索引名称,默认是:字段名_排序类型 开始排序 sparseboolean 过滤掉 null,不存在的字段

查看索引

 db.collection.getIndexes()
 {
  v  : 1,
  key  : {
  _id  : 1
 },
  name  :  _id_ ,
  ns  :  leyue.userdatas 
 },
 {
  v  : 1,
  key  : {
  name  : 1 // 索引字段
 },
  name  :  name_1 , // 索引名称
  ns  :  leyue.userdatas 
 }

删除索引

  db.collection.dropIndex(index) 删除指定的索引。

  db.collection.dropIndexes() 删除除了_id 以外的所有索引。

index 是字符串 表示按照索引名称 name 删除字段。

index 是 {字段名称:1} 表示按照 key 删除索引。

创建 / 查看 / 删除 示例

查看数据

 db.userdatas.find()
{  _id  : ObjectId( 597f357a09c84cf58880e412),  name  :  u3 ,  age  : 32 }
{  _id  : ObjectId( 597f357a09c84cf58880e411),  name  :  u4 ,  age  : 30,  score  : [ 7, 4, 2, 0 ] }
{  _id  : ObjectId( 597fcc0f411f2b2fd30d0b3f),  age  : 20,  score  : [ 7, 4, 2, 0, 10, 9, 8, 7 ],  name  :  lihao  }
{  _id  : ObjectId( 597f357a09c84cf58880e413),  name  :  u2 ,  age  : 33,  wendang  : {  yw  : 80,  xw  : 90 } }
{  _id  : ObjectId( 5983f5c88eec53fbcd56a7ca),  date  : ISODate(2017-08-04T04:19:20.693Z) }
{  _id  : ObjectId( 597f357a09c84cf58880e40e),  name  :  u1 ,  age  : 26,  address  :  中国砀山  }
{  _id  : ObjectId( 597f357a09c84cf58880e40f),  name  :  u1 ,  age  : 37,  score  : [ 10, 203, 12, 43, 56, 22 ] }
{  _id  : ObjectId( 597f357a09c84cf58880e410),  name  :  u5 ,  age  : 78,  address  :  china beijing chaoyang  }

给字段 name 创建索引

 //  创建索引
 db.userdatas.createIndex({name :1})
 {
  createdCollectionAutomatically  : false,
  numIndexesBefore  : 1,
  numIndexesAfter  : 2,
  ok  : 1
 }

 }  ]

给字段 name 创建索引并命名为 myindex

 db.userdatas.createIndex({name :1})
 db.userdatas.createIndex({name :1},{name : myindex})
 db.userdatas.getIndexes()
 [
 {
  v  : 1,
  key  : {
  _id  : 1
 },
  name  :  _id_ ,
  ns  :  leyue.userdatas 
 },
 {
  v  : 1,
  key  : {
  name  : 1
 },
  name  :  myindex ,
  ns  :  leyue.userdatas 
 }
 ]

给字段 name 创建索引 创建的过程在后台执行

当 mongodb 集合里面的数据过大时 创建索引很耗时,可以在放在后台运行。

 db.userdatas.dropIndex(myindex)
 db.userdatas.createIndex({name :1},{name : myindex , background :true})

给 age 字段创建唯一索引

 db.userdatas.createIndex({age :-1},{name : ageIndex , unique :true, sparse :true})
 db.userdatas.getIndexes()
 {
  v  : 1,
  key  : {
  _id  : 1
 },
  name  :  _id_ ,
  ns  :  leyue.userdatas 
 },
 {
  v  : 1,
  key  : {
  name  : 1
 },
  name  :  myindex ,
  ns  :  leyue.userdatas ,
  background  : true
 },
 {
  v  : 1,
  unique  : true,
  key  : {
  age  : -1
 },
  name  :  ageIndex ,
  ns  :  leyue.userdatas ,
  sparse  : true
 }
//  插入一个已存在的 age
 db.userdatas.insert({  name  :  u8 ,  age  : 32})

  code  : 11000,   errmsg  :  E11000 duplicate key error index: leyue.userdatas.$ageIndex dup key: { : 32.0 }  } })

创建复合索引

 db.userdatas.createIndex({name :1, age :-1})
 db.userdatas.getIndexes()
 {
  v  : 1,
  key  : {
  _id  : 1
 },
  name  :  _id_ ,
  ns  :  leyue.userdatas 
 },
 {
  v  : 1,
  key  : {
  name  : 1,
  age  : -1
 },
  name  :  name_1_age_-1 ,
  ns  :  leyue.userdatas 
 }
]

所有的字段都存在集合 system.indexes 中

db.system.indexes.find()
{  v  : 1,  key  : {  _id  : 1 },  name  :  _id_ ,  ns  :  leyue.userdatas  }
{  v  : 1,  key  : {  _id  : 1 },  name  :  _id_ ,  ns  :  leyue.scores  }
{  v  : 1,  key  : {  _id  : 1 },  name  :  _id_ ,  ns  :  leyue.test  }
{  v  : 1,  key  : {  user  : 1,  name  : 1 },  name  :  myindex ,  ns  :  leyue.test  }
{  v  : 1,  key  : {  _id  : 1 },  name  :  _id_ ,  ns  :  leyue.mycapped  }
{  v  : 1,  key  : {  user  : 1 },  name  :  user_1 ,  ns  :  leyue.test  }
{  v  : 1,  key  : {  name  : 1 },  name  :  myindex ,  ns  :  leyue.userdatas  }

索引总结

  1: 创建索引时,1 表示按升序存储,- 1 表示按降序存储。

  2: 可以创建复合索引, 如果想用到复合索引, 必须在查询条件中包含复合索引中的前 N 个索引列

  3: 如果查询条件中的键值顺序和复合索引中的创建顺序不一致的话,

  MongoDB 可以智能的帮助我们调整该顺序, 以便使复合索引可以为查询所用。

  4: 可以为内嵌文档创建索引, 其规则和普通文档创建索引是一样的。

  5: 一次查询中只能使用一个索引,$or 特殊, 可以在每个分支条件上使用一个索引。

  6: $where,$exists 不能使用索引, 还有一些低效率的操作符, 比如:$ne,$not,$nin 等。

  7: 设计多个字段的索引时, 应该尽量将用于精确匹配的字段放在索引的前面。

explain 使用

语法

 db.collection.explain(). method(...)

explain() 可以设置参数:

queryPlanner。

executionStats。

allPlansExecution。

示例

for(var i=0;i 100000;i++) { db.test.insert({ user : user +i});
}

没有使用索引

 db.test.explain(executionStats).find({user : user200000})
  queryPlanner  : {
  plannerVersion  : 1,
  namespace  :  leyue.test ,
  indexFilterSet  : false,
  parsedQuery  : {
  user  : {
  $eq  :  user200000 
 }
 },
  winningPlan  : {
  stage  :  COLLSCAN ,
  filter  : {
  user  : {
  $eq  :  user200000 
 }
 },
  direction  :  forward 
 },
  rejectedPlans  : [ ]
 },
  executionStats  : {
  executionSuccess  : true,
  nReturned  : 2,
  executionTimeMillis  : 326,
  totalKeysExamined  : 0,
  totalDocsExamined  : 1006497,
  executionStages  : {
  stage  :  COLLSCAN ,
  filter  : {
  user  : {
  $eq  :  user200000 
 }
 },
  nReturned  : 2,
  executionTimeMillisEstimate  : 270,
  works  : 1006499,
  advanced  : 2,
  needTime  : 1006496,
  needYield  : 0,
  saveState  : 7863,
  restoreState  : 7863,
  isEOF  : 1,
  invalidates  : 0,
  direction  :  forward ,
  docsExamined  : 1006497
 }
 },
  serverInfo  : {
  host  :  lihaodeMacBook-Pro.local ,
  port  : 27017,
  version  :  3.2.1 ,
  gitVersion  :  a14d55980c2cdc565d4704a7e3ad37e4e535c1b2 
 },
  ok  : 1
}

executionStats.executionTimeMillis: query 的整体查询时间。

executionStats.nReturned : 查询返回的条目。

executionStats.totalKeysExamined:索引扫描条目。

executionStats.totalDocsExamined:文档扫描条目。

executionTimeMillis = 326 query 执行时间

nReturned=2 返回两条数据

totalKeysExamined=0 没有用到索引

totalDocsExamined 全文档扫描

理想状态:

nReturned=totalKeysExamined totalDocsExamined=0

Stage 状态分析

stage 描述 COLLSCAN 全表扫描 IXSCAN 扫描索引 FETCH 根据索引去检索指定 documentSHARD_MERGE 将各个分片返回数据进行 mergeSORT 表明在内存中进行了排序 LIMIT 使用 limit 限制返回数 SKIP 使用 skip 进行跳过 IDHACK 针对_id 进行查询 SHARDING_FILTER 通过 mongos 对分片数据进行查询 COUNT 利用 db.coll.explain().count() 之类进行 count 运算 COUNTSCANcount 不使用 Index 进行 count 时的 stage 返回 COUNT_SCANcount 使用了 Index 进行 count 时的 stage 返回 SUBPLA 未使用到索引的 $or 查询的 stage 返回 TEXT 使用全文索引进行查询时候的 stage 返回 PROJECTION 限定返回字段时候 stage 的返回

对于普通查询,我希望看到 stage 的组合 (查询的时候尽可能用上索引):

  Fetch+IDHACK

  Fetch+ixscan

  Limit+(Fetch+ixscan)

  PROJECTION+ixscan

  SHARDING_FITER+ixscan

  COUNT_SCAN

不希望看到包含如下的 stage:

COLLSCAN(全表扫描),SORT(使用 sort 但是无 index), 不合理的 SKIP,SUBPLA(未用到 index 的 $or),COUNTSCAN(不使用 index 进行 count)

使用索引

 db.test.createIndex({user :1},{name : myindex , background :true})
 db.test.explain(executionStats).find({user : user200000})
  queryPlanner  : {
  plannerVersion  : 1,
  namespace  :  leyue.test ,
  indexFilterSet  : false,
  parsedQuery  : {
  user  : {
  $eq  :  user200000 
 }
 },
  winningPlan  : {
  stage  :  FETCH ,
  inputStage  : {
  stage  :  IXSCAN ,
  keyPattern  : {
  user  : 1
 },
  indexName  :  myindex ,
  isMultiKey  : false,
  isUnique  : false,
  isSparse  : false,
  isPartial  : false,
  indexVersion  : 1,
  direction  :  forward ,
  indexBounds  : {
  user  : [  [\ user200000\ , \ user200000\] 
 ]
 }
 }
 },
  rejectedPlans  : [ ]
 },
  executionStats  : {
  executionSuccess  : true,
  nReturned  : 2,
  executionTimeMillis  : 0,
  totalKeysExamined  : 2,
  totalDocsExamined  : 2,
  executionStages  : {
  stage  :  FETCH ,
  nReturned  : 2,
  executionTimeMillisEstimate  : 0,
  works  : 3,
  advanced  : 2,
  needTime  : 0,
  needYield  : 0,
  saveState  : 0,
  restoreState  : 0,
  isEOF  : 1,
  invalidates  : 0,
  docsExamined  : 2,
  alreadyHasObj  : 0,
  inputStage  : {
  stage  :  IXSCAN ,
  nReturned  : 2,
  executionTimeMillisEstimate  : 0,
  works  : 3,
  advanced  : 2,
  needTime  : 0,
  needYield  : 0,
  saveState  : 0,
  restoreState  : 0,
  isEOF  : 1,
  invalidates  : 0,
  keyPattern  : {
  user  : 1
 },
  indexName  :  myindex ,
  isMultiKey  : false,
  isUnique  : false,
  isSparse  : false,
  isPartial  : false,
  indexVersion  : 1,
  direction  :  forward ,
  indexBounds  : {
  user  : [  [\ user200000\ , \ user200000\] 
 ]
 },
  keysExamined  : 2,
  dupsTested  : 0,
  dupsDropped  : 0,
  seenInvalidated  : 0
 }
 }
 },
  serverInfo  : {
  host  :  lihaodeMacBook-Pro.local ,
  port  : 27017,
  version  :  3.2.1 ,
  gitVersion  :  a14d55980c2cdc565d4704a7e3ad37e4e535c1b2 
 },
  ok  : 1
}

executionTimeMillis: 0

totalKeysExamined: 2

totalDocsExamined:2

nReturned:2

stage:IXSCAN

使用索引和不使用差距很大,合理使用索引,一个集合适合做 4-5 个索引。

感谢你能够认真阅读完这篇文章,希望丸趣 TV 小编分享的“MongoDB 数据库中索引和 explain 的示例分析”这篇文章对大家有帮助,同时也希望大家多多支持丸趣 TV,关注丸趣 TV 行业资讯频道,更多相关知识等着你来学习!

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-08-04发表,共计7411字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)