15、MongoDB实战:MongoDB的文档操作-MongoDB索引-6800字匠心出品

MongoDB 的文档操作-MongoDB 索引

    • 1.创建索引
  • 2.查看索引

    • 2.1 查看集合索引
    • 2.2 查看索引键
    • 2.3 查看索引大小
  • 3.修改索引

  • 4.删除索引

    • 4.1 删除集合中的指定索引
    • 4.2 删除集合中的全部索引
  • 5.重建索引

  • 6.MongoDB 中的索引类型

    • 6.1 单字段索引(Single Field Index)
    • 6.2 交叉索引
    • 6.3 复合索引(Compound Index)
    • 6.4 多 key 索引 (Multikey Index)
  • 7.索引额外属性

    • 7.1 唯一索引 (unique index)
    • 7.2 部分索引 (partial index)
    • 7.3 稀疏索引(sparse index)
  • 8.覆盖索引查询

  • 9.查询计划

  • 10.使用索引注意事项

    • 10.1 如何创建合适的索引
      • 10.1.1建立合适的索引
      • 10.1.2 复合索引的字段排列顺序
      • 10.1.3 查询时尽可能仅查询出索引字段
      • 10.1.4 对现有的数据大表建立索引的时候,采用后台运行方式
  • 11.索引限制

    • 11.1 额外开销
    • 11.2 内存使用
    • 11.3 查询限制
    • 11.4 最大范围
  • 12.正则查询

  • 索引通常能够极大的提高查询的效率,如果没有索引,MongoDB 在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。

  • 这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对系统的性能是非常致命的。

  • 索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构

1.创建索引

  • 在 MongoDB 中会自动为文档中的_Id(文档的主键)键创建索引,与关系型数据的主键索引类似。
  • 我们可以使用 createIndex()函数来为其他的键创建索引。在创建索引时需要指定排序规则。1 按照升序规则创建索引,-1 按照降序规则创建索引。
  • 在创建索引时,需要使用具有 dbAdmin 或者 dbAdminAnyDatabase 角色的用户。
  • 语法格式:db.COLLECTION_NAME.createIndex({创建索引的键:排序规则,…},{创建索引的参数(可选参数)})
  • 参数说明
    *
  • 为 dev 集合中的 title 键创建索引,并让创建工作在后台运行
    *

2.查看索引

2.1 查看集合索引

  • 我们可以通过 getIndexes()或者 getIndexSpecs()函数查看集合中的所有索引信息
  • 语法格式:db.COLLECTION_NAME.getIndexse()
  • 语法格式:db.COLLECTION_NAME.getIndexSpecs()
  • 使用 getIndexes()函数查看当前 dev 集合中的索引
    *
  • 使用 getIndexSpecs()函数查看当前 dev 集合中的索引
    *

2.2 查看索引键

  • 我们可以通过使用 getIndexKeys()函数查看集合的索引键。
  • 语法格式:db.COLLECTION_NAME.getIndexKeys();
  • 查看 dev 集合中的索引键
    *

2.3 查看索引大小

  • 我们可以通过 totalIndexSize()函数来查看当前集合中索引的大小,单位为字节。
  • 语法格式:db.COLLECTION_NAME.totalIndexSize(detail)
  • 参数解释:detail 可选参数,传入除 0 或 false 外的任意数据,那么会显示该集合中每个索引的大小及集合中索引的总大小。如果传入 0 或 false 则只显示该集合中所有索引的总大小。默认值为 false。
  • 查看 dev 集合中所有索引的总大小
    *
  • 查看 dev 集合中的每个索引的大小以及总大小
    *

3.修改索引

  • MongoDB 没有单独的修改索引函数,如果要修改某个索引,需要先删除旧的索引,再创建新的索引。

4.删除索引

4.1 删除集合中的指定索引

  • 我们可以通过 dropIndex()函数来删除指定索引
  • 语法格式:db.COLLECTION_NAME.dropIndex(“索引名称”)。
  • 删除 title 键的索引
    *

4.2 删除集合中的全部索引

  • 我们可以使用 dropIndexes()函数删除集合中的全部索引,_id 键的索引除外。
  • 语法格式:db.COLLECTION_NAME.dropIndexes()
    *

