浅析innoDB索引结构
浅析innoDB索引结构
No.1
引言
在上两期分享了 innoDB锁机制,innoDB存储结构,相信大家对innoDB也有了一定的了解, 今天将在上期存储结构的基础上,详细聊聊我们平时使用较多的innoDB索引是怎么回事?
No.2
课前提问
-
为什么InnoDB表必须有主键,并且推荐使用整型的自增主键?没有主键怎么办?
-
为什么非主键索引结构叶子节点存储的是主键值?
-
不考虑其他因素,主键长度8B+指针6B,一行数据1k,Innodb引擎三层的索引结构能存多少数据?
No.3
磁盘读取
为什么要讲这一章
为啥子要讲磁盘读取呢,我们不是要讲索引结构吗? 这有因必有果,如果说索引是果的话,那磁盘读取就是因。相信阅读过上篇innoDB存储结构的同学都知道,对于innoDB来讲,不管是索引还是数据本身都是以文件的形式存储在磁盘上的,那么对于mysql数据的操作本质上就是磁盘I\O,换而言之,磁盘I\O的速度决定了数据库的操作速度,所以下面我们就来先研究研究磁盘I\O是咋回事。
磁盘构造
先来介绍几个基本概念
-
磁头:由图所示,磁头就是与盘片磁道直接接触的东西,通过盘片上的凹凸(0,1)进行数据读取。
-
磁道:可以理解为盘片就是由一圈圈磁道组成的,最外侧定义为0磁道,依次向内。
-
扇区:可以理解为我们用分蛋糕的形式将盘片分割,每一块就是一个扇区。
-
柱面:由图可知一个磁盘是由多个磁头和盘片组成的,主轴旋转也是多磁头同步的,所以我们把多盘片的同磁道组成的圆柱面叫做柱面,读写数据也是按柱面操作的,写满一个柱面才会写下一个柱面。
磁盘读写流程
磁盘对数据的读取我们可以大致分为3步
-
寻道:找到数据所在的磁道(柱面)(此步最耗时,物理结构改变)
-
旋转:找到对应扇区
-
数据传输:和主存交互数据
磁盘I\O时间 = 寻道时间+旋转时间+数据传输时间
顺序读取和随机读取
由上述流程可知,对读取时间影响最大的就是寻道这个步骤,显然若要用到的数据都在一个磁道扇面的话,那么仅需一次寻道就可加载完成,若目标数据分布在盘片的各个磁道上,则需要多次寻道,时间自然更长。
局部性原理
当一个数据被用到时,其附近的数据也通常会马上被使用。
预读机制
预读:页是计算机管理存储器的逻辑块,硬件及操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页(在许多操作系统中,页的大小通常为4k)
主存和磁盘以页为单位交换数据。当程序要读取的数据不在主存中时,会触发一个缺页异常,此时系统会向磁盘发出读盘信号,磁盘会找到数据所在当前页并向后连续读取一页或几页载入内存中,这个操作就叫做预读。
根据上述的局部性原理,操作系统对于磁盘I\O做了预读来进行优化,这使得顺序读取的磁盘IO次数进一步减少,从而加快加载速度。
No.4
innoDB的B+树索引
什么是索引
MySQL官方对索引的定义为: 索引(Index)是帮助MySQL高效获取数据的数据结构。
索引的本质:索引是数据结构,而且是实现了高级查找算法的数据结构
通俗的理解:索引就好比一本书的目录,可以让你直接找到目标的页数。
为啥要用索引
当有了索引后,我们搜索数据的流程变为:
-
磁盘读取索引信息
-
根据索引信息确定目标记录位置
-
再次进行磁盘IO则可读取到目标数据
相对于无索引遍历数据的方式大大减少IO次数,增加读写性能。一般使用磁盘I/O次数评价索引结构的优劣。
B+树数据结构
m阶的B+树定义如下:
-
根节点至少有两个孩子
-
每个中间节点都包含k个元素和k个孩子,
其中 m/2 <= k <= m
-
每一个叶子节点都包含k-1个元素,
其中 m/2 <= k <= m
-
非叶节点不存储数据,只存索引
-
所有数据存在叶子节点,有序单链表
本文因重点不在于对数据结构的讲解,所以为什么innoDB选用B+树而不是选用二叉平衡树,链表等等其他数据结构做索引,B+相对于B树的优点等问题,请自行学习,或等待以后的分享中讲解。
innoDB的B+树
-
叶子节点为实际数据,并且为双向链表
-
每个节点等同于innoDB存储结构中的一个页,这也保证了每个节点的数据,物理和逻辑上都在同一个页中,一个节点的载入就是一个页的载入。
-
对于innoDB的B+树来说,树的阶=(一个页 / 索引字段长度),这也可以解释为什么不推荐索引字段过长,因为索引字段过长会导致阶变小,从而导致树的高度变高,最终导致索引文件过大。
由此我就可以得出课前提问的第3问的答案,具体流程在文章结尾统一给出。
innoDB基于B+树的聚集索引
1. 对于innoDB来说 , 数据文件本质上就是B+树实现的聚集索引文件,叶子节点存储了具体的行数据,这也就解释了开篇的问题,innoDB是必须要有主键的,若没有设置innoDB也会默认生成主键,但此主键不可见.
2. 因为磁盘由预读机制,并且innoDB数据页内物理地址是连续的(页和页之间是逻辑连续),所以推荐使用整形的自增主键,这样能保证数据文件的存储是顺序存储,保证了数据按序填满数据页。
innoDB基于B+树的非聚集索引
对于非主键建立的非聚集索引来说,区别在于叶子节点存储的不在是数据,而是主键值。
No.5
数据页结构(节点)
No.6
根据索引查找数据
innoDB通过索引结构查找数据的流程为:
以上图为例寻找id=11的行数据。
-
磁盘io加载索引段,11<21根据索引确定在p1页
-
磁盘io加载p1页,11=11确定数据在p11页
-
磁盘io加载p11页数据至主存,通过页内的稀疏索引进行二分查找到目标数据行
No.7
习题答案
问题一:为什么InnoDB表必须有主键,并且推荐使用整型的自增主键?没有主键怎么办?
-
必须有主键,因为数据文件本质是聚集索引文件
-
若未指定innoDB或默认指定隐式主键
-
整形自增主键,使得innoDB的数据文件在物理结构上数据连续,搭配磁盘IO的预读,性能更好,并且在检索时比对索引值效率高,减少树的高度和分裂次数
问题二:为什么非主键索引结构叶子节点存储的是主键值?
-
节省空间,行数据只存储一份,在主键形成的聚集索引文件中。
-
提高主键复用,保证主键索引一致性
问题三:不考虑其他因素,主键长度8B+指针6B,一行数据1k,Innodb引擎三层的索引结构能存多少数据?
第一层根节点内的元素数= (16KB/14B)= 1170个
第二层节点数 = 第一层根节点内的元素数 = 1170个
叶子节点数量 = 1170*1170 = 1,368,900个
每个叶子大小 = 页的大小 = 16KB
每个叶子内行数 = 16KB/1KB = 16条
数据行数 = 叶子节点数量*每个叶子内行数
= 1,368,900*16
= 21,902,400条
END
希望大家踊跃留言讨论