MongoDB 索引管理

文章目录

    • 前言
    • 1. 术语介绍
      • 1.1 index / key
      • 1.2 Coverd Query
      • 1.3 IXSCAN / COLLSCAN
      • 1.4 Selectivity
      • 1.5 Index Prefix
    • 2. 索引原理
    • 3. 索引的维护
      • 3.1 创建索引语法
      • 3.2 单字段索引
      • 3.3 多字段复合索引
      • 3.4 数组的多列索引
      • 3.5 全文索引
      • 3.6 Hash 索引
      • 3.7 TTL 索引
      • 3.8 删除索引
      • 3.9 后台创建索引
    • 4. 执行计划

前言

索引是数据库中离不开的话题,其作用是提高数据的获取性能。与关系型数据库一样,MongoDB 同样可以利用索引提高查询效率。如果没有索引 MongoDB 的查询需要扫描集合中的每一条记录,然后挑选出与查询条件匹配的文档记录。也就是常说的全表扫描,一个非常耗时的操作。

MongoDB 默认索引数据结构也是 B+Tree 与关系型数据库的索引相似。本篇文章将介绍 MongoDB 中的索引与维护。

1. 术语介绍

本小节介绍 MongoDB 索引相关的术语,不难发现 MongoDB 索引与关系型数据库使用的是同一套理论,如果懂一款关系型数据库,应该会很好理解。

1.1 index / key

索引,是一种数据结构,在 MongoDB 中使用 B+tree 算法。索引只包含索引字段,不包含数据,叶子节点记录的是一行数据的物理位置,与 PostgreSQL 存储方式类似,数据与索引分开存储。

1.2 Coverd Query

覆盖查询,MongoDB 也支持多列索引,所以也可以使用索引完成覆盖查询。所有需要返回的数据都包含在索引中,不需要额外的字段,就可以不需要从数据页加载数据,查询的性能也会得到提升,但是存储成本和维护成本也会增加。

1.3 IXSCAN / COLLSCAN

索引扫描 / 集合扫描,指的是表的方法方式。如下表是 MongoDB 中的术语。

SQL 术语概念MongoDB 术语概念解释说明
databasedatabase数据库
tablecollection数据库表 / 集合
rowfield数据字段 / 域
indexindex索引

所以 IXSCAN 指的是索引扫描,通过索引定位到行记录。COLLSCAN 指的是全表扫描,在 MongoDB 中表也叫集合。通过索引定位记录的速度肯定要比整个集合搜索一次的效率要高很多,所以优秀的索引设计也是数据库性能的关键。

1.4 Selectivity

选择性,也可称为过滤性,是判断一个字段是否适合创建索引的重要参考指标。

例如,在一个 10000 条记录的集合中:

  • 满足 gender = F 的记录有 4000 条
  • 满足 city = LA 的记录有 100 条
  • 满足 ln = Tony 的记录有 10 条

条件 ln 能过滤最多的数据,所以它的选择性最好,其次是 city 字段,gender 字段最弱。

如果有一条查询需要同时满足三个字段的条件,只能创建一个单列索引,那么最优的选择是选择性最好的字段。

1.5 Index Prefix

索引前缀,当创建一个 a, b, c, d 四个字段的复合索引时,该索引会有三个前缀。分别是:

  • a
  • a, b
  • a, b, c

所有索引前缀都可以被该索引覆盖,所以就无需针对使用到这些前缀的查询创建额外的索引。

2. 索引原理

请添加图片描述

B+Tree 多路平衡搜索树,是一种树形分层结构,第 1 层为顶部节点,第 2 层为分支节点,均只保存索引字段上的键值,第 3 层为叶子节点,除了保存索引字段的键值外,还有一个指针变量,指向具体数据文件上的某条文档记录的位置。

推荐使用:B-Tree 动画演示

B+Tree 类型索引结构有以下几个特点。

  • 每个叶子节点的深度都相同,通常为 3 层或 4 层。
  • 由于第 1 层顶部节点和第 2 层分支节点几乎总被加载到内存中,因此查询文档记录时,物理磁盘的读取实际次数通常仅为一次或两次,性能非常可观。
  • 叶子节点是通过双向链表连接且已经排好序的,因此对于范围查询来说,直接遍历叶子节点的链表就能快速定位匹配文档记录的指针位置。
  • 数据不断的写入,也会涉及到索引维护,索引节点的拆分和分裂移动,是一个非常耗时的操作。

