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)

大家都在看

  • Facade 外观(结构型)

    Facade 外观 (结构型) 一:描述: Facade 外观模式是为子系统至客户端之间提供简单的一致的接口,来降低耦合度。 二:模式图 三:实现代码简单例子: 1 、业务模块; …

    数据库 2023年6月11日
    093
  • 微服务架构设计模式

    内容简介 成功地开发基于微服务架构的应用软件,需要掌握一系列全新的架构思想和实践。在这本书籍中解释了 44 个架构设计模式,这些模式用来解决诸如服务拆分、事务管理、查询和跨服务通信…

    数据库 2023年6月6日
    0136
  • 重新学习数据库(1)

    单元概述 通过本章的学习能够了解MySQL结构查询语言的概念,掌握SELECT查询语句的基本语法,掌握SELECT查询语句中过滤条件的使用,掌握过滤条件中比较运算符和逻辑运算符的使…

    数据库 2023年5月24日
    060
  • go 切片的扩容

    slice type slice struct { array unsafe.Pointer len int cap int } func makeslice(et *_type,…

    数据库 2023年6月9日
    047
  • 手把手教你使用 Java 在线生成 pdf 文档

    一、介绍 在实际的业务开发的时候,研发人员往往会碰到很多这样的一些场景,需要提供相关的电子凭证信息给用户,例如网银/支付宝/微信购物支付的电子发票、订单的库存打印单、各种电子签署合…

    数据库 2023年6月14日
    0107
  • 3、数组、集合、Lambda、Stream与Optional类

    一、数组: 数组保存在JVM堆内存中 1、数组的创建: (1)、一维数组创建方式一: //一维数组方式一 Integer[] array01 = {1,2,3}; System.o…

    数据库 2023年6月6日
    079
  • JWT简介

    JWT简介 在用户注册或登录后,我们想记录用户的登录状态,或者为用户创建身份认证的凭证。我们不再使用Session认证机制,而使用Json Web Token认证机制。 (1) 什…

    数据库 2023年6月14日
    094
  • Redis SCAN命令

    获取指定前缀的key 需求描述: Redis中有大量以xxx开头的key,在不使用keys命令的情况下,如何快速获取这些前缀的key 解决方案: redis自带的scan命令可以解…

    数据库 2023年6月14日
    074
  • 你是否听说过 HashMap 在多线程环境下操作可能会导致程序死循环?

    作者:炸鸡可乐原文出处:www.pzblog.cn 一、问题描述 经常有些面试官会问, 是否了解过 HashMap 在多线程环境下使用时可能会发生死循环,导致服务器 cpu 100…

    数据库 2023年6月14日
    073
  • [Mysql]如何设置root密码(8.0+)

    在ubuntu上安装mysql时默认root账号是没有密码的,可以先用 mysql进入mysql,然后输入下面这个( mynewpassword改成要设置的密码): ALTER U…

    数据库 2023年6月16日
    073
  • 实现一个简单的Database1(译文)

    “What I cannot create, I do not understand.” – Richard Feynman I’m build…

    数据库 2023年6月11日
    096
  • MongoDB中如何优雅地删除大量数据

    删除大量数据,无论是在哪种数据库中,都是一个普遍性的需求。除了正常的业务需求,我们需要通过这种方式来为数据库”瘦身”。 为什么要”瘦身&#822…

    数据库 2023年6月11日
    080
  • 【StoneDB】从库如何规避不支持的DML和DDL

    (以下情况仅针对StoneDB 1.0版本不支持的部分DML和DDL操作,StoneDB 2.0及以上版本将无需此类操作)在主从复制中,主库的任何更新都将同步到从库。如果从库不想重…

    数据库 2023年5月24日
    085
  • 自学SQL网题目解答与笔记

    Id Title Director Year Length_minutes 1 Toy Story John Lasseter 1995 81 2 A Bug’s Li…

    数据库 2023年5月24日
    093
  • 如何在MySQL中进行简单的增删改查

    — 创建dept表并设置主键create table dept(deptno int(2) primary key ,dname varchar(14),loc var…

    数据库 2023年6月16日
    097
  • HTML5基础知识

    作者导言: 引用偶像刘德华的一句话 “学到的就要教人,赚到的就要给人”! 以下是关联的web前端基础知识文章,通过这些文章,您既可以系统地学习和了解这些知识…

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