【MySQL】索引的数据结构

news2024/12/24 11:36:50

为什么使用索引

索引是存储引擎用于快速找到数据记录的一种数据结构。进行数据查找时,首先查看查询条件是否命中某条索引,符合则可以通过索引查找相关数据,如果不符合则要全表扫描,即需要一条一条地查找记录,直到找到与条件符合的记录。

在这里插入图片描述

假如给数据使用二叉树进行存储,如下图所示:

在这里插入图片描述

对字段Col2添加了索引,相当于在硬盘上为Col2维护了一个索引的数据结构,二叉搜索树。二叉搜索树的每个结点存储的是(K,V)结构,key是Col2,value是该key所在行的文件指针。例如:该二叉搜索树的根节点是(34,0x07)。现在对Col2添加了索引,这时候查询Col2=89这条记录的时候会先查找该二叉搜索树,读34到内存,89>34,继续查找右侧数据,读89到内存,89 == 89,找到数据。

目的:减少磁盘IO的次数,加快查询速率。

索引及优缺点

概述

MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。

索引的本质:索引是数据结构。你可以简单理解为“排好序的快速查找数据结构”,满足特定查找算法。这些数据结构以某种方式指向数据, 这样就可以在这些数据结构的基础上实现 高级查找算法 。

优点

(1)类似大学图书馆建书目索引,提高数据检索的效率,降低数据库的IO成本 ,这也是创建索引最主要的原因。

(2)通过创建唯一索引,可以保证数据库表中每一行数据的唯一性

(3)在实现数据的参考完整性方面,可以加速表和表之间的连接 。换句话说,对于有依赖关系的子表和父表联合查询时,可以提高查询速度。

(4)在使用分组和排序子句进行数据查询时,可以显著减少查询中分组和排序的时间 ,降低了CPU的消耗

缺点

增加索引也有许多不利的方面,主要表现在如下几个方面:

​ (1)创建索引和维护索引要耗费时间 ,并且随着数据量的增加,所耗费的时间也会增加。

​ (2)索引需要占磁盘空间 ,除了数据表占数据空间之外,每一个索引还要占一定的物理空间, 存储在磁盘上 ,如果有大量的索引,索引文件就可能比数据文件更快达到最大文件尺寸。

​ (3)虽然索引大大提高了查询速度,同时却会降低更新表的速度 。当对表中的数据进行增加、删除和修改的时候,索引也要动态地维护,这样就降低了数据的维护速度。

InnoDB中索引推演

索引之前的查找

在一个页中查找

  • 以主键为搜索条件

    可以在页目录中使用二分法快速定位到对应的槽,然后遍历该槽对应分钟中的记录可快速找到指定的记录。

  • 以其他列为搜索条件

    在数据页中没有对非主键列建立页目录,因此无法用二分法快速定位相应的槽,只能从最小记录开始依次遍历单链表中的每条记录。

在很多页中查找

在大部分情况下,表中存放的记录是非常多的,需要很多数据页才能存储。在很多数据页中查找记录可以分为两个步骤:

  • 定位到记录所在的页
  • 在所在页中查找相应的记录

在没有索引的情况下,不论是根据主键列或者其他列的值进行查找,由于我们并不能快速的定位到记录所在的页,所以只能从第一个页沿着双向链表一直往下找,在每一个页中根据我们上面的查找方式去查找指定的记录。因为要遍历所有的数据页,所以这种方式显然是很耗时的。如果一个表有一亿条记录呢?此时索引应运而生。

设计索引

建表

mysql> CREATE TABLE index_demo(
    -> c1 INT,
    -> c2 INT,
    -> c3 CHAR(1),
    -> PRIMARY KEY(c1)
    -> ) ROW_FORMAT=Compact;
Query OK, 0 rows affected (0.57 sec)

