MongoDB—索引
Index
定义
索引,一个单独的、存储在磁盘上的数据结构
mongodb 的索引采用 B-tree 数据结构存储
-
易于遍历,支持相等匹配和范围查询
-
存储字段的值以及指向其所在文档的指针
包含集合中所有文档的指针(包含数据表中所有记录的引用指针)
-
按字段的值排序
mongodb index
- 在集合级别定义索引,支持在文档中的任何字段或子字段上建立索引
- 给某个字段添加索引,可以快速找出在该字段有特定值的文档,提高查询速度
- 如果没有索引,mongodb必须扫描集合中所有的文档,以选择那些符合查询条件的文档。一般而言,在查询文档时应避免扫描全部文档,这样会使查询速度非常慢
- 建立索引可以缩小mongodb扫描文档的数量,提高查询速度
- mongodb 可以返回基于索引排序的查询结果
演示基于索引查询并排序
_id
- mongodb 默认会为文档创建一个
_id
字段,并建立唯一索引 - 不能删除建立在
_id
字段上的索引 - 索引名称为
_id_
创建索引
db.collection.createIndex
-
为集合创建索引
-
如果索引(名称)已经存在,则不会再次创建
-
除 collation 选项外,使用 不同的选项 不会创建 具有相同索引规范的 索引,也不会修改这些选项的值
只能删除之前的索引,重新使用新的选项创建
-
使用不同的 collation 选项,可以创建 多个 具有相同的索引规范的 索引,但是需要指定 唯一的 索引名称
db.person.createIndex({name:1},{name:"a_c", collation:{locale:"fr"}}) db.person.createIndex({name:1},{name:"a_b", collation:{locale:"fa"}})
-
如果文档不包含指定的字段,则操作失败
格式
db.collection.createIndex(keys, options)
-
keys
- 类型:Document
- 描述:
-
包含字段和值的文档,其中字段是索引键,值是该字段的索引类型
判断索引是否相同根据索引的 keys,因此 keys 不同就代表索引不同
-
用下划线连接组成默认索引名称
-
同一个字段上只能创建一个相同类型的索引,可以创建多个不同类型的索引
例如 {age:1} {age:-1} 两个keys不同,索引(名)不同
-
-
索引类型包括:test、geospatial、hashed、ascending、descending
排序规则
- 值为 1,表示升序(ascending)
- 值为 -1,表示降序(descending)
-
-
options
-
类型:Document
-
描述:创建索引的选项,不同类型的索引有特定于该类型的选项,以下为共有选项
-
background
-
类型:布尔【4.2版本中被废弃】
-
描述:是否在后台执行创建索引的过程,不阻塞对集合的操作
-
false【默认】
不在后台创建
-
true
后台创建
-
-
4.2 版本中所有的索引构建,使用一个优化的构建过程,它只在构建过程的开始和结束时持有独占锁,构建过程中让步于读写操作
4.2 版本会忽略该选项
-
4.2 版本之前索引的构建的整个过程都会持有独占锁,阻塞数据库及其所有集合上的操作,直到操作完成,在后台创建索引不会持有独占锁
-
-
unique
-
类型:布尔
-
描述:
-
是否创建具有唯一性的索引
-
true
此索引具有唯一性,当索引重复时,不接受对文档的插入或更新
-
false【默认】
-
-
不适用于 hashed 索引
-
-
-
name
-
类型:字符串
-
描述
-
自定义索引名称
-
如果不指定,mongodb将通过 下划线 连接 索引字段的名称和排序规则 生成一个索引名称
例如在 { item : 1, quantity: -1 } 上创建的索引名称为 item_1_quantity_-1
-
一旦创建不能修改,只能删除再重新创建
-
4.2 版本之前,索引名最大长度为 127 字节,4.2版本开始取消该限制
-
-
partialFilterExpression
-
类型:Document
-
描述:仅为集合中符合条件的文档建立索引,降低创建和维护成本
-
-
sparse
[spɑrs] 稀疏
-
类型:布尔
-
描述:仅为集合中具有指定字段的文档建立索引
-
true
建立稀疏索引
-
false 【默认】
zdsphere、2d、geoHaystack、text 默认为稀疏文档,忽略此选项
-
-
-
expireAfterSeconds
- 类型:integer,单位 秒
- 描述:用于 TTL 索引中 控制 文档保存在集合中的时间
-
storageEngine
- 类型:Document
- 描述:指定存储引擎配置
-
-
Returns
-
成功
{ "createdCollectionAutomatically" : false, "numIndexesBefore" : 3, "numIndexesAfter" : 4, "ok" : 1 }
- createdCollectionAutomatically 是否自动创建集合
- numIndexesBefore 创建之前索引个数
- numIndexesAfter 创建之后索引个数
- ok:1 创建成功
-
失败
{ "ok" : 0, "errmsg" : "Error: hashed indexes do not currently support array values", "code" : 16766, "codeName" : "Location16766" }
- ok:0 创建失败
- errmsg 失败信息
- code 失败码
- codeName ?
查询索引
db.collection.getIndexes
-
查询集合中所有的索引信息
-
返回一个数组,数组元素是描述索引信息的文档
索引信息包括创建索引时的信息:keys 以及 options
格式
db.collection.getIndexes()
db.collection.totalIndexSize
- 查询集合中所有索引的大小
- 如果索引使用前缀压缩 (这是WiredTiger的默认值),则返回压缩后的大小
- 单位 字节 byte
格式
db.collection.totalIndexSize()
删除索引
db.collection.dropIndex
- 删除指定索引
- 不能删除默认创建在
_id
字段上的索引
格式
db.collection.dropIndex(index)
-
index
-
类型:字符串、文档
-
描述:指定要删除的索引
-
可以指定索引的名称 或 索引规范文档
通过
db.collection.getIndexes()
获取索引名称 -
删除 text 类型的索引,必须指定索引名称
无法通过索引规范文档找到索引,删除失败
-
4.2 版本开始,不能通过
db.collection.dropIndex("*")
删除所有非_id
字段上的索引,应该使用db.collection.dropIndexes()
-
-
排它锁
- 4.2 版本之前,dropIndex 操作在父级数据库上获取一个排它锁,阻塞所有对数据库及其所有集合的操作,直到操作完成
- 4.2 版本之后,dropIndex 操作在指定集合上获取一个排它锁,阻塞对集合的所有后续操作
示例
-
通过索引名称删除
db.pets.dropIndex( "cat_-1" )
-
通过索引规范文档删除
db.pets.dropIndex( { "cat" : -1 } )
de.collection.dropIndexes
- 删除单个或多个索引
- 不能删除默认创建在
_id
字段上的索引
格式
db.collection.dropIndex(indexes)
-
indexes
-
类型:字符串、文档、字符串数组
-
描述:需要删除的索引
-
删除
_id
之外的所有索引,忽略该参数 -
删除单个索引,指定索引的名称或索引规范文档,text 类型的索引只能指定名称
-
删除多个索引,通过数组指定索引的名称
如果数组中包含不存在的索引,则不删除任何索引,操作报错
4.2 新增
-
-
排它锁
- 4.2 版本之前,dropIndex 操作在父级数据库上获取一个排它锁,阻塞所有对数据库及其所有集合的操作,直到操作完成
- 4.2 版本之后,dropIndex 操作在指定集合上获取一个排它锁,阻塞对集合的所有后续操作
示例
-
删除
_id
之外的所有索引db.collection.dropIndexes()
-
删除单个索引
db.collection.dropIndexes( { a: 1, b: 1 } ) db.collection.dropIndexes( "a_1_b_1" )
-
删除多个索引
db.collection.dropIndexes( [ "a_1_b_1", "a_1", "a_1__id_-1" ] )
索引类型
Single Field 索引
- 单字段索引
- 对于单字段索引,升序降序并不重要,因为mongodb可以按任意方向遍历索引
示例
db.collection.createIndex( { orderDate: 1 } )
- 在 orderDate 字段上创建一个升序索引
- 默认名称
orderDate_1
Compound 索引
-
复合索引
-
将多个字段组合成一个索引
-
创建复合索引时,字段的顺序非常重要
-
整体上按第一个字段进行排序,对于按第一个字段排序并列的索引,再使用第二个字段进行排序
-
查询条件中必须包含索引的前缀字段
例如以3个字段组成的索引,查询条件中需包含第一个字段 或 第一个及第二个字段 或 全部三个字段
-
复合索引中不能包含 hashed 索引
示例
db.collection.createIndex( { orderDate: 1, zipcode: -1 } )
- 创建一个在 orderDate 字段进行升序排列,在 zipcode 字段进行降序排列的复合索引
Multikey 索引
- 多键索引
- 使用点表示法,对嵌套数组中的字段建立索引
- mongodb会为数组中的每个元素创建单独的 索引项
- 允许查询条件匹配数组的一个或多个元素来选择符合条件的文档
- 如果索引字段包含数组元素,mongodb会自动创建一个多键索引
Geospatial 索引
- 地理空间索引
Text 索引
- 文本索引:对字符串内容进行查询
- 一般在 字符串类型字段 或 字符串数组类型字段 上创建文本索引
- 使用文本索引查找时,不区分字母大小写
- 目前不支持中文文本索引,支持英语、法语、德语、俄语、西班牙语、土耳其语
- 文本索引具有 sparse 属性,忽略创建过程中的 sparse 选项
- 文本索引支持
$text
查询操作 - 限制
- 一个集合最多只能创建一个文档索引
- 排序操作 sort,不能使用文本索引中的排序
- 复合文本索引中,如果在文本索引键之前包含其他键,则通过 $text 搜索时,查询谓词必须包含前面键的相等匹配条件
- 只能通过索引名称删除文本索引
格式
db.collection.createIndex(
keys ,
options
)
-
keys
-
类型:Document
-
描述:text 类型的索引规范文档
-
单字段
{ field1: "text"}
-
多字段
{ field1: "text", field2: "text", ... } // 复合索引 { field1: 1, field2: "text", ... }
-
全部字段
用
$**
代表文档中的所有字符串类型的字段{ "$**": "text" }
-
-
-
options
- weights
- 类型:Document
- 描述:
- 包含字段和权重的文档,指定字段的权重,默认值为1,可设定为 1-99999 之间的整数
- 优先查询权重大的字段,次之查询权重小的字段
- default_language
- 类型:字符串
- 描述
- 对于文本索引,不同的语言有不同的分析规则
- 默认值为
english
- language_override
- 类型:字符串
- 描述
- For text indexes, the name of the field, in the collection’s documents, that contains the override language for the document
- 默认值为
language
- textIndexVersion
- 类型:integer
- 描述:指定文本索引的版本
- weights
示例
-
创建文本索引
db.reviews.createIndex( { comments: "text" } ) db.reviews.createIndex( { subject: "text", comments: "text" } ) db.reviews.createIndex( { "$**": "text" } )
-
指定不同的权重
db.reviews.createIndex( { "$**": "text" } , { weights : {subject: 10, comments: 5}} )
- 索引名
$**_text
- 索引名
Hashed 索引
-
哈希索引:使用字段值的哈希值来创建索引
-
主要用在分片的片键上
-
支持 非数组 单字段 索引,不支持多字段索引
多字段索引报错
db.articles.createIndex({subject:"hashed",author:"hashed"})
- "errmsg" : "Currently only single field hashed index supported."
数组字段索引报错
db.articles.createIndex({arr:"hashed"})
-
"errmsg" : "Error: hashed indexes do not currently support array values",
-
creating a hashed index on a field that contains an array or attempting to insert an array into a hashed indexed field returns an error.
-
不能设定唯一性约束,因为存在hash碰撞
db.articles.createIndex({subject:"hashed"},{unique:true})
- "errmsg" : "Currently hashed indexes cannot guarantee uniqueness. Use a regular index."
-
创建哈希索引的字段,也可以同时创建其他索引
不支持创建复合索引,但是同一个字段可以创建多个不同类型的索引
db.articles.createIndex({subject:"hashed"}) db.articles.createIndex({subject:"hashed",subject:"text"}) // 只会创建一个文本索引 db.articles.createIndex({subject:"hashed",subject:1}) // 只会创建一个升序索引 // 最终在 subject 字段上创建了3个不同类型的索引
-
哈希索引支持相等查询,不支持范围查询
-
在使用哈希索引查询时,mongodb 会自动计算,无需单独计算
4.0 版本开始,mongodb提供了
convertShardKeyToHashed()
方法,该方法使用相同的哈希计算方法,可用于查看键的哈希值 -
类型为浮点数的字段不能创建哈希索引
mongodb 哈希索引在计算之前会将浮点数截断为64位的整数,例如 2.3、 2.2、 2.9 都被截断为2进行计算
格式
db.collection.createIndex( { field: "hashed" } )
索引属性
Unique 索引
- 唯一性
Partial 索引
- 局部性
Sparse 索引
- 稀疏性
TTL 索引
-
生命周期性(文档在一段时间后会被mongodb自动删除)
-
一般在 Date类型的字段 或 包含Date类型元素的数组字段 上创建TTL索引,其他字段无效
-
过期阈值的计算
- 如果字段类型是 Date,则过期阈值是字段值日期加上指定的过期时间
- 如果字段类型是包含 Date 类型元素的数组,则过期阈值是元素中最早的日期加上指定的过期时间
- 如果非以上两种类型,TTL 索引无效,文档不会过期
-
将
expireAfterSeconds
设置为0,则过期时间就是日期字段的值 -
TTL 线程
- mongod 中的后台线程,每隔60秒运行一次,用来读取索引中的值并从集合中删除过期的文档
- 不保证文档过期后立即被删除,文档过期的时间和从数据库中删除的时间存在延迟(60s)
- 当 TTL 线程处于活动状态时,可以在
db.currentOp()
的输出中看到删除操作
-
限制
-
不能在
_id
字段上建立 TTL 索引 -
不能通过改变 options 将非 TTL 索引变为 TTL 索引
-
不能在固定集合中建立 TTL 索引,因为mongodb不能从固定集合中删除文档
-
不能是复合索引,只能是单字段索引
-
格式
db.collection.createIndex(
<keys>,
{
expireAfterSeconds: <integer>
}
)
示例
文档
db.ttl.insertMany([
{date:new Date("2020-08-13T17:50:00"), money:1},
{date:new Date("2020-08-13T17:52:00"), money:2},
{date:new Date("2020-08-13T17:54:00"), money:3},
])
db.ttl.createIndex({date:-1})
db.ttl.createIndex({date:1, money:-1})
db.ttl.createIndex({date:1},{expireAfterSeconds:10})
- 在 ttl 集合中的 date 字段上创建3个索引
- 有一个 TTL 索引,过期阈值为:date 日期加上指定过期时间
- 结果是,到过期阈值时,文档被依次删除
修改过期时间
db.runCommand({
collMod: <collection>,
index:{
keyPattern: <keys>,
expireAfterSeconds: <integer>
}
})
示例
db.ttl.insertMany([
{date:new Date("2020-08-13T18:08:00"), money:1},
{date:new Date("2020-08-13T18:09:00"), money:2},
{date:new Date("2020-08-13T18:10:00"), money:3},
])
db.runCommand({
collMod:"ttl",
index:{
keyPattern:{date:1},
expireAfterSeconds:60*60
}
})
返回结果
{ "expireAfterSeconds_old" : 10, "expireAfterSeconds_new" : 3600, "ok" : 1 }
Hidden 索引
4.4 新增
- 对查询规划器不可见,不能用于查询
- 用来评估删除索引的潜在影响,而不必实际删除该索引。如果影响是负面的,用户可以取消隐藏索引,而不必重新创建
- 索引在隐藏时也会被维护,因此一旦不隐藏,就可以立即使用这些索引