5.重建索引

  • 我可以使用 reIndex()函数重建索引。重建索引可以减少索引存储空间,减少索引碎片,优化索引查询效率。一般在数据大量变化后,会使用重建索引来提升索引性能。重建索引是删除原索引重新创建的过程,不建议反复使用。
  • 语法格式:db.COLLECTION_NAME.reIndex()
    *

6.MongoDB 中的索引类型

  • 在 MongoDB 中支持多种类型的索引,包括单字段索引、复合索引、多 key 索引、文本索引等,每种类型的索引有不同的使用场合。

6.1 单字段索引(Single Field Index)

  • 所谓单字段索引是指在索引中只包含了一个键。查询时,可加速对该字段的各种查询请求,是最常见的索引形式。MongoDB 默认创建的_Id 索引也是这种类型。我们可以使用createIndexes({索引键:排序规则})函数来创建单字段索引。
  • 语法格式:db.COLLECTION_NAME.createIndexes({索引键名:排序规则})
  • 为 dev 集合中的 title 键创建单字段索引
    *

6.2 交叉索引

  • 所谓交叉索引就是为一个集合的多个字段分别建立索引,在查询的时候通过多个字段作为查询条件,这种情况称为交叉索引。
  • 在查询文档时,在查询条件中包含一个交叉索引键或者在一次查询中使用多个交叉索引键作为查询条件都会触发交叉索引
  • 为 dev 集合中的 size 键创建交叉索引。
    *

6.3 复合索引(Compound Index)

  • 复合索引是 Single Field Index 的升级版本,它针对多个字段联合创建索引,先按第一个字段排序,第一个字段相同的文档按第二个字段排序,依次类推。
  • 语法格式:db.COLLECTION_NAME.createIndex({索引键名:排序规则, 索引键名:排序规则,…});
  • 复合索引能满足的查询场景比单字段索引更丰富,不光能满足多个字段组合起来的查询,也能满足所以能匹配符合索引前缀的查询。
  • 删除 dev 中的交叉索引
    *
  • 创建 title 与 size 的复合索引
    *
  • 查看索引
    *

6.4 多 key 索引 (Multikey Index)

  • 当索引的字段为数组时,创建出的索引称为多 key 索引,多 key 索引会为数组的每个元
    素建立一条索引。
  • 语法格式:db.COLLECTION_NAME.createIndex({数组键名:排序规则})
  • 为 dev 集合中 tags 键创建多 Key 索引
    *
  • 查看索引
    *

7.索引额外属性

  • MongoDB 除了支持多种不同类型的索引,还能对索引定制一些特殊的属性。

7.1 唯一索引 (unique index)

  • 唯一索引会保证索引对应的键不会出现相同的值,比如_id 索引就是唯一索引
  • 语法格式:db.COLLECTION_NAME.createIndex({索引键名:排序规则},{unique:true})
  • 如果唯一索引所在字段有重复数据写入时,抛出异常。
  • 删除 dev 集合中的索引。为 dev 集合中的 title 键建立唯一索引
    *
  • 插入 title 相同的值测试唯一索引
    *

7.2 部分索引 (partial index)

  • 部分索引是只针对符合某个特定条件的文档建立索引,3.2 版本才支持该特性。
  • MongoDB 部分索引只为那些在一个集合中,满足指定的筛选条件的文档创建索引。由于部分索引是一个集合文档的一个子集,因此部分索引具有较低的存储需求,并降低了索引创建和维护的性能成本。部分索引通过指定过滤条件来创建,可以为 MongoDB 支持的所有索引类型使用部分索引。
  • 简单点说:部分索引就是带有过滤条件的索引,即索引只存在与某些文档之上
  • 语 法 格 式 : db.COLLECTION_NAME.createIndex({ 索引键名 : 排 序 规则},{partialFilterExpression:{键名:{匹配条件:条件值}}})
  • 为 dev 集合中的 size 键创建部分索引。条件为大于 300
    *
  • 查看索引
    *
  • 注意:部分索引只为集合中那些满足指定的筛选条件的文档创建索引。如果你指定的partialFilterExpression 和唯一约束、那么唯一性约束只适用于满足筛选条件的文档。具有唯一约束的部分索引不会阻止不符合唯一约束且不符合过滤条件的文档的插入

