MongoDB 索引管理

news2024/11/18 3:35:14

文章目录

    • 前言
    • 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. 执行计划

未完待续

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

回顾基础--HTML篇

HTML语法规范 <html></html> 开始标签与结束标签 <br /> 单标签 包含关系 <head><title></title> </head>并列关系 <head></head> <body></body> 1、 标题标签 标题标签 【双标签】【不同标题字体大小…

IP风险画像:源头防范网络攻击的全面策略

在当今数字化的时代&#xff0c;网络攻击呈现多样化和复杂化的趋势&#xff0c;为了确保网络的安全&#xff0c;制定全面的IP风险画像并从源头防范网络攻击是至关重要的。ip数据云将探讨如何通过建立IP风险画像来识别和应对潜在的威胁&#xff0c;从而实现更加安全可靠的网络环…

网站监测工具测评之功能强大的Uptime Kuma

背景 最近调研一款【网络隔离性检测】工具&#xff0c;无意间发现了这款工具——Uptime-Kuma&#xff0c;并且进行了部署测试以及API接口的部分调用尝试&#xff0c;个人认为这个工具在网站监控领域具备出色的功能和性能&#xff1b;下面为大家详细的介绍这个工具。 官网地址…

java项目启动报错排查过程——mysql服务挂了

来自前端同事无法启动后台项目的案例 Caused by: java.net.ConnectException Create breakpoint : Connection refused: connect The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server. 大致…

最新发布的Edge扩展插件:安装位置一览

目录 学习目标&#xff1a; 学习内容&#xff1a; 学习时间&#xff1a; 学习产出&#xff1a; Edge扩展插件的介绍&#xff1a; Edge扩展插件的安装位置&#xff1a; Edge扩展插件的管理方式&#xff1a; Edge扩展插件的启用和禁用&#xff1a; 学习目标&#xff1a; 了解Edg…

vulhub中的Nginx漏洞的详细解析

Nginx漏洞 1.cd到nginx_parsing_vulnerability cd /opt/vulhub/nginx/nginx_parsing_vulnerability 2.执行docker-compose up -d 3.查看靶场是否开启成功 dooker ps 4.访问浏览器 因为这里是80端口所以直接使用ip就能访问成功 5.上传图片 注意这里的图片是含有一句话木马的图…

数据结构第十二弹---堆的应用

堆的应用 1、堆排序2、TopK问题3、堆的相关习题总结 1、堆排序 要学习堆排序&#xff0c;首先要学习堆的向下调整算法&#xff0c;因为要用堆排序&#xff0c;你首先得建堆&#xff0c;而建堆需要执行多次堆的向下调整算法。 但是&#xff0c;使用向下调整算法需要满足一个前提…

小型洗衣机好用吗?高性价比迷你洗衣机推荐

现在大多数的上班族&#xff0c;面临的都是早九晚六的工作&#xff0c;而且工作完下班回家还是面对各种各样的家务&#xff0c;特别是清洗需要换洗的洗衣&#xff0c;属实是有点辛苦了。可能很多人为了方便&#xff0c;每次洗衣服的都是把一堆衣服直接丢进洗衣机&#xff0c;直…

LeetCode-字符串转换整数atoi(8)

题目描述&#xff1a; 请你来实现一个 myAtoi(string s) 函数&#xff0c;使其能将字符串转换成一个 32 位有符号整数&#xff08;类似 C/C 中的 atoi 函数&#xff09;。 函数 myAtoi(string s) 的算法如下&#xff1a; 读入字符串并丢弃无用的前导空格 检查下一个字符&…

Android通过Recyclerview实现流式布局自适应列数及高度

调用 FlowAdapter 跟普通recyclerview一样使用 RecyclerView rvLayout holder.getView(R.id.spe_tag_layout); FlowAdapter rvAdapter new FlowAdapter(); FlowLayoutManager flowLayoutManager new FlowLayoutManager(); rvLayout.setLayoutManager(flowLayoutManager); r…

BlogPark测试报告

目录 一&#xff0c;项目背景 二&#xff0c;项目功能 三&#xff0c;测试计划 3.1 测试用例的设计 3.2 功能测试 1.正常登录 2.正常写博客测试 &#xff08;输入完整的标题和内容&#xff09; 3.发布博客之后跳转到详情页观察是否有刚发布的博客 4.删除博客观察列表的…

如何创建微信小程序?高效实现你的创意

在数字化浪潮下&#xff0c;微信小程序以其便捷高效的特点逐渐成为人们日常生活与商业活动的重要载体。有很多人却被难住&#xff0c;若没有没有编程经验&#xff0c;如何创建微信小程序&#xff1f;答案是通过合理利用工具&#xff0c;让创意迅速落地为功能完备的小程序。 一…

【qml】第一次尝试qml与c++交互

背景&#xff1a; 目的是学习qml&#xff0c;因为看到很多qml的酷炫效果&#xff0c;想试一试。 看过网上一些代码&#xff0c;qt提供的工具类好几个&#xff0c;看着就晕。只想提炼一下&#xff0c;做个记录。 我先整理了一套自己的想法&#xff1a;所谓交互&#xff0c;还…

浏览器缓存引发的odoo前端报错

前两天&#xff0c;跑了一个odoo16项目&#xff0c;莫名其妙的前端报错&#xff0c; moment.js 报的错&#xff0c; 这是一个时间库&#xff0c;不是我自己写的代码&#xff0c;我也没做过任何修改&#xff0c;搞不清楚为什么报错。以为是odoo的bug&#xff0c;所以从gitee下载…

K8S存储卷和数据卷

容器内的目录和宿主机的目录进行挂载 容器在系统上的生命周期是短暂的&#xff0c;delete&#xff0c;k8s用控制器创建的pod&#xff0c;delete相当于重启&#xff0c;容器的状态也会恢复到初始状态&#xff0c;一旦回到初始状态&#xff0c;所有的后天编辑的文件都会消失 容器…

rax3000m 刷机 uboot + immortalwrt

0. 环境 - win10 ubuntu22 - rax3000m 生产日期20231027 一台&#xff08;nand版本的&#xff09; 1. 上电&#xff0c;登录web 电脑连接路由器LAN1 http://192.168.10.1/ 账号&#xff1a;user 密码&#xff1a;KK6kYC!3 上网设置&#xff1a;自动获取IP 2. 开启 ssh 2…

Linux CentOS 7.6安装JDK详细保姆级教程

一、检查系统是否自带jdk java --version 如果有的话&#xff0c;找到对应的文件删除 第一步&#xff1a;先查看Linux自带的JDK有几个&#xff0c;用命令&#xff1a; rpm -qa | grep -i java第二步:删除JDK&#xff0c;执行命令&#xff1a; rpm -qa | grep -i java | xarg…

软件测试工程师,从6K到25k的测试之路养成,一路狂飙...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、技术方向 就技…

华为数通HCIA题库(750题)

完整题库在这里&#xff1a;华为数通HCIA-RS题库注释版-加水印.pdf资源-CSDN文库 此处只节选几题。 1.网络管理员在网络中捕获到了一个数据帧&#xff0c;其目的MAC地址是01-00-5E-AO-B1-C3。关于该MAC地址的说法正确的是&#xff08; )。 A.它是一个单播MAC地址 B.它是一个广播…