当前位置:   article > 正文

MongoDB索引_mongo indexed hash

mongo indexed hash

索引

什么是索引?

索引是特殊的数据结构,它以一种易于遍历的形式存储集合数据集的一小部分。索引存储一个或一组特定字段的值,按字段的值排序。索引项的排序支持有效的相等匹配和基于范围的查询操作。此外,MongoDB可以通过使用索引中的排序返回排序后的结果。

WiredTiger: B+

比如基于 B+ tree 的索引数据结构图示如下:

单键索引

如基于主键ID 进行的B+ tree 数据结构

复合索引(复合索引只能支持前缀子查询)(A,B,C)

基于name, age, position 建立的复合索引

name

name age

name age postion

索引的特点?

索引支持更快的查询

更快的排序

默认id索引

在创建集合期间,MongoDB 在_id字段上创建唯一索引。该索引可防止客户端插入两个具有相同值的文档。你不能将_id字段上的index删除。

创建索引

db.collection.createIndex(<keys>, <options>)

<keys> 指定了创建索引的字段

构造一组数据

  1. mongos> db.orders.insertMany( [
  2. ... { _id: 0, name: "Pepperoni", size: "small", price: 19,
  3. ... quantity: 10, date: ISODate( "2021-03-13T08:14:30Z" ) },
  4. ... { _id: 1, name: "Pepperoni", size: "medium", price: 20,
  5. ... quantity: 20, date : ISODate( "2021-03-13T09:13:24Z" ) },
  6. ... { _id: 2, name: "Pepperoni", size: "large", price: 21,
  7. ... quantity: 30, date : ISODate( "2021-03-17T09:22:12Z" ) },
  8. ... { _id: 3, name: "Cheese", size: "small", price: 12,
  9. ... quantity: 15, date : ISODate( "2021-03-13T11:21:39.736Z" ) },
  10. ... { _id: 4, name: "Cheese", size: "medium", price: 13,
  11. ... quantity:50, date : ISODate( "2022-01-12T21:23:13.331Z" ) },
  12. ... { _id: 5, name: "Cheese", size: "large", price: 14,
  13. ... quantity: 10, date : ISODate( "2022-01-12T05:08:13Z" ) },
  14. ... { _id: 6, name: "Vegan", size: "small", price: 17,
  15. ... quantity: 10, date : ISODate( "2021-01-13T05:08:13Z" ) },
  16. ... { _id: 7, name: "Vegan", size: "medium", price: 18,
  17. ... quantity: 10, date : ISODate( "2021-01-13T05:10:13Z" ) }
  18. ... ] )

创建一个单键索引

  1. mongos> db.orders.createIndex({"name":1})
  2. {
  3. "raw" : {
  4. "shard2/node01:28010,node01:28011,node01:28012" : {
  5. "numIndexesBefore" : 2,
  6. "numIndexesAfter" : 3,
  7. "createdCollectionAutomatically" : false,
  8. "commitQuorum" : "votingMembers",
  9. "ok" : 1
  10. },
  11. "shard1/node01:27010,node01:27011,node01:27012" : {
  12. "numIndexesBefore" : 2,
  13. "numIndexesAfter" : 3,
  14. "createdCollectionAutomatically" : false,
  15. "commitQuorum" : "votingMembers",
  16. "ok" : 1
  17. }
  18. },
  19. "ok" : 1,
  20. "$clusterTime" : {
  21. "clusterTime" : Timestamp(1652851733, 8),
  22. "signature" : {
  23. "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
  24. "keyId" : NumberLong(0)
  25. }
  26. },
  27. "operationTime" : Timestamp(1652851733, 8)
  28. }

索引的默认名称是索引键和索引中每个键的方向(即1或-1)的连接,使用下划线作为分隔符, 也可以通过指定 name 来自定义索引名称;

