MySQL InnoDB索引原理

数据库与I/O原理

数据会持久化到磁盘,查询数据是就会有I/O操作,相对于缓存操作,I/O操作的时间成本相当高昂。

I/O操作的基本单位是一个磁盘页面,比如16KB的页面大小。当数据量比较大时,单表数据就会分布在多个磁盘页面。

如果没有索引,就必须按顺序加载磁盘页面到缓存进行查找,判断数据是否存在。随着数据量的增长,磁盘I/O操作的次数也会越来越多。

因此,有必要通过一些辅助的数据结构来提交检索的速度。

从上面可以看出,想要快速读取到数据,可从以下几个方面着手

1. 如何尽量减少磁盘IO操作

2. 如何快速定位到数据所在的磁盘页面

3. 如何快速定位数据在磁盘页面内的位置

数据库索引是什么

索引是存储引擎用于快速查找记录的一种数据结构。

举个类似的例子,当我们要阅读《高性能MySQL》的第五章时,一般会先查找目录,找到第五章对应的页码,然后翻到对应页码即可。

目录一般不会超过10页,整本书有将近700页。

如果没有目录,那么我们只能顺序或者使用二分的方法来查找第五章,需要翻页的次数就会更多。

索引的作用与书籍的目录相似,用于辅助快速查找目标数据。

存储结构

记录(行)格式

InnoDB支持四种记录格式,分别是 REDUNDANT、 COMPACT、 DYNAMIC和 COMPRESSED,MySQL5.7默认是DYNAMIC格式。

下图是DYNAMIC行格式的示意图

记录头信息的格式示意图如下

MySQL InnoDB索引原理

部分字段含义

deleted_flag:顾名思义,该记录是否被删除的标志

min_rec_flag:B+树每层非叶子结点中最小的记录项的标志

n_owned: 页面中分组的

heap_on: 表示当前记录在页面堆中的相对记录

record_type: 表示当前记录的类型,0表示普通记录,1表示B+树非叶子结点的目录项记录,2表示Infimum记录,3表示Supremum记录。

next_record: 指向下一条记录,表示下一条记录的相对位置

记录示例

所有页面都有两条虚拟记录,即Infimum和Supremum。

Infimum代表页面中的最小的记录,而Supremum则代表页面中最大的记录。

数据排序

页内的记录串联成一个单向链表。

如果表有主键,会根据主键排序;

没主键有唯一非空索引,会根据该索引排序;

两者都没有,InnoDB会自动生成一个 row_id列并根据该列进行排序。

页面格式

页是InnoDB管理存储空间的基本单位,一个页的大小一般是16K。

数据页面的结构如下图

MySQL InnoDB索引原理

File Header:页面通用信息,如当前页号、上一页/下一页页号

Page Header:页面的各种状态信息,如分组数量,记录数

User Records:记录的有序链表

Free Space:页面中尚未使用的空间

Page Directory:对User Records数据进行分组,减少遍历链表的次数,加速查找

File Tailer:校验页面数据是否完整

数据查找

页面内的数据是有序的单向链表。

假设单行数据128B,而单个磁盘页面大小可以是16KB,因此一个磁盘页面最多可以存放128条数据。这样挨个查找太慢。

可以利用有序链表的特性,对有序数据进行分组,记录每组的最大值,形成一个有序分组列表。先二分查找有序分组列表,再查找分组内的数据。

这里就会涉及的行记录的n_owned和页面的Page Directory了,InnoDB分组规则如下

  1. Infimum记录所在的分组只能有一条记录

  2. Supremum记录所在的分组拥有的记录数量为1~8条

  3. 其它分组拥有的记录数量为4~8条

4.分组指向组内ID最大的行。

查找过程

下图是简化的行记录和Page Directory。

MySQL InnoDB索引原理

在上图中查找ID=17的记录

  1. 利用分组进行二分查找,

(1 + 5) / 2 = 3,分组3的最大ID为10,因此继续在右半区间查找

(3 + 5) / 2 = 4,分组最大的ID为15,17位于右半区间,又应为5 – 4 = 1,因此,17位于分组5

  1. 组内顺序查找

在分组内遍历单向链表,查找到ID=17的记录

B+树索引

B+树数据结构

B树详解,这边随笔中介绍了B树的查找、插入、删除操作,可以深入理解B数的数据结构

sql;gutter:true;collapse:false
CREATE TABLE t_student (
id   int NOT NULL AUTO_INCREMENT COMMENT   '主键ID' ,
age   int NOT NULL DEFAULT '0' COMMENT   '年龄' ,
height   int NOT NULL DEFAULT '0' COMMENT   '身高' ,
PRIMARY KEY (id),
KEY age (age)
) ENGINE=InnoDB   DEFAULT CHARSET=utf8mb4   COLLATE =utf8mb4_0900_ai_ci ROW_FORMAT=COMPACT;

聚簇索引

为了方便画图表示,下面是简化的聚簇索引各种记录格式

MySQL InnoDB索引原理

聚簇索引结构举例

MySQL InnoDB索引原理

从上图可以看出,

1)页面内记录按照主键增长的顺序构成一个单项链表

2)对于普通记录,则是一个按照主键有序的双向链表

二级索引

为了方便画图表示,下面是简化的二级索引各种记录格式

MySQL InnoDB索引原理

MySQL InnoDB索引原理

从上图可以看出,

1)页面内记录按照二级索引age增长的顺序构成一个单项链表

2)对于普通记录,则是一个按照age有序的双向链表

3)普通记录并没没有包含完整的信息,而是

