写点什么

MongoDB 官方文档笔记之索引 Indexes

作者:菜皮日记
  • 2023-09-09
    北京
  • 本文字数:3849 字

    阅读完需:约 13 分钟

本文是在阅读 MongoDB 官方文档时记录的一些主要概念,更多细节可以查看文中的参考链接。

查看当前 db 索引

db.COLLECTION_NAME.getIndexes()
[  { "v" : 1, "key" : {   "_id" : 1 }, "name" : "_id_", "ns" : "newDB.sites"  },  { "v" : 1, "key" : {   "name" : 1,   "domain" : -1 }, "name" : "name_1_domain_-1", "ns" : "newDB.sites"  }]
复制代码

单字段索引 single field

在单个字段上创建索引,1 表示升序,-1 表示降序。

对于单字段索引来说,指定升序降序无关紧要,查询时选择升序降序性能是一样的。

但对于复合索引来说,查询需要符合索引的顺序才能走上索引。

# 创建索引db.records.createIndex( { score: 1 } )
# 查询使用索引db.records.find( { score: 2 } )db.records.find( { score: { $gt: 10 } } )
复制代码

嵌入式文档的内部字段上的索引

什么是嵌入式字段?就是这个字段的值也是一个文档。MongoDB 支持在嵌入式文档的内部字段上建立索引

# location 是一个文档{  "_id": ObjectId("570c04a4ad233577f97dc459"),  "score": 1034,  "location": { state: "NY", city: "New York" }}
# 在location下面的state字段上创建索引db.records.createIndex( { "location.state": 1 } )
# 查询db.records.find( { "location.state": "CA" } )db.records.find( { "location.city": "Albany", "location.state": "NY" } )
复制代码

在嵌入式文档本身建立的索引

还拿上面例子来讲,可以直接在 location 上面建立索引

{  "_id": ObjectId("570c04a4ad233577f97dc459"),  "score": 1034,  "location": { state: "NY", city: "New York" }}
# 直接在location上建立索引db.records.createIndex( { location: 1 } )
# 使用索引db.records.find( { location: { city: "New York", state: "NY" } } )
复制代码

复合索引 compound

在多个字段上建立的索引,需分别指定每个字段的升降序规则。查询时需要符合升降序规则或符合反向规则,才能走上索引。

复合索引最多只能支持 32 个字段。

复合索引支持前缀查询

# 文档结构{ "_id": ObjectId(...), "item": "Banana", "category": ["food", "produce", "grocery"], "location": "4th Street Store", "stock": 4, "type": "cases"}
# 建立索引 以item升序 且 stock升序简历索引。含义是,当文档的item值相同时,按stock升序排序db.products.createIndex( { "item": 1, "stock": 1 } )
# 复合索引也支持前缀查询db.products.find( { item: "Banana" } )db.products.find( { item: "Banana", stock: { $gt: 5 } } )
复制代码

复合索引的索引顺序

索引以升序(1)或降序(-1)排序顺序存储对字段的引用。对于单字段索引,键的排序顺序无关紧要,因为 MongoDB 可以在任一方向上遍历索引。但是,对于复合索引,属性的顺序决定了索引是否支持结果集的排序。

# 假设符合索引如下db.events.createIndex( { "username" : 1, "date" : -1 } )
# 这个索引支持如下两种查询db.events.find().sort( { username: 1, date: -1 } )db.events.find().sort( { username: -1, date: 1 } )
# 但不支持下面的查询db.events.find().sort( { username: 1, date: 1 } )
# 即只能从索引一头儿查询才能走上索引
复制代码

复合索引中的前缀索引

# 假设有如下复合索引{ "item": 1, "location": 1, "stock": 1 }
# 则可支持下面两种前缀索引查询{ item: 1 }{ item: 1, location: 1 }
# 同时也支持前缀的部分匹配,即如下查询可用上item字段的前缀索引{ item: 1, stock: 1 }
复制代码

多键索引 multikey

支持对数组元素内的字段做索引。

# 如果field 是一个数组,则自动建立多键索引,无需特殊指定db.coll.createIndex( { <field>: < 1 or -1 > } )
复制代码

但 mongodb 限制不可以在过个数组上做复合索引

# 下面这种情况,不可以在 {a:1, b:1} 上做索引{ _id: 1, a: [ 1, 2 ], b: [ 1, 2 ], category: "AB - both arrays" }
复制代码

文本索引 text

MongoDB 提供文本索引以支持对字符串内容的文本搜索查询。text 索引可以包含任何值为字符串或字符串元素数组的字段。

# 在comments字段上建立文本索引db.reviews.createIndex( { comments: "text" } )
# 在多个字段上建立text索引db.reviews.createIndex(   {     subject: "text",     comments: "text"   } )
复制代码

文本索引还可以设置支持的语言、控制相关性分数权重排序、限制条目等,更多查看 https://www.mongodb.com/docs/manual/core/index-text/

通配符索引

有时查询的维度可能不固定,会按照多个字段去查询