mysql> desc index_demo;
+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| c1    | int     | NO   | PRI | NULL    |       |
| c2    | int     | YES  |     | NULL    |       |
| c3    | char(1) | YES  |     | NULL    |       |
+-------+---------+------+-----+---------+-------+
3 rows in set (0.00 sec)

在这里插入图片描述

  • record_type : 记录头信息的一项属性,表示记录的类型, 0 表示普通记录、1表示目录项记录、 2 表示最小记录、 3 表示最大记录。
  • next_record : 记录头信息的一项属性,表示下一条地址相对于本条记录的地址偏移量,我们用箭头来表明下一条记录是谁。
  • 各个列的值 :这里只记录在 index_demo 表中的三个列,分别是 c1 、 c2 和 c3 。
  • 其他信息 :除了上述3种信息以外的所有信息,包括其他隐藏列的值以及记录的额外信息。

效果如下:

在这里插入图片描述

将记录放到数据页里的示意图如下:

在这里插入图片描述

一个简单的索引设计方案

根据某个搜索条件查找一些记录时,为什么要遍历所有的数据页?因为各个页中的记录并没有规律,不得不依次遍历所有的数据页。

若想快速定位需要查找的记录在哪些数据页中?可以为快速定位记录所在的数据页建立一个目录,而建这个目录必须完成下面的事情:

  • 下一个数据页中用户记录的主键值必须大于上一个页中用户记录的主键值。

    假设:每个数据页最多能存放3条记录,然后向index_demo表中插入3条记录。

    mysql> INSERT INTO index_demo VALUES(1,2,'u'),(3,9,'d'),(5,3,'y')
    

    那么,这些记录已经按照主键值的大小串联成一个单向链表,如图所示:

在这里插入图片描述

从图中可以看出,index_demo表中的3条记录被插入到编号为10的数据页(record_type=0)中,此时再插入一条记录:

mysql> INSERT INTO index_demo VALUES(4,4,'a')

因为页10最多只能放3条记录,因此需要再分配一个新页。

在这里插入图片描述

新分配的数据页编号可能不是连续的,只是通过维护着上一个页和下一个页的编号而建立了链表关系。

在页10中用户记录最大的主键值是5,而页28中有一条记录的主键值是4,因为5>4,所以不符合下一个数据页中用户记录的主键值必须大于上一个页中用户记录的主键值的要求,所以要进行一次记录移动,将主键值为5的记录移动到页28中,然后将主键值为4的记录插入到页10中,示意图如下:

在这里插入图片描述

在对页中的记录进行增删改操作的过程中,需要通过一些记录移动的操作来保持这个状态一直成立——这个过程为页分裂

  • 给所有的页建立一个目录项。

    因为数据页的编号可能不连续,因此在向index_demo表中插入许多条记录后,可能是这样的:

在这里插入图片描述

这些16kb的页在物理存储上是不连续的,如果想从这么多页中根据主键值快速定位某些记录所在的页,需要做个目录,每个页对应一个目录项,每个目录项包括两部分:

  • 页的用户记录中最小的主键值,key
  • 页号,page_no

在这里插入图片描述

以页28为例,对应目录项2,这个目录项中包含着该页的页号28以及该页中用户记录的最小主键值5。那么需要将几个目录项在物理存储器上连续存储,就可以实现根据主键值快速查找某条记录的功能。

举例:查找主键值为20的记录,具体查找过程分两步:

1、从目录项中根据二分法快速确定出主键值为20的记录在目录项3中,对应的页为9

2、在页9中定位具体的记录

到这里,针对数据页做的简易目录就好了,这个目录有一个别名,称为索引

InnoDB中的索引方案

迭代1次:目录项记录的页

上面简易方案中有如下几个问题:

1、需要非常大的连续的存储空间才能把所有的目录项都放下,对记录数量非常多的表是不现实的

2、常会对记录进行增删,如果将页28中的记录都删除了,意味着目录项2也不必存在,需要将目录项2后的目录项都向前移动一个,操作效率差