7.3 稀疏索引(sparse index)

  • 稀疏索引仅包含具有索引字段的文档的条目,即使索引字段包含空值也是如此。索引会跳过缺少索引字段的任何文档。索引是“稀疏的”,因为它不包含集合的所有文档。相反,非稀疏索引包含集合中的所有文档,为那些不包含索引字段的文档存储空值
  • 语法格式:db.COLLECTION_NAME.createIndex({索引键名:排序规则},{sparse:true})
  • 为 dev 集合中的 tag 键创建稀疏索引
    *
  • 查看索引
    *
  • 注意:从 MongoDB 3.2 开始,MongoDB 提供了创建部分索引的选项 。部分索引提供了稀疏索引功能的超集。如果您使用的是 MongoDB 3.2 或更高版本,则部分索引应优先于稀疏索引。

8.覆盖索引查询

  • 官方的 MongoDB 的文档中说明,覆盖查询是以下的查询:
    1、 所有的查询字段是索引的一部分;
    2、 所有的查询返回字段在同一个索引中;
  • 由于所有出现在查询中的字段是索引的一部分, MongoDB 无需在整个数据文档中检索匹配查询条件和返回使用相同索引的查询结果
  • 因为索引存在于 RAM 中,从索引中获取数据比通过扫描文档读取数据要快得多。
  • 如有如下索引:
    db.stu.createIndex({title:1,:size:1})
  • 那么执行如下查询时,该索引会覆盖查询:
    db.stu.find({title:“dev”},{size:1,_id:0})
  • 也就是说,对于上述查询,MongoDB 的不会去数据库文件中查找。相反,它会从索引中提取数据,这是非常快速的数据查询
  • 由于我们的索引中不包括 _id 字段,_id 在查询中会默认返回,我们可以在 MongoDB的查询结果集中排除它。

9.查询计划

  • 在 MongoDB 中通过 explain()函数启动执行计划,我们可以使用查询计划分析索引的使用情况,可通过查看详细的查询计划来决定如何优化。
  • 语法结构:db.COLLECTION_NAME.find().explain()
  • 删除 dev 集合中的所有索引。通过查询计划查看查询 size 键的值大于 200 的查询结果
    *
  • 为 size 键创建单字段索引。再次查看查询结果。
  • 创建索引
    *
  • 查看执行结果
    *

10.使用索引注意事项

  • 既然索引可以加快查询速度,那么是不是只要是查询语句,就创建索引呢?答案是否定的。因为索引虽然加快了查询速度,但索引也是有代价的:索引文件本身要消耗存储空间,同时索引会加重插入、删除和修改记录时的负担,另外,数据库在运行时也要消耗资源维护索引,因此索引并不是越多越好。
  • 那么什么情况不建议创建索引呢?例如一两千条甚至只有几百条记录的表,没必要建索引,让查询做全集合扫描就好了。至于多少条记录才算多?我个人建议以 2000 作为分界线,记录数不超过 2000 可以考虑不建索引,超过 2000 条可以酌情考虑创建索引

10.1 如何创建合适的索引

10.1.1建立合适的索引
  • 为每一个常用查询结构建立合适的索引
  • 复合索引是创建的索引由多个字段组成,例如:
    db.test.createIndex({“username”:1, “age”:-1})
  • 交叉索引是每个字段单独建立索引,但是在查询的时候组合查找,例如:
    db.test.createIndex({“username”:1})
    db.test.createIndex({“age”:-1})
    db.test.find({“username”:“kaka”, “age”: 30})
  • 交叉索引的查询效率较低,在使用时,当查询使用到多个字段的时候,尽量使用复合索引,而不是交叉索引。
10.1.2 复合索引的字段排列顺序
  • 当我们的组合索引内容包含匹配条件以及范围条件的时候,比如包含用户名(匹配条件)以及年龄(范围条件),那么匹配条件应该放在范围条件之前。
  • 比如需要查询:
    db.test.find({“username”:“kaka”, “age”: {$gt: 30}})
  • 那么复合索引应该这样创建:
    db.test.ensureIndex({“username”:1, “age”:-1})
