索引
什么是索引?
MySQL 官方对索引的定义为:索引(Index)是帮助 MySQL 高效获取数据的数据结构。提取句子主干,就可以得到索引的本质:索引是数据结构。类似于字典的目录页,通过目录页我们可以快速找到目标字所在的页码,然后在当前页中寻找;在MySQL通过索引快速定位到目标资源所在的页,然后查找行记录。
在进一步了解索引之前,我们先来了解一些相关概念。
前置概念
磁盘
磁道
将磁盘的盘面按一个个同心圆圈进行划分,每个同心圆就是一条磁道。
扇区
将磁道以多个对称的通过盘面的圆心的直线划分成多个弧线,每个弧线段就是一片扇区。扇区是磁盘的最小存储单元,通常为512Byte。
柱面
一般来说,一个磁盘由多个盘片所组成,多个盘面的同一磁道组成了一个柱面。
块
逻辑意义上的存储单位,用于操作系统与磁盘打交道。(Windows下的簇与Linux下的块属于同等概念)
页
逻辑意义上的存储单位,用于操作系统与内存打交道。
局部性原理
时间局部性
如果一个信息项正在被访问,那么在近期它很可能还会被再次访问。
很显然,用过的数据很可能再次被用到。
空间局部性
在最近的将来将用到的信息很可能与现在正在使用的信息在空间地址上是临近的。
正在使用的某个数据地址旁边的数据很可能被用到,比如某个数组、集合等等。
顺序局部性
在典型程序中,除转移类指令外,大部分指令是顺序进行的。顺序执行和非顺序执行的比例大致是 5:1。此外,对大型数组访问也是顺序的。
指令的顺序执行、数组的连续存放等是产生顺序局部性的原因。
正在执行的某个指令以及还在排队等候处理的指令,大部分是按照顺序来执行的。
IO时间消耗
寻道时间
对于磁盘上的数据,系统将数据逻辑地址传给磁盘,磁盘的控制电路解析出物理地址(磁道、扇区),随后磁头移动到相应的磁道。磁头移动到目标磁道的过程消耗的时间即为寻道时间。
旋转时间
磁盘旋转,将对应的扇区转到磁头下消耗的时间。
读取时间
将数据从磁盘读取到内存中所消耗的时间。
在一次IO中,时间消耗主要为寻道时间+旋转时间,读取时间可以忽略不计。
磁盘预读
为了减少IO时间消耗并结合局部性原理,MySQL在读取数据时无论数据的大小都会从目标记录的当前扇区开始读取一整页的数据存入到内存中使用。mysql innodb 的默认的页大小是 16k。
这里为什么说从当前扇区开始读取1页数据即符合了局部性原理?数据在存储时会按照B+树的结构进行存储,这种存储在物理和逻辑两个层面上都是连续的,因而简单读取1页数据就能做到减少局部数据的IO。(这里的物理连续是指单个叶子节点内部的数据,主键值递增的单向链表;叶子节点间的数据存储在物理上并不能保证一定连续,它们通过双向链表进行连接。)
索引的读写与结构
MySQL数据库支持多种存储引擎,不同的引擎其所使用的索引类型也不同,如Hash索引、全文索引、BTree索引等。这里我们主要讨论Innodb支持的BTree索引。
底层数据结构
B+树
B+树是InnoDB和Myisam引擎所选择的索引结构,并优化带有顺序访问指针,将叶子节点通过双向链表串起来。
红黑树
本质上是平衡二叉树。
B 树
平衡 m 阶查找树,m 阶的 B 树中结点最多有 m 个分支。
为什么是B+树而不是Hash 索引、红黑树、B Tree
首先我们需要知道选用合适的数据结构进行索引存储的目的是什么:
从图2我们可以看出,磁盘搜索的延时与内存搜索延时根本不在一个量级上,因此,优化数据查询的关键在于减少磁盘IO的次数。为了减少磁盘IO,我们采用了索引来更快的定位到数据。但这还不够,索引本身也是数据,也需要进行IO操作去进行寻找。因此,