因此,需要一种可以灵活管理所有目录项的方式

在InnoDB中怎么区分一条记录是普通的用户记录还是目录项记录?

使用记录头信息中的record_type属性,各个取值代表的意思如下:

0 表示普通记录

1表示目录项记录

2 表示最小记录

3 表示最大记录

在这里插入图片描述

目录项记录和普通用户记录的不同点:

  • record_type值
  • 目录项记录只有主键值和页编号,而普通用户记录是用户自己定义的。
  • 记录头信息中有一个min_rec_mask属性,只有在存储目录项记录的页中的主键值最小的目录项记录的min_rec_mask值为1,其他别的记录的min_rec_mask值为0.

迭代2次:多个目录项记录的页

一个页只有16KB大小,能存放的目录项记录是有限的,如果表中的数据太多,以至于一个数据页不足存放所有的目录项记录,怎么处理?

在这里插入图片描述

生成新的目录项记录的页。

此时,根据主键值查找一条用户记录大致需要3个步骤:

1、确定目录项记录页

2、通过目录项记录页确定用户记录真实所在的页

3、在真实存储用户记录的页中定位到具体的记录

迭代3次:目录项记录页的目录页

和数据页一样,目录项记录页也是不连续的,如果表中的数据非常多,会产生很多存储目录项记录页,怎么根据主键值快速定位一个存储目录项记录的页?

为这些存储目录项记录的页再生成一个更高级的目录,像多级目录。

在这里插入图片描述

如图,生成一个存储更高级目录项的页33,这个页中的两条记录分别代表页30和页32,如果用户记录的主键值在[1,320)之间,则到页30中查找更详细的目录项记录。

随着表中记录的增加,这个目录的层级会继续增加,简化一下,可以用下图描述:

在这里插入图片描述

这个结构是——B+树。

常见索引概念

聚簇索引

一张表只能有一个聚簇索引

聚簇索引不是一种单独的索引类型,而是一种数据存储方式(所有的用户记录都存储在叶子节点)。

索引即数据,数据即索引

特点

1、使用记录主键值的大小进行记录和页的排序,包括三方面含义:

  • 页内记录是按照主键的大小顺序排成一个单向链表
  • 各个存放用户记录的页,也是根据页中用户记录的主键大小顺序排成一个双向链表
  • 存放目录项记录的页分为不同的层次,在同一层次中的页是根据页中目录项记录的主键大小顺序排成一个双向链表。

2、B+树的叶子节点存储的是完整的用户记录(存储了所有列的值,包括隐藏列)

典型的聚簇索引(按主键)

在这里插入图片描述

优点

  • 数据访问更快,将索引和数据保存在同一个B+树中
  • 聚簇索引对于主键的排序查找和范围查找速度很快
  • 可以节省大量的io操作

缺点

  • 插入速度严重依赖插入顺序,按照主键的顺序插入是最快的方式,否则会出现页分裂,严重影响性能。对InnoDB表,一般定义一个自增的ID列为主键。
  • 更新主键的代价很高
  • 二级索引访问需要两次索引查找,第一次找到主键值,第二次根据主键值查找到行数据。

限制

  • 目前只有InnoDB数据引擎支持聚簇索引,而MyISAM不支持
  • 每个MySQL的表只能有一个聚簇索引,一般是该表的主键。
  • 如果没有定义主键,InnoDB会选择一个非空的唯一索引代替。
  • 为了充分利用聚簇索引的特性,InnoDB表的主键列尽量选择有序的顺序id,而不建议用无序的id,如UUID、MD5、HASH、字符串列作为主键无法保证数据的顺序增长。

二级索引(非聚簇索引、辅助索引)

上面的聚簇索引只能在搜索条件是主键值时才能发挥作用。

那么想以别的列作为搜索条件,应该怎么办?