3. 索引的维护

MongoDB 中有 8 种索引类型,分别是单列索引、组合索引、多值索引、地理位置索引、全文索引、TTL 索引、部分索引、哈希索引。

3.1 创建索引语法

在 MongoDB 集合中插入文档记录时,如果没有指定 _id 字段的值,则会默认生成一个 ObjectID 类型的值并赋给 _id 字段,同时也会默认在 _id 字段上创建一个具有唯一性的主键索引。

在集合其他字段上创建索引的语法:

db.collection.createIndex(Keys, options)
  • keys:指定需要创建索引的字段,可以是一个字段或多个字段,其值的格式为 {“字段名” : “索引类型”},索引类型可以为 1 或 -1,当为 1 时表示创建一个升序排列的索引,当为 -1 时表示创建一个降序排列的索引。索引类型还可以为 text、hashed 文本类型 hash 类型索引等。
  • options:为可选字段,例如通过 name 指定索引的名称,unique 指定索引的唯一性等。

3.2 单字段索引

给如下示例集合创建一个单列索引。

db.schools.insertOne(
   {
      "_id": ObjectId("570c04a4ad233577f97dc459"),
      "studentsEnrolled": 1034,
      "location": { state: "NY", city: "New York" }
   }
)

选择给 studentsEnrolled 字段创建一个升序索引。

db.schools.createIndex({studentsEnrolled:1})

可通过如下命令查询一个集合的索引。

db.schools.getIndexes({})
{
    "v": 2,
    "key": {
        "_id": 1
    },
    "name": "_id_",
    "ns": "test.schools"
}
{
    "v": 2,
    "key": {
        "studentsEnrolled": 1
    },
    "name": "studentsEnrolled_1",
    "ns": "test.schools"
}

索引记录中 v 表示索引记录的版本号,key 表示索引创建哪个字段上,name 表示索引名称,ns 表示索引命名空间依次为索引所在的 “数据库名.集合名”。

3.3 多字段复合索引

给 schools 集合创建一个复合索引。

db.schools.insertOne(
   {
      "_id": ObjectId("570c04a4ad233577f97dc459"),
      "studentsEnrolled": 1034,
      "location": { state: "NY", city: "New York" }
   }
)

可通过如下命令创建一个多字段的复合索引。

db.products.createIndex( { "studentsEnrolled": 1, "location": 1 } )

执行成功后,通过如下命令查询 schools 集合中的索引情况。

{
    "v": 2,
    "key": {
        "_id": 1
    },
    "name": "_id_",
    "ns": "test.schools"
}
{
    "v": 2,
    "key": {
        "studentsEnrolled": 1
    },
    "name": "studentsEnrolled_1",
    "ns": "test.schools"
}
{
    "v": 2,
    "key": {
        "studentsEnrolled": 1,
        "location": 1
    },
    "name": "studentsEnrolled_1_location_1",
    "ns": "test.schools"
}

复合索引包含两个字段,分别是 studentsEnrolled 和 location,B+tree 中的叶子节点按照 studentsEnrolled 字段升序排列,对于相同的 studentsEnrolled 将以 location 字段进行升序排列。

3.4 数组的多列索引

MongoDB 支持数组类型的字段,涉及到数组内部的查询,如精确匹配数组中所有元素的查询、匹配其中任意元素的查询、匹配特定位置元素的查询等。为了提高数组查询的性能,可以在数组类型的字段创建索引,当 MongoDB 在构造索引的 B-Tree 时,将默认在 B-Tree 叶子节点为数组中的每一个元素创建索引条目。

集合数据如下:

{ _id: 5, type: "food", item: "aaa", ratings: [ 5, 8, 9 ] }
{ _id: 6, type: "food", item: "bbb", ratings: [ 5, 9 ] }
{ _id: 7, type: "food", item: "ccc", ratings: [ 9, 5, 8 ] }

