MySql索引数据结构
什么是索引
索引是数据库管理系统中用以高速获取数据的一个排好序的数据结构。你可以把它当作书本的目录,通过书本目录的页码,我们能快速的定位到想要查询到的位置。如果没有目录,我们只能一页页的去寻找内容。
索引的选择
下面看一个例子:
图中例子左侧为数据库表,右侧为索引,通过这个红黑树索引,便可以快速找到数据对应的物理地址(注意数据即使相邻,他们的物理地址也不一定相邻,这是由于磁盘的随机读写性)。
但是实际中很少使用二叉树来充当索引的数据结构,下面逐一分析:
- 二叉树:
如图所示的二叉树,倘若存储的数据为顺序存储,二叉树无法实现快速搜索。其数据结构更像链表。 - 红黑树(二叉平衡树):
红黑树虽然解决了顺序插入的问题,但是红黑树的查找次数与树高正相关,倘若查找的节点为叶子节点,那么需要查找的次数等于树高。(想看红黑树插入过程的可以访问如下链接去查看数据结构可视化)
由于索引一般以文件形式存放在磁盘中,而磁盘I/O的速度远远低于内存I/O。因此降低磁盘I/O的次数可以极大的提高读取时间。
目前大部分数据库系统及文件系统都采用B-Tree或其变种B+Tree作为索引结构。
B树与B+树
B树
一个m阶B树的属性有:
每一个节点最多有 m 个子节点
每一个非叶子节点(除根节点)最少有 ⌈m/2⌉ 个子节点
如果根节点不是叶子节点,那么它至少有两个子节点
有 k 个子节点的非叶子节点拥有 k − 1 个关键字
所有的叶子节点都在同一层
下图为3阶B树:
B+树
一个m阶B+树的属性有:
每一个节点最多有m个字节点
每一个非叶子节点(除根节点)最少有 ⌈m/2⌉ 个子节点
如果根节点不是叶子节点,那么它至少有两个子节点
有 k 个子节点的非叶子节点拥有 k 个关键字
且B+树叶子节点的关键字从小到大有序排列,左边结尾数据都会保存右边节点开始数据的指针
下图为3阶B+树:
Mysql中的B树与B+树
B树
叶子节点具有相同深度
所有索引元素不重复
节点中的数据索引从左往右递增排序
B+树
非叶子节点不存储data,只存索引(冗余)
叶子节点包含所有索引字段
叶子节点区间有指向相邻节点的指针
索引的类型
按照数据结构分类可分为:B+树索引、Hash索引、全文(Full-text)索引。
按照物理存储分类可分为:聚集索引、辅助索引。
按照字段特性分类可分为:主键索引、普通索引、前缀索引。
按照字段个数分类可分为:单列索引、组合索引。
本文只介绍部分索引类型的大概概念,详细的可以参考如下博客
MySQL索引有哪些分类,你真的清楚吗?
聚集索引
聚集索引又称聚簇索引,它的每个叶子节点都存储了完整的表数据,叶子节点件按照id递增连接,可以方便的跨”页“查找数据。
辅助索引(二级索引)
辅助索引存储的是聚集索引所在列的值。
回表查询
由于辅助索引存储的表数据不完整,因为当索引通过辅助索引去查询到聚集索引的值后,需要去到聚集索引中查询,以获得更多的表数据。
但是回表不是一定会发生的,如果Select查询的字段在单个辅助索引中都能查询到,就不需要回表。此时的辅助索引被称为覆盖索引,或称它触发了索引覆盖。
可以用Explain命令查看SQL语句的执行计划,执行计划的Extra字段中若出现Using index,表示查询触发了索引覆盖。
两种索引引擎的实现
MyISAM实现索引
本地文件夹(Mysql文件目录\data\数据库名)中可以查看到相应数据库表存放在磁盘中的文件。
MyISAM中索引文件与数据文件分开存放,因此MyISAM中data字段存储的是表数据的物理地址。而MyISAM的主键索引和辅助索引结构没什么不同,区别只是主键索引要求key值是唯一的,但是辅助索引key值可以重复。所以MyISAM表可以没有主键。
由于MyISAM的索引文件和数据文件分开,所以MyISAM的索引都是非聚集索引。
InnoDB实现索引
主键索引:
InnoDB的主键索引包含了所有的表数据,因此又被成为聚集索引。InnoDB要求数据表必须要有聚集索引,它默认会在主键字段上建立聚集索引。
如果没有主键的话,它会将表中第一个非空的且数据唯一的列建立成聚集索引。如果不存在这种列,Mysql会字段生成一个隐式的自增的id列,并在这一列上建立聚集索引。这个字段长度为6个字节,类型为长整型。
辅助索引(二级索引):
InnoDB的辅助索引使用主键作为data域。因此InnoDB不应该使用过长的字段来做主键,否则主键索引过大,辅助索引也相应的变大。
检索辅助索引的时候,需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。
面试题
Mysql为什么选用B+树?
Mysql以”页“为单位做一个区间,通过上面的分析可以知道影响树的查找速度的因素是查找的次数,即树高。那么如果一”页“中能够存放的索引节点越多,那么树的树高越低。Mysql中的一个页的大小为16K。
通过命令show global status like "Innodb_page_size"看到该值是16384。
一页的大小是有限的,相较于B树结构,B+树data数据只存放在叶子节点中,那么在非叶子节点可以存放的索引节点就更多,这样就可以大大减少查询次数,提高效率。
为什么InnoDB必须要建立主键?
InnoDB要求数据表必须要有聚集索引,它默认会在主键字段上建立聚集索引。如果没有主键的话,它会将表中第一个非空的且数据唯一的列建立成聚集索引。如果不存在这种列,Mysql会字段生成一个隐式的自增的id列,并在这一列上建立聚集索引。这个字段长度为6个字节,类型为长整型。
让Mysql来建立字段并且维护会十分消耗Mysql的性能,因此最好自己建主键。
为什么InnoDB的主键不应该使用过长的字段?
InnoDB的辅助索引使用主键作为data域。如果主键使用过长的字段,会导致所有的辅助索引文件过大。
为什么InnoDB的主键要求单调自增?
因为InnoDB索引文件本身是一颗B+树,非单调的主键会导致在插入新记录时,数据文件为了维持B+树的递增特性而频繁的分裂调整,十分低效。而使用自增字段作为主键则可以避免B+树的频繁分裂。