答案:可以多建几棵B+树,不同的B+树中的数据采用不同的排序规则。比如,采用c2列的大小作为数据页、页中记录的排序规则,再建一棵B+树。

在这里插入图片描述

和聚簇索引的不同点:

  • 页内的记录按照c2列的大小顺序排成一个单向链表。
  • 各个存放用户记录的页是根据页中记录的c2列大小顺序排列成一个双向链表
  • 存放目录项记录的页分为不同的层次。
  • 叶子节点存储的不是完整的用户记录,只是c2列+主键两个列的值。

以查找c2列的值为4的记录为例,查找过程如下:

1、确定目录页记录项,根据根页面44,可以快速定位到目录项记录所在的页为页42(2<4<9)

2、通过目录项记录页确定用户记录真实所在的页

3、在真实存储用户记录的页中定位到具体的记录

4、由于该B+树的叶子节点中的记录只存储了c2列和主键列,因此必须再根据主键值到聚簇索引中查找一遍完整的用户记录

因此,引出了一个重要的概念:回表。

回表

根据以c2列大小排序的B+树只能确定要查找记录的主键值,要查找完整的用户记录的话,需要到聚簇索引中再查找一遍,这个过程称为回表。

因为这种按照非主键列建立的B+树需要一次回表操作才能定位到完整的用户记录,因此被称为二级索引/辅助索引

在这里插入图片描述

联合索引

可以同时以多个列的大小作为排序规则,即同时为多个列建立索引,例如让B+树按照c2和c3列的大小进行排序,有两层含义:

1、将各个记录和页按照c2列进行排序

2、在c2列相同的情况下,采用c3列进行排序

示意图:

在这里插入图片描述

注意:

  • 每条目录项记录由c2、c3、页号三部分组成,各条记录先按照c2列的值进行排序。
  • B+树叶子节点处的用户记录由c2、c3和主键c1列组成。

联合索引只会建立一棵B+树,但是如果是为c2和c3列分别建立索引会建立两棵B+树。

InnoDB的B+树索引的注意事项

根页面的位置不动

B+树形成过程:

  • 每当为某个表创建一个B+树索引的时候,会为这个索引创建一个根节点页面。最开始表中没有数据的时候,每个B+树索引对应的根节点中没有用户记录,也没有目录项记录
  • 向表中插入用户记录,先将用户记录存储到这个根节点中
  • 当根节点中的可用空间用完时继续插入记录,此时会将根节点中所有记录复制到一个新分配的页,然后对这个新页进行页分裂操作,得到另一个新页b。这时新插入的记录根据键值的大小进行分配,而根节点升级为存储目录项记录的页。

一个B+树索引的根节点自诞生之日起,便不会移动。

非叶子节点中目录项记录的唯一性

主要针对非聚簇索引。

需要保证在B+树的同一层内节点的目录项记录除页号字段外是唯一的。

对二级索引的内节点的目录项记录的内容实际上由三部分构成。

  • 索引列的值
  • 主键值
  • 页号

在这里插入图片描述

一个页面最少存储2条记录

如果一个大的目录中只存放一个子目录会导致目录的层级非常多,而最后那个存放真实数据的目录中只能存放一条记录,因此InnoDB的一个数据页至少要存放两条记录。

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

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

相关文章

「网络编程」第二讲:网络编程socket套接字(二)_ 简单UDP网络通信程序的实现

「前言」文章是关于网络编程的socket套接字方面的&#xff0c;上一篇是网络编程socket套接字&#xff08;一&#xff09;&#xff0c;下面开始讲解&#xff01; 「归属专栏」网络编程 「笔者」枫叶先生(fy) 「座右铭」前行路上修真我 「枫叶先生有点文青病」 「每篇一句」 我认…

chatgpt赋能python:Python遍历文章的SEO指南