其中 ratings 字段为数组类型,在其上面创建一个索引,语法如下。

db.inventory.createIndex({"ratings": 1})

数组中的每一个元素,都会被当作一个独立的键值构建在 B+tree 叶子节点中,如 ratings: [ 5, 8, 9 ] 被拆成 5、8、9 共三个键存储在索引条目中,且这 3 条索引记录都指向同一个文档 { _id: 5 } 在数据文件中的位置。

3.5 全文索引

创建一个 stores 集合。

db.stores.insert(
 [
  { _id: 1, name: "Java Hut", description: "Coffee and cakes" },
  { _id: 2, name: "Burger Buns", description: "Gourmet hamburgers" },
  { _id: 3, name: "Coffee Shop", description: "Just coffee" },
  { _id: 4, name: "Clothes Clothes Clothes", description: "Discount clothing"},
  { _id: 5, name: "Java Shopping", description: "Indonesian goods" }
 ]
)

创建一个 name 字段的全文索引语句示例如下:

db.stores.createIndex({'name': 'text'})

查看该集合的索引信息:

{
    "v": 2,
    "key": {
        "_id": 1
    },
    "name": "_id_",
    "ns": "test.stores"
}
{
    "v": 2,
    "key": {
        "_fts": "text",
        "_ftsx": 1
    },
    "name": "name_text",
    "ns": "test.stores",
    "weights": {
        "name": 1
    },
    "default_language": "english",
    "language_override": "language",
    "textIndexVersion": 3
}

在创建 B+tree 索引过程中,会将文本字符串按特定语言中的分隔符进行分词,生成一个键值对加入到索引条目,插入 B+tree 的叶子节点。由于在创建文本索引过程中会对每一个主干单词生成索引条目,所以文本索引所需的空间可能是巨大的。

文本索引在插入过程中,要验证分词,所以对插入性能也会有所影响。

3.6 Hash 索引

创建 Hash 索引时,利用 hash 函数计算字段的值,保证计算后的取值更加均匀分布。

db.stores.createIndex({'name':'hashed'})

Hash 索引仅支持等值查询,不支持范围查询。

3.7 TTL 索引

TTL 索引是一种特殊的单字段索引,用户可以使用 TTL 索引淘汰过期数据,节省存储空间。比如对于存储事件日志的场景,如果只需要存储最近 1 小时的数据,可以在每条文档中指定 “lastModifiedDate” 字段记录生成的时间,然后按照这个字段创建 1 个 1 小时过期的 TTL 索引:

db.eventlog.createIndex( { "lastModifiedDate": 1 }, { expireAfterSeconds: 3600 } )

创建 TTL 索引字段的值类型必须是一个日期类型或者包含日期属性的数组。

每个 mongod 进程在启动时,会创建一个 TTLMonitor 后台线程,这个后台线程会每隔 60s 发起 1 轮 TTL 清理操作。每轮 TTL 操作会在搜集完实例上的所有 TTL 索引后,依次对每个 TTL 索引生成执行计划并执行数据清理。

3.8 删除索引

删除集合中指定索引的语法如下:

db.collection.dropIndex(index)

index 参数表示字符串类型的索引名称或创建索引时指定的文档记录。

如下示例集合中,有两个索引:

{
    "v": 2,
    "key": {
        "_id": 1
    },
    "name": "_id_",
    "ns": "test.stores"
}
{
    "v": 2,
    "key": {
        "_fts": "text",
        "_ftsx": 1
    },
    "name": "name_text",
    "ns": "test.stores",
    "weights": {
        "name": 1
    },
    "default_language": "english",
    "language_override": "language",
    "textIndexVersion": 3
}
{
    "v": 2,
    "key": {
        "name": "hashed"
    },
    "name": "name_hashed",
    "ns": "test.stores"
}
db.stores.dropIndex("name_hashed")

一次性删除多个索引语法:

db.stores.dropIndex(["name_hashed", "name_text"])

3.9 后台创建索引

创建索引时,将默认使用前台的方式,执行过程中会堵塞所有读写操作。可通过指定 background 参数设置索引后台创建,与 MySQL 中的 Online DDL 类似,后台创建索引虽然不会堵塞读写操作,但是执行时间会变长。

