首页 > 技术文章 > MongoDB—索引

usmile 2020-08-30 10:29 原文

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
      • 描述:指定文本索引的版本

示例

  • 创建文本索引

    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 新增

  • 对查询规划器不可见,不能用于查询
  • 用来评估删除索引的潜在影响,而不必实际删除该索引。如果影响是负面的,用户可以取消隐藏索引,而不必重新创建
  • 索引在隐藏时也会被维护,因此一旦不隐藏,就可以立即使用这些索引

推荐阅读