# 数据文档{ "userMetadata" : { "likes" : [ "dogs", "cats" ] } }{ "userMetadata" : { "dislikes" : "pickles" } }{ "userMetadata" : { "age" : 45 } }{ "userMetadata" : "inactive" }
# 可以建立通配符索引db.userData.createIndex( { "userMetadata.$**" : 1 } )
# 支持以下查询db.userData.find({ "userMetadata.likes" : "dogs" })db.userData.find({ "userMetadata.dislikes" : "pickles" })db.userData.find({ "userMetadata.age" : { $gt : 30 } })db.userData.find({ "userMetadata" : "inactive" })
复制代码

https://www.mongodb.com/docs/manual/core/index-wildcard/

2dsphere 和 2d 索引

分别是对计算类似地球的球体上的几何形状的查询的索引,和对存储为二维平面上的点的数据使用 2d 索引

https://www.mongodb.com/docs/manual/core/2dsphere/

https://www.mongodb.com/docs/manual/core/2d/

索引特性

unique 唯一索引

唯一索引确保索引字段不会存储重复值,默认情况下,MongoDB 在创建集合期间在 _id 字段上创建一个唯一的索引。

# 单字段上创建唯一索引db.members.createIndex( { "user_id": 1 }, { unique: true } ) 
# 多字段上复合唯一索引db.members.createIndex( { groupNumber: 1, lastname: 1, firstname: 1 }, { unique: true } )
# 数组类型的多键索引# 文档结构如下:{ _id: 1, a: [ { loc: "A", qty: 5 }, { qty: 10 } ] }# 建立索引db.collection.createIndex( { "a.loc": 1, "a.qty": 1 }, { unique: true } )

复制代码

partial 部分索引

只对满足指定筛选器表达式的文档做索引,这样索引量更少,性能更高。

# 对rating>5的文档做索引db.restaurants.createIndex(   { cuisine: 1, name: 1 },   { partialFilterExpression: { rating: { $gt: 5 } } })
复制代码

sparse 稀疏索引

只对含有索引字段的文档建立索引,字段值是空值也建立。

与之对应的非稀疏索引,则是会对空缺字段存储空值。

由于稀疏索引中有部分文档未被计入索引中,所以在稀疏索引上 count 查询不准确

db.addresses.createIndex( { "xmpp_id": 1 }, { sparse: true } )
复制代码

其他索引特性:

  • ttl 有效期索引

  • 不区分大小写索引

索引交叉 Index Intersection

指同时利用多个索引

# 两个独立索引{ qty: 1 }{ status: 1, ord_date: -1 }
# 下面的查询可以同时利用两个索引,也就是索引交集db.orders.find( { qty: { $gt: 10 } , status: "A" } )
复制代码

索引交集跟复合索引很像:复合索引性能更高,要求更严格。索引交叉性能差一些,但更灵活。

# 复合索引如下{ status: 1, ord_date: -1 }
# 复合索引支持如下查询db.orders.find( { status: { $in: ["A", "P" ] } } )db.orders.find(   {     ord_date: { $gt: new Date("2014-02-01") },     status: {$in:[ "P", "A" ] }   })
# 但无法支持下面的查询db.orders.find( { ord_date: { $gt: new Date("2014-02-01") } } )db.orders.find( { } ).sort( { ord_date: 1 } )
# 如果建立两个独立的索引,则可以用到这两个索引的索引交集{ status: 1 }{ ord_date: -1 }
复制代码

但有一种情况不能走索引交叉:当查询需要排序时,而排序字段不在查询谓词(查询字段)中时,无法利用索引交集。

换句话说,只有在查询谓词中的字段能够组成索引交集的,才能利用索引交集。

# 有如下四种索引{ qty: 1 }{ status: 1, ord_date: -1 }{ status: 1 }{ ord_date: -1 }
# 这条无法利用索引交集,查询中只有qty,而按status排序db.orders.find( { qty: { $gt: 10 } } ).sort( { status: 1 } )
# 这条可以利用,查询中qty和 status+ord_date组成了索引交集db.orders.find( { qty: { $gt: 10 } , status: "A" } ).sort( { ord_date: -1 } )
复制代码

业务中使用复合索引还是索引交集,需要取决于系统具体情况。

管理索引,索引的增删查改

https://www.mongodb.com/docs/manual/tutorial/manage-indexes/

衡量索引的使用情况

  • db.collection.explain() 查看执行计划

  • db.people.find({ name: "John Doe", zipcode: { $gt: "63000" } }).hint( { zipcode: 1 } ) 强制走某索引

索引策略:索引的使用和排序

字符串比较时,需要考虑字符串的字符集,需要与建立索引时一致才能走索引。

排序时,也符合前缀匹配

复合索引支持正模式与反模式

用户头像

菜皮日记

关注

全干程序员 2018-08-08 加入

还未添加个人简介

评论

发布
暂无评论
MongoDB 官方文档笔记之索引 Indexes_mongodb_菜皮日记_InfoQ写作社区