语法如下:

db.stores.createIndex({'name':'hashed'}, {background: true})

4. 执行计划

未完待续

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/311025.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

nextjs + ahooks 报错 Cannot use import statement outside a module

在 nextjs 中使用 ahooks 时,报错 SyntaxError: Cannot use import statement outside a module,如下图所示: 解决方案 transpilePackages 官网介绍 Next.js can automatically transpile and bundle dependencies from local packages (lik…

跨境电商卖家都在用的海外云手机

在过去的几年里,“品牌出海”一直是国内企业关注的焦点之一。我们亲眼目睹了跨境电商的迅猛增长,为了抢占市场份额,许多国内电商纷纷加入这一领域。在跨境电商运营的过程中,海外云手机几乎成了业内大佬们一致推崇的运营利器。那么…

C/C++ 有关质数(素数)的问题

第一题:判断是否为质数 代码&#xff1a; #include <bits/stdc.h> using namespace std; int main() {int a;int flag 1;cin>>a;for(int j2;j<a;j){if(a % j 0){cout<<a<<"不是质数";flag0;break;}}if(flag1) cout<<a<<&quo…

中国电子学会2023年9月份青少年软件编程Scratch图形化等级考试试卷二级真题(含答案)

一、选择题&#xff08;共25题&#xff0c;共50分&#xff09; 1.点击绿旗&#xff0c;运行程序后&#xff0c;舞台上的图形是&#xff1f;&#xff08;D &#xff09;&#xff08;2分&#xff09; A.画笔粗细为4的三角形 B.画笔粗细为5的六边形 C.画笔粗细为4的六角形 D.画…

Redis-Cluster 与 Redis 集群的技术大比拼

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 Redis-Cluster 与 Redis 集群的技术大比拼 前言概念与原理对比Redis-Cluster&#xff1a;基于哈希槽的分布式解决方案传统 Redis 集群&#xff1a;主从架构下的数据分片方式 搭建与配置的异同Redis-Cl…

Python 语言基础

目录 Python 语言基础语法特点注释缩进规范编写规则命名规范 变量保留字与标识符Python中的变量定义变量 基本数据类型数字字符串Bool类型数据类型转换 输入和输出input&#xff08;&#xff09;输入print 输出 Python 语言基础 语法特点 注释 单行注释&#xff0c;语法如下…

Win2008R2上RedisDesktopManager 黑屏

问题&#xff1a; 运行发现右侧显示缓存信息的部分是黑屏。 解决方式&#xff1a; 管理工具->远程桌面服务->远程桌面会话主机配置->RDP-TCP->属性->客户端设置->颜色深度->限制最大颜色深度,将16位改为32位

Maven在java中的实现(对java的项目进行打包)

前言: 在前面的文章中我们了解了Maven的作用,并在自己的电脑上安装配置好了Maven,也成功的在IDEA中添加了Maven,但是具体的实现还是有一些些小问题,那么接下来,我将带着大家对Java项目进行一次打包,系统的完成一次,并在途中解决一下会出现的问题. 我以图片中选中的这个包为例,…

jsPlumb、mxGraph和Antv x6实现流程图选型

解决方案 结合我们项目以及主流解决方案&#xff0c;提供以下几种方案&#xff1a; 序号技术栈性质是否开源说明1jsPlumb国外框架社区版、商业版中台项目现有方案2mxGraph国外框架开源比较有名的开源绘图网站draw.io &#xff08;和processOn类似&#xff09;&#xff0c;使用…

力扣日记1.10-【二叉树篇】701. 二叉搜索树中的插入操作

力扣日记&#xff1a;【二叉树篇】701. 二叉搜索树中的插入操作 日期&#xff1a;2024. 参考&#xff1a;代码随想录、力扣 —————————————————————— 天哪&#xff0c;上次打开力扣还是2023&#xff0c;转眼已经2024&#xff1f;&#xff01; 两个星期过去…

软件测试|如何在Linux中下载和安装软件包

简介 在Linux操作系统中&#xff0c;下载和安装软件包是一项基本任务。不同的Linux发行版可能有不同的包管理工具和方式&#xff0c;但总体流程是类似的。以下是在Linux中下载和安装软件包的详细步骤。 步骤1&#xff1a;选择适当的包管理工具 因为Linux有不同的发行版本&am…

代码随想录算法训练营第23天 | 669. 修剪二叉搜索树 108.将有序数组转换为二叉搜索树 538.把二叉搜索树转换为累加树 总结篇

669. 修剪二叉搜索树 题目链接&#xff1a; 669. 修剪二叉搜索树 给定一个二叉搜索树&#xff0c;同时给定最小边界L 和最大边界 R。通过修剪二叉搜索树&#xff0c;使得所有节点的值在[L, R]中 (R>L) 。你可能需要改变树的根节点&#xff0c;所以结果应当返回修剪好的二…

Vue与后端交互、生命周期

一&#xff1a;Axios 1.简介 ① Axios 是一个基于 promise 的 HTTP 库&#xff0c;可以用在浏览器和 node.js 中 ② axios官网&#xff1a;axios中文网|axios API 中文文档 | axios 2.实例 json文件&#xff1a;film.json&#xff08;这里只是一部分&#xff0c;原代码太多…

语义解析:如何基于SQL去实现自然语言与机器智能连接的桥梁

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 &#x1f4ab;个人格言:"没有罗马,那就自己创造罗马~" 目录 语义解析 定义 作用 语义解析的应用场景 场景一&#xff1a; 场景二&#xff1a; 总结语…

RISC-V是如何与X86、ARM三分天下

目录 1.行业CPU指令集格局 2.汽车中的RISC-V进展 2.1 国际进展 2.2 国内进展 3.小结 2023年3月2日&#xff0c;在平头哥牵头举办的玄铁RISC-V生态大会上&#xff0c;工程院院士倪光南表示&#xff0c;基于RISC-V模块化、可扩展、容易定制、不受垄断制约等优势&#xff0c;…

山羊目标检测数据集VOC格式290张

山羊&#xff0c;一种聪明而机敏的哺乳动物&#xff0c;以其独特的形态和特点而受到人们的喜爱。 山羊的体型中等&#xff0c;四肢强健&#xff0c;有着坚硬的蹄子和浓密的毛发。它们的头部较大&#xff0c;有着一对弯曲的角&#xff0c;角上有很多节状突起。山羊的毛色多为棕…

美国生物医学博士后最低年薪有望涨至7万美元

2023年底&#xff0c;美国国立卫生研究院&#xff08;NIH&#xff09;咨询小组发布了一份报告&#xff0c;建议将生物医学领域博士后的最低起薪从目前的56 484美元/年提高到70 000美元/年。知识人网小编结合我们了解到的情况&#xff0c;整理文章如下。 去年&#xff0c;我们知…

浅析NVMe key per IO加密技术-2

二、Key per IO功能设置的流程 设置Key Per I/O功能需要对NVMe存储设备进行一系列配置&#xff0c;涉及多个步骤和能力要求。以下是一个简化的流程概述&#xff1a; 硬件支持&#xff1a;首先&#xff0c;NVMe固态硬盘支持Key Per I/O技术&#xff0c;并且了解相关的NVM Expre…

使用 OpenAI 自定义 API 提高电商平台的推荐精度

一、引言 在当今的电商时代&#xff0c;推荐系统已成为影响用户购买决策的关键因素之一。为了提供更精准的推荐&#xff0c;许多电商平台纷纷寻求先进的技术支持。OpenAI 自定义 API 正是这样一种强大而灵活的工具&#xff0c;能够通过自然语言处理和机器学习技术&#xff0c;…

C++上位软件通过Snap7开源库访问西门子S7-200/LOGO PLC/合信M226ES PLC V存储区的方法

前言 在前面例程中谈到了C 通过Snap7开源库S7通信库跟西门子S7-1200PLC/S7-1500PLC以及合信CTMC M226ES PLC/CPU226 PLC通信的方式方法和应用例程。但是遗憾的是Snap7中根据官方资料显示只能访问PLC的 DB区、MB区、C区、T区 、I区、Q区&#xff0c;并没有提到有关如何访问S7-20…