回表: 数据库根据索引(非主键)找到了指定的记录所在行后,还需要根据索引上保存的主键 ID 再次到数据块里获取数据。

建立索引的原则

1.尽量使用占用空间少的索引

索引字段占用空间小,意味着单个页面可以存放更多的目录项目记录,使得B+数更加扁平,从而减少IO次数

  1. 选择频繁作为查询条件的字段作为索引

频繁作为查询条件的字段作为索引,减少查询的时间,避免全表查询。

  1. 选择区分度高的字段作为索引

例如性别只有男1女2两种情况,如果建立索引,目录项只有两条记录,意义不大。还增加了维护索引的成本。

  1. 最左匹配原则

多个字段构成联合索引时,这几个字段的顺序十分重要。

假设有联合索引

目录项记录是先按a排序,如果a相等再按b排序,如果a和b都相等,再按c排序。

如果查询条件只有(b,c),则改索引并不会生效。如果只有(a),那索引只是部分生效。

InnoDB Row Formats

《MySQL是怎么运行的》

Original: https://www.cnblogs.com/amos01/p/16488759.html
Author: Amos01
Title: MySQL InnoDB索引原理

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/611852/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • MySQL45讲之用户关注案例

    本文介绍 MySQL45 讲中提到的一个用户关注的案例,并记录下可行的处理方案。 业务背景 业务上有这样的需求,A、B两个用户,如果互相关注,则成为好友。存在两个表,关系(rela…

    数据库 2023年5月24日
    0143
  • Redis 文件事件

    事件驱动 Redis 服务器是事件驱动程序,分为 文件事件和 时&am…

    数据库 2023年6月6日
    0104
  • Centos安装mysql57

    1.1 MySQL安装 1.1.1 下载 wget 命令 yum -y install wget 1.1.2 在线下载mysql安装包 wget https://dev.mysql…

    数据库 2023年5月24日
    0120
  • Java代码如何创建GUID字符串呢?

    随机字符串是我们日常开发中,经常使用的一种字符串,那么下文将讲述具有代表性的字符串GUID GUID字符串是全球唯一标识,是我们经常使用的一种唯一标识 如:分布式系统中使用其作为表…

    数据库 2023年6月11日
    090
  • django-Celery分布式队列简单使用

    介绍: Celery 是一个简单、灵活且可靠的,处理大量消息的分布式系统,并且提供维护这样一个系统的必需工具。 它是一个专注于实时处理的任务队列,同时也支持任务调度。 worker…

    数据库 2023年6月6日
    0100
  • [mybatis]快速搭建一个mybatis程序,实现对数据的增删改查

    MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。 MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。 MyBati…

    数据库 2023年5月24日
    087
  • ansible-复制模块

    简介:临时的,在ansible中是指需要快速执行的单条命令,并且不需要保存的命令。对于复杂的命令则为 playbook。 1、复制模块 可在终端执行ansible-doc copy…

    数据库 2023年6月14日
    086
  • Java中的SPI原理浅谈

    在面向对象的程序设计中,模块之间交互采用接口编程,通常情况下调用方不需要知道被调用方的内部实现细节,因为一旦涉及到了具体实现,如果需要换一种实现就需要修改代码,这违反了程序设计的&…

    数据库 2023年6月14日
    072
  • day04-3服务器推送新闻

    多用户即时通讯系统04 4.编码实现03 4.7功能实现-服务器推送消息功能实现 4.7.1思路分析 服务器推送新闻,本质其实就是群发消息 在服务器启动一个独立线程,专门负责推送新…

    数据库 2023年6月11日
    067
  • MySQL触发器

    触发器 先来个实际的案例 触发器概述 和存储过程一样,都是嵌入在MySQL服务器中的一段程序 触发器由 事件触发,比如INSERT ,UPDATE 等用户的动作或触发某项行为,自动…

    数据库 2023年5月24日
    098
  • Ansible Playbook概览

    Ansible playbook 执行需要三步路执行: 1.编写playbook 2.定义主机清单文件 3.设置运行环境,写入配置文件 1.编写playbook Playbook使…

    数据库 2023年6月14日
    080
  • Django中使用QQ登录

    1.返回QQ登录网址的视图 请求方式: GET /oauth/qq/authorization/?next=xxx 请求参数: 查询字符串 参数名 类型 是否必须 说明 next …

    数据库 2023年6月14日
    0112
  • login方法访问不到解决过程

    背景:由于项目登录模块之前使用传统的字符验证码,干扰又太严重,经常会有输入十次以上才能蒙对的情况。于是提出让改为滑动验证码(斗鱼,B站等等)。如图所示: 原有的: 要改的: 这个实…

    数据库 2023年6月11日
    0141
  • xtrabackup2版本和xtrabackup8版本对比

    导语在使用xtrabackup8版本对mysql8版本进行备份恢复搭建从库的时候,继续使用xtrabackup2版本的方式,从xtrabackup_binlog_info 文件中找…

    数据库 2023年6月16日
    0119
  • 动手实验查看MySQL索引的B+树的高度

    一:文中几个概念 h:统称索引的高度;h1:主键索引的高度;h2:辅助索引的高度;k:非叶子节点扇区个数。 二:索引结构 叶子节点其实是双向链表,而叶子节点内的行数据是单向链表,该…

    数据库 2023年6月14日
    0103
  • 如何重置postgresql用户密码

    如何重置postgresql用户密码 场景: 打算新建一个postgresql的数据库 FooDB 并把所有者权限赋给用户 foo 正常操作应该是:先创建用户foo,再用foo身份…

    数据库 2023年6月9日
    0108
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球