mongos> db.orders.createIndex({"name":1},{name:"name_1"})
{
        "raw" : {
                "shard1/node01:27010,node01:27011,node01:27012" : {
                        "numIndexesBefore" : 3,
                        "numIndexesAfter" : 3,
                        "note" : "all indexes already exist",
                        "ok" : 1
                },
                "shard2/node01:28010,node01:28011,node01:28012" : {
                        "numIndexesBefore" : 3,
                        "numIndexesAfter" : 3,
                        "note" : "all indexes already exist",
                        "ok" : 1
                }
        },
        "ok" : 1,
        "$clusterTime" : {
                "clusterTime" : Timestamp(1652851761, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        },
        "operationTime" : Timestamp(1652851761, 1)
}

对于单字段索引和排序操作,索引键的排序顺序(升序或降序)并不重要,因为MongoDB可以从任何方向遍历索引。

查询集合中已经存在的索引

  1. mongos> db.orders.getIndexes()
  2. [
  3. {
  4. "v" : 2,
  5. "key" : {
  6. "_id" : 1
  7. },
  8. "name" : "_id_"
  9. },
  10. {
  11. "v" : 2,
  12. "key" : {
  13. "_id" : "hashed"
  14. },
  15. "name" : "_id_hashed"
  16. },
  17. {
  18. "v" : 2,
  19. "key" : {
  20. "name" : 1
  21. },
  22. "name" : "name_1"
  23. }
  24. ]

创建一个复合索引

MongoDB支持在多个字段上创建用户定义索引,即 复合索引。

复合索引中列出的字段的顺序具有重要意义。如果一个复合索引由 {name: 1, age: -1} 组成,索引首先按name 升序排序,然后在每个name值内按 age 降序 排序。

  1. mongos> db.orders.createIndex({name:-1,size:1})
  2. mongos> db.orders.getIndexes()
  3. [
  4. {
  5. "v" : 2,
  6. "key" : {
  7. "_id" : 1
  8. },
  9. "name" : "_id_"
  10. },
  11. {
  12. "v" : 2,
  13. "key" : {
  14. "name" : 1
  15. },
  16. "name" : "name_1"
  17. },
  18. {
  19. "v" : 2,
  20. "key" : {
  21. "name" : -1,
  22. "size" : 1
  23. },
  24. "name" : "name_-1_size_1"
  25. }
  26. ]

对于复合索引和排序操作,索引键的排序顺序(升序或降序)可以决定索引是否支持排序操作。

创建多键索引

MongoDB使用多键索引来索引存储在数组中的内容。如果索引包含数组值的字段,MongoDB为数组的每个元素创建单独的索引项。数组字段中的每一个元素,都会在多键索引中创建一个键

db.collection.createIndex( { tags:1});

索引的效果解析

可以使用 explain 进行分析的操作包含 aggregate, count, distinct, find ,group, remove, update

winningPlan: stage 的值含义

COLLSCAN: 整个集合扫描

IXScan: 索引扫描

FETCH: 根据索引指向的文档的地址进行查询

SORT: 需要再内存中排序,效率不高

覆盖查询

       当查询条件和查询的<投影>只包含索引字段时,MongoDB直接从索引返回结果,而不扫描任何文档或将文档带入内存。这些覆盖的查询可能非常高效。

  1. mongos> db.orders.explain().find({ name:"Pepperoni"},{_id:0, name:1});
  2. {
  3. "queryPlanner" : {
  4. "mongosPlannerVersion" : 1,
  5. "winningPlan" : {
  6. "stage" : "SHARD_MERGE",
  7. "shards" : [
  8. {
  9. "shardName" : "shard1",
  10. "connectionString" : "shard1/node01:27010,node01:27011,node01:27012",
  11. "serverInfo" : {
  12. "host" : "node01",
  13. "port" : 27010,
  14. "version" : "5.0.8",
  15. "gitVersion" : "c87e1c23421bf79614baf500fda6622bd90f674e"
  16. },
  17. "namespace" : "order.orders",
  18. "indexFilterSet" : false,
  19. "parsedQuery" : {
  20. "name" : {
  21. "$eq" : "Pepperoni"
  22. }
  23. },
  24. "queryHash" : "3066FB64",
  25. "planCacheKey" : "066D32F9",
  26. "maxIndexedOrSolutionsReached" : false,
  27. "maxIndexedAndSolutionsReached" : false,
  28. "maxScansToExplodeReached" : false,
  29. "winningPlan" : {
  30. "stage" : "PROJECTION_SIMPLE",
  31. "transformBy" : {
  32. "_id" : 0,
  33. "name" : 1
  34. },
  35. "inputStage" : {
  36. "stage" : "SHARDING_FILTER",
  37. "inputStage" : {
  38. "stage" : "FETCH",
  39. "inputStage" : {
  40. "stage" : "IXSCAN",
  41. "keyPattern" : {
  42. "name" : 1
  43. },
  44. "indexName" : "name_1",
  45. "isMultiKey" : false,
  46. "multiKeyPaths" : {
  47. "name" : [ ]
  48. },
  49. "isUnique" : false,
  50. "isSparse" : false,
  51. "isPartial" : false,
  52. "indexVersion" : 2,
  53. "direction" : "forward",
  54. "indexBounds" : {
  55. "name" : [
  56. "[\"Pepperoni\", \"Pepperoni\"]"
  57. ]
  58. }
  59. }
  60. }
  61. }
  62. },
  63. "rejectedPlans" : [
  64. {
  65. "stage" : "PROJECTION_SIMPLE",
  66. "transformBy" : {
  67. "_id" : 0,
  68. "name" : 1
  69. },
  70. "inputStage" : {
  71. "stage" : "SHARDING_FILTER",
  72. "inputStage" : {
  73. "stage" : "FETCH",
  74. "inputStage" : {
  75. "stage" : "IXSCAN",
  76. "keyPattern" : {
  77. "name" : -1,
  78. "size" : 1
  79. },
  80. "indexName" : "name_-1_size_1",
  81. "isMultiKey" : false,
  82. "multiKeyPaths" : {
  83. "name" : [ ],
  84. "size" : [ ]
  85. },
  86. "isUnique" : false,
  87. "isSparse" : false,
  88. "isPartial" : false,
  89. "indexVersion" : 2,
  90. "direction" : "forward",
  91. "indexBounds" : {
  92. "name" : [
  93. "[\"Pepperoni\", \"Pepperoni\"]"
  94. ],
  95. "size" : [
  96. "[MinKey, MaxKey]"
  97. ]
  98. }
  99. }
  100. }
  101. }
  102. }
  103. ]
  104. },
  105. {
  106. "shardName" : "shard2",
  107. "connectionString" : "shard2/node01:28010,node01:28011,node01:28012",
  108. "serverInfo" : {
  109. "host" : "node01",
  110. "port" : 28010,
  111. "version" : "5.0.8",
  112. "gitVersion" : "c87e1c23421bf79614baf500fda6622bd90f674e"
  113. },
  114. "namespace" : "order.orders",
  115. "indexFilterSet" : false,
  116. "parsedQuery" : {
  117. "name" : {
  118. "$eq" : "Pepperoni"
  119. }
  120. },
  121. "queryHash" : "3066FB64",
  122. "planCacheKey" : "066D32F9",
  123. "maxIndexedOrSolutionsReached" : false,
  124. "maxIndexedAndSolutionsReached" : false,
  125. "maxScansToExplodeReached" : false,
  126. "winningPlan" : {
  127. "stage" : "PROJECTION_SIMPLE",
  128. "transformBy" : {
  129. "_id" : 0,
  130. "name" : 1
  131. },
  132. "inputStage" : {
  133. "stage" : "SHARDING_FILTER",
  134. "inputStage" : {
  135. "stage" : "FETCH",
  136. "inputStage" : {
  137. "stage" : "IXSCAN",
  138. "keyPattern" : {
  139. "name" : 1
  140. },
  141. "indexName" : "name_1",
  142. "isMultiKey" : false,
  143. "multiKeyPaths" : {
  144. "name" : [ ]
  145. },
  146. "isUnique" : false,
  147. "isSparse" : false,
  148. "isPartial" : false,
  149. "indexVersion" : 2,
  150. "direction" : "forward",
  151. "indexBounds" : {
  152. "name" : [
  153. "[\"Pepperoni\", \"Pepperoni\"]"
  154. ]
  155. }
  156. }
  157. }
  158. }
  159. },
  160. "rejectedPlans" : [
  161. {
  162. "stage" : "PROJECTION_SIMPLE",
  163. "transformBy" : {
  164. "_id" : 0,
  165. "name" : 1
  166. },
  167. "inputStage" : {
  168. "stage" : "SHARDING_FILTER",
  169. "inputStage" : {
  170. "stage" : "FETCH",
  171. "inputStage" : {
  172. "stage" : "IXSCAN",
  173. "keyPattern" : {
  174. "name" : -1,
  175. "size" : 1
  176. },
  177. "indexName" : "name_-1_size_1",
  178. "isMultiKey" : false,
  179. "multiKeyPaths" : {
  180. "name" : [ ],
  181. "size" : [ ]
  182. },
  183. "isUnique" : false,
  184. "isSparse" : false,
  185. "isPartial" : false,
  186. "indexVersion" : 2,
  187. "direction" : "forward",
  188. "indexBounds" : {
  189. "name" : [
  190. "[\"Pepperoni\", \"Pepperoni\"]"
  191. ],
  192. "size" : [
  193. "[MinKey, MaxKey]"
  194. ]
  195. }
  196. }
  197. }
  198. }
  199. }
  200. ]
  201. }
  202. ]
  203. }
  204. },
  205. "serverInfo" : {
  206. "host" : "node01",
  207. "port" : 4000,
  208. "version" : "5.0.8",
  209. "gitVersion" : "c87e1c23421bf79614baf500fda6622bd90f674e"
  210. },
  211. "serverParameters" : {
  212. "internalQueryFacetBufferSizeBytes" : 104857600,
  213. "internalQueryFacetMaxOutputDocSizeBytes" : 104857600,
  214. "internalLookupStageIntermediateDocumentMaxSizeBytes" : 104857600,
  215. "internalDocumentSourceGroupMaxMemoryBytes" : 104857600,
  216. "internalQueryMaxBlockingSortMemoryUsageBytes" : 104857600,
  217. "internalQueryProhibitBlockingMergeOnMongoS" : 0,
  218. "internalQueryMaxAddToSetBytes" : 104857600,
  219. "internalDocumentSourceSetWindowFieldsMaxMemoryBytes" : 104857600
  220. },
  221. "command" : {
  222. "find" : "orders",
  223. "filter" : {
  224. "name" : "Pepperoni"
  225. },
  226. "projection" : {
  227. "_id" : 0,
  228. "name" : 1
  229. },
  230. "lsid" : {
  231. "id" : UUID("de01c16c-67c9-480f-836d-4f3aa6cc6b75")
  232. },
  233. "$clusterTime" : {
  234. "clusterTime" : Timestamp(1652852181, 1),
  235. "signature" : {
  236. "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
  237. "keyId" : NumberLong(0)
  238. }
  239. },
  240. "$db" : "order"
  241. },
  242. "ok" : 1,
  243. "$clusterTime" : {
  244. "clusterTime" : Timestamp(1652852230, 2),
  245. "signature" : {
  246. "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
  247. "keyId" : NumberLong(0)
  248. }
  249. },
  250. "operationTime" : Timestamp(1652852224, 1)
  251. }

这时不需要 fetch, 可以直接从索引中获取数据。

db.orders.explain().find().sort( {name:1 ,size:-1}) ;

使用已创建索引的字段进行排序,能利用索引的顺序,不需要重新排序,效率高

db.orders.explain().find().sort( {name:1 ,size: 1}) ;

使用未创建索引的字段进行排序, 因为和创建索引时的顺序不一致,所以需要重新排序,效率低

如果需要更改某些字段上已经创建的索引,必须首先删除原有索引,再重新创建新索引,否则,新索引不会包含原有文档

db.collection.dropIndex()

使用索引名称删除索引

db.collection.dropIndex("name_1");

使用索引定义删除索引

db.collection.dropIndex({name:1,age:-1});

db.collection.createIndex(<keys>, <options>)

<options> 定义了创建索引时可以使用的一些参数,也可以指定索引的特性

索引的唯一性

     索引的unique属性使MongoDB拒绝索引字段的重复值。除了唯一性约束,唯一索引和MongoDB其他索引功能上是一致的

db.collection.createIndex({filed:1},{unique:true});

如果文档中的字段已经出现了重复值,则不可以创建该字段的唯一性索引

如果新增的文档不具备加了唯一索引的字段,则只有第一个缺失该字段的文档可以被添加,索引中该键值被置为null。

复合键索引也可以具有唯一性,这种情况下,不同的文档之间,其所包含的复合键字段值的组合不可以重复。

索引的稀疏性

    索引的稀疏属性可确保索引仅包含具有索引字段的文档的条目。索引会跳过没有索引字段的文档。可以将稀疏索引与唯一索引结合使用,以防止插入索引字段值重复的文档,并跳过索引缺少索引字段的文档。

准备数据:

db.sparsedemo.insertMany([{name:"xxx",age:19},{name:"zs",age:20}])

创建 唯一键,稀疏索引

db.sparsedemo.createIndex({name:1},{unique:true ,sparse:true});

如果同一个索引既具有唯一性,又具有稀疏性,就可以保存多篇缺失索引键值的文档了

db.sparsedemo.insertOne({name:"zs2w",age:20});

db.sparsedemo.insertOne({name:"zs2w2",age:20});

mongos> db.sparsedemo.insertOne({name:"zs2w2",age:20});
WriteError({
        "index" : 0,
        "code" : 11000,
        "errmsg" : "E11000 duplicate key error collection: order.sparsedemo index: name_1 dup key: { name: \"zs2w2\" }",
        "op" : {
                "_id" : ObjectId("628487cd59656175ee81a817"),
                "name" : "zs2w2",
                "age" : 20
        }
}) :

说明:如果只单纯的 唯一键索引,则 缺失索引键的字段,只能有一个

复合键索引也可以具有稀疏性,在这种情况下,只有在缺失复合键所包含的所有字段的情况下,文档才不会被加入到索引中。

索引的生存时间

针对日期字段,或者包含了日期元素的数组字段,可以使用设定了生存时间的索引,来自动删除字段值超过生存时间的文档。

构造数据:

db.ttl.insertMany( [ { name:"zhangsanss", age:19,tags:["00","It","SH"], create_time:new Date()} ] ); 

db.ttl.createIndex({ create_time: 1},{expireAfterSeconds:30 });

mongos> db.ttl.insertMany( [ { name:"zhangsanss", age:19,tags:["00","It","SH"], create_time:new Date()},{name:"lisi","age":20,tags:["222","苹果"]} ] );

mongos> db.ttl.find()
{ "_id" : ObjectId("6284891159656175ee81a81a"), "name" : "zhangsanss", "age" : 19, "tags" : [ "00", "It", "SH" ], "create_time" : ISODate("2022-05-18T05:50:09.818Z") }
{ "_id" : ObjectId("6284891159656175ee81a81b"), "name" : "lisi", "age" : 20, "tags" : [ "222", "苹果" ] }

30秒后,这条记录就会被删除

mongos> db.ttl.find()
{ "_id" : ObjectId("6284891159656175ee81a81b"), "name" : "lisi", "age" : 20, "tags" : [ "222", "苹果" ] }

在create_time字段上面创建了一个生存时间是30s的索引

  1. mongos> db.ttl.getIndexes()
  2. [
  3. {
  4. "v" : 2,
  5. "key" : {
  6. "_id" : 1
  7. },
  8. "name" : "_id_"
  9. },
  10. {
  11. "v" : 2,
  12. "key" : {
  13. "create_time" : 1
  14. },
  15. "name" : "create_time_1",
  16. "expireAfterSeconds" : 30
  17. }
  18. ]

复合键索引不具备生存时间的特性

      当索引键是包含日期元素的数组字段时,数组中最小的日期将被用来计算文档是否已经过期

数据库使用一个后台线程来监测和删除过期的文档,删除操作可能会有一定的延迟

Compound Indexes — MongoDB Manual

$addFields (aggregation) — MongoDB Manual

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/648292
推荐阅读
相关标签
  

闽ICP备14008679号