Python遍历文章的SEO指南 Python是一种高度灵活的编程语言&#xff0c;因其易于学习和使用而为许多程序员、数据科学家和SEO专业人士所青睐。在这篇文章中&#xff0c;我们将探讨Python如何遍历文章和对SEO优化的最佳实践。 什么是Python遍历文章&#xff1f; 遍历文章是指采…

【论文阅读】Segment Anything(SAM)——可分割一切的CV大模型

【前言】随着ChatGPT席卷自然语言处理&#xff0c;Facebook凭借着Segment Anything在CV圈也算扳回一城。迄今为止&#xff0c;github的star已经超过3万&#xff0c;火的可谓一塌糊涂。作为AI菜鸟&#xff0c;可不得自己爬到巨人肩膀上瞅一瞅~ 论文地址&#xff1a;https://arxi…

NDK编译C++源码生成Android平台so文件(opencv_android)

1.准备CPP文件 编写CMakeLists.txt 编写 mk文件 android-8 is unsupported. Using minimum supported version android-16 APP_PLATFORM android-16 is higher than android:minSdkVersion 1 in ./AndroidManifest.xml 修改Application.mk中的APP_PLATFORM为 android-21 builg…

59、基于51单片机多机 NRF24L01 无线温湿度 DHT11报警系统设计(程序+原理图+PCB源文件+参考论文+开题报告+任务书+元器件清单等)

摘 要 温湿度在工农业生产中占有很重要的地位&#xff0c;是工农业生产的重要组成数据。温湿度过高会造成粮食发霉长芽&#xff0c;还会引起大棚蔬菜一系列的病害。因此对其适时准确的测量就显得尤为重要。而一般的测量过程较为复杂繁琐&#xff0c;误差还大。比如现在所使…

MySQL-SQL存储过程/触发器详解(上)

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a; 小刘主页 ♥️努力不一定有回报&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️学习两年总结出的运维经验&#xff0c;以及思科模拟器全套网络实验教程。专栏&#xf…

Python 循环与判断(详解)

❄️作者介绍&#xff1a;奇妙的大歪❄️ &#x1f380;个人名言&#xff1a;但行前路&#xff0c;不负韶华&#xff01;&#x1f380; &#x1f43d;个人简介&#xff1a;云计算网络运维专业人员&#x1f43d; 前言 在Python中&#xff0c;循环语句有两个&#xff0c;一个是fo…

Redis缓存穿透-击穿-雪崩详细分析加解决办法

Redis 缓存穿透 问题描述-如图 缓存穿透的原因 key 对应的数据在数据源并不存在&#xff0c;每次针对此key 的请求从缓存获取不到&#xff0c;请求都会压到数据源, 可能压垮数据源比如: 用一个不存在的用户id 获取用户信息&#xff0c;不论缓存还是数据库都没有&#xff0c;…

Spring Boot 集成 Redisson分布式锁(拿来即用版)

Redisson 是一种基于 Redis 的 Java 驻留集群的分布式对象和服务库&#xff0c;可以为我们提供丰富的分布式锁和线程安全集合的实现。在 Spring Boot 应用程序中使用 Redisson 可以方便地实现分布式应用程序的某些方面&#xff0c;例如分布式锁、分布式集合、分布式事件发布和订…

JSON5的作用、安装及使用

JSON5是对JSON的扩展&#xff0c;让人可以更容易手工编写和维护&#xff0c;用来减少一些JSON的限制&#xff0c;诸如json语法不支持注释&#xff0c;不支持字符串换行&#xff0c;所有的key都必须双引号&#xff0c;末尾不能有多余的逗号…等等&#xff0c;一大堆极其严格的要…

chatgpt赋能python:Python的退役与SEO

Python的退役与SEO 随着Python编程语言的流行和普及&#xff0c;越来越多的人开始使用它来开发各种类型的应用程序。但是&#xff0c;就像我们所知道的&#xff0c;所有技术都会发生变化&#xff0c;包括编程语言。因此&#xff0c;Python程式员可能会感到困惑和担忧&#xff…

JavaScript对象 (八):对象类型的使用、值类型和引用类型、函数的this指向、工厂方法创建对象、构造函数和类、new创建对象

1. 对象类型的使用 1.1 认识对象类型 基础数据类型可以存储一些简单的值&#xff0c;但是现实世界的事物抽象成程序时&#xff0c;往往比较复杂。 比如一个人&#xff0c;有自己的特性&#xff08;比如姓名、年龄、身高&#xff09;&#xff0c;有一些行为&#xff08;比如跑…

课程17:菜单管理功能实现

🚀前言 本文是《.Net Core从零学习搭建权限管理系统》教程专栏的课程(点击链接,跳转到专栏主页,欢迎订阅,持续更新…) 专栏介绍:以实战为线索,基于.Net 7 + REST + Vue、前后端分离,不依赖任何第三方框架,从零一步一步讲解权限管理系统搭建。 专栏适用于人群:We…

为什么运行时安全性重新流行起来?

容器通过以更高效和可扩展的方式创建、打包和部署应用程序&#xff0c;彻底改变了软件开发过程。 然而&#xff0c;能力越大&#xff0c;责任越大&#xff0c;对“左移安全性”的高度关注为那些在运行时忽视安全性的组织带来了风险。 通过对容器运行时安全采取多层次、全面的…

云服务器ECS_云主机_服务器托管_弹性计算-阿里云

阿里云服务器ECS&#xff08;Elastic Compute Service&#xff09;是一种安全可靠、弹性可伸缩的云计算服务&#xff0c;阿里云提供多种云服务器ECS实例规格&#xff0c;如通用算力型u1、ECS计算型c7、通用型g7、GPU实例等&#xff0c;阿里云服务器网分享阿里云服务器ECS详细介…

Linux文件操作四剑客

目录 一、grep &#xff08;一&#xff09;作用 &#xff08;二&#xff09;格式 &#xff08;三&#xff09;选项 &#xff08;四&#xff09;案例 1、查看/etc目录下所有包含bash的文件名&#xff1a;grep -rl bash /etc 2、查看/var/log目录下所有包含error的文…

读发布!设计与部署稳定的分布式系统(第2版)笔记04_集成点

1. 第一个拥有10亿用户的网站 1.1. 2016年&#xff0c;Facebook宣布其每日活跃用户数量为11.3亿 1.2. 对整个应用程序来说&#xff0c;“五个9”的可靠性远远不够&#xff0c;这每天会让成千上万的用户失望 1.3. 假如按照六西格玛质量标准来衡量&#xff0c;那么Facebook每天…

LIN-网络管理:休眠(Go To Sleep)和唤醒(Wake up)

文章目录 一、LIN总线的两种状态二、休眠模式&#xff08;Go To Sleep&#xff09;①利用诊断帧中的主机请求帧 0x3C 作休眠命令②当总线静默(没有显性和隐性电平之间的切换)4s&#xff5e;10s 时&#xff0c;节点自动进入休眠状态。 三、唤醒模式&#xff08;Wake up&#xff…

智能图片降噪-Topaz Photo AI

今天给各位小伙伴们测试了一款可以使视频智能无损放大的软件——Topaz Photo AI。 小编在很早之前也有了解过Topaz系列的软件&#xff0c;都是通过人工智能处理的&#xff0c;对小白新手们很适用&#xff0c;由于使用人工智能方面的软件或程序对硬件要求都比较高&#xff0c;因…

交通指南系统

一、实验目的 1. 掌握图的基本存储方法&#xff1b; 2. 掌握有关图的操作算法并用高级语言实现&#xff1b; 3. 熟练掌握图的两种搜索路径的遍历方法。 二、实验内容 假设以一个带权有向图表示某一区域的公交线路网&#xff0c;图中顶点代表一些区域中的重要场所&#xff0…