10.1.3 查询时尽可能仅查询出索引字段
  • 有时候仅需要查询少部分的字段内容,而且这部分内容刚好都建立了索引,那么尽可能只查询出这些索引内容,需要用到的字段显式声明(_id 字段需要显式忽略!)。因为这些数据需要把原始数据文档从磁盘读入内存,造成一定的损耗。
  • 比如说我们的表有三个字段:
    name, age, mobile
  • 索引是这样建立的:
    db.stu.createIndex({“name”:1,“age”:-1})
  • 我们仅需要查到某个用户的年龄(age),那可以这样写:
    db.stu.find({“name”:“kaka”}, {"_id":0, “age”:1})
  • 注意到上面的语句,我们除了”age”:1 外,还加了”_id”:0,因为默认情况下,_id都是会被一并查询出来的,当不需要_id 的时候记得直接忽略,避免不必要的磁盘操作。
10.1.4 对现有的数据大表建立索引的时候,采用后台运行方式
  • 在对数据集合建立索引的过程中,数据库会停止该集合的所有读写操作,因此如果建立索引的数据量大,建立过程慢的情况下,建议采用后台运行的方式,避免影响正常业务流程。
  • db.stu.ensureIndex({“name”:1,“age”:-1},{“background”:true})

11.索引限制

11.1 额外开销

  • 每个索引占据一定的存储空间,在进行插入,更新和删除操作时也需要对索引进行操作。所以,如果你很少对集合进行读取操作,建议不使用索引

11.2 内存使用

  • 由于索引是存储在内存(RAM)中,你应该确保该索引的大小不超过内存的限制
  • 如果索引的大小大于内存的限制,MongoDB 会删除一些索引,这将导致性能下降

11.3 查询限制

  • 索引不能被以下的查询使用:
  • 正则表达式(最左匹配除外)及非操作符,如 $nin, $not, 等。
  • 算术运算符,如 $mod, 等。
  • 所以,检测你的语句是否使用索引是一个好的习惯,可以用 explain 来查看。

11.4 最大范围

  • 集合中索引不能超过 64 个
  • 索引名的长度不能超过 128 个字符
  • 一个复合索引最多可以有 31 个字段

12.正则查询

  • MongoDB 中查询条件也可以使用正则表达式作为匹配约束。
  • 语法格式:db.COLLECTION_NAME.find({字段名:正则表达式});
  • db.COLLECTION_NAME.find({字段名:{$ regex:正则表达式,$options:正则选项}});

  • 正则表达式格式:/xxx/

正则选项:

  • i - 不区分大小写以匹配大小写的情况。
  • m - 多行查找,如果内容里面不存在换行符号(例如 \n)或者条件上没有(start/end),该选项没有任何效果
  • x - 设置 x 选项后,正则表达式中的非转义的空白字符将被忽略。需要$ regex 与$options语法
  • s - 允许点字符(即.)匹配包括换行符在内的所有字符。需要$ regex 与$options 语法
  • i,m,x,s 可以组合使用。
  • 查询 dev 集合中 title 字段以’S’开头的数据
    db.dev.find({title:/^S/})
    db.dev.find({title:{$regex:/^S/}})
  • 查询 dev 集合中 title 字段以’g’结尾的数据
    db.stu.find({title:/g$ /})
    db.stu.find({title:{$ regex:/g$/}});
  • 查询 dev 集合中 dev 字段中包含’ing’的数据
    db.stu.find({title:/ing/});
    db.stu.find({title:{$regex:/ing/}});
  • 查询 dev 集合中 title 字段以’L’开头的数据,且忽略大小写
    db.dev.find({title:/^S/i});
    db.dev.find({title:{$ regex:/^S/i}});
    db.dev.find({title:{$ regex:/^S/, $options:“i”}});
  • 查询 dev 集合中 title 字段已’S’开头、'g’结尾的数据
    db.dev.find({title:/^S.*g$ /});
    db.dev.find({title:{$ regex:/^z.*n$/}});
  • 查询 dev 集合中 title 字段以’S’或’t’开头的数据
    db.dev.find({title:{$in:[/^S/, /^t/]}});
  • 查询 dev 集合中 title 字段不以’S’开头的数据
    db.dev.find({title:{$not:/^S/}});
  • 查询 dev 集合中 title 字段不以’S’或’t’开头的数据
    db.stu.find({title:{$nin:[/^S/, /^t/]}});

版权声明:本文不是「本站」原创文章,版权归原作者所有 | 原文地址: