原文:https://www.kancloud.cn/kancloud/theory-of-mysql-index/41846
http://blog.leanote.com/post/804305986@qq.com/b1bcd4f82d6b
前言
- MySQL数据库支持多种索引类型,如BTree索引,哈希索引,全文索引等等。本文只关注于BTree索引。
数据结构及算法基础
索引的本质
-
索引的定义:索引是一类数据结构,用于帮助MySQL高效的获取数据。
数据库系统维护着满足特定查找算法的数据结构,这些数据结构以某种方式指向数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。
上图展示了一种可能的索引方式:左边是数据表,一共有两列七条记录,最左边的是数据记录的物理地址(注意逻辑上相邻的记录在磁盘上也并不是一定物理相邻的)。为了加快Col2的查找,可以维护一个右边所示的二叉查找树,每个节点分别包含索引键值和一个指向对应数据记录物理地址的指针,这样就可以运用二叉查找在θ(log2n)θ(log2n)的复杂度内获取到相应数据。虽然这是一个货真价实的索引,但是实际的数据库系统几乎没有使用二叉查找树或其进化品种红黑树实现的,原因在于树的深度过高所导致的IO次数过多。
常用的树型索引
B-Tree
-
B-Tree的定义与特征:B-Tree中的每个结点都保存一组二元组[key, data],其中key为关键字,data为除key以外的数据。
- B树又叫平衡多路查找树,一棵m阶的B树有如下特征:
- 根结点有[2,m][2,m]个子树,非根内部结点有[⌈m/2⌉,m][⌈m/2⌉,m]个子树。
- 所有叶子结点(空结点)都出现在同一层。
- 假设每个非终端结点中包含有n个关键字信息:{p0,k1,p1,k2,p2,...,kn,pn}{p0,k1,p1,k2,p2,...,kn,pn},其中kiki为关键字且关键字按顺序升序排序,pipi为指向子树的结点,则指针pi−1pi−1所指向子树中的所有结点的关键字均处于(ki−1,ki)(ki−1,ki)范围内 —— 最左子树无下限,最右子树无上限。
- B树又叫平衡多路查找树,一棵m阶的B树有如下特征:
B+Tree
- MySQL普遍使用B+Tree实现其索引结构。
-
B+Tree的定义与特征:B+Tree中的每个内结点都保存一组关键字,而data只存于叶子结点中。
- B+树又叫平衡多路查找树,一棵m阶的B+树有如下特征:
- 根结点有[2,m][2,m]个子树,非根内部结点有[⌈m/2⌉,m][⌈m/2⌉,m]个子树。
- 所有叶子结点(保存data的结点)都出现在同一层。
- 假设每个非终端结点中包含有n个关键字信息:{k1,p1,k2,p2,...,kn,pn}{k1,p1,k2,p2,...,kn,pn},其中kiki为关键字且关键字按顺序升序排序,pipi为指向子树的结点,则指针pipi所指向子树中的所有结点的关键字均处于[ki,ki+1)[ki,ki+1)范围内 —— 最左子树有下限,最右子树无上限。
- B+树又叫平衡多路查找树,一棵m阶的B+树有如下特征:
-
一般在数据库系统或文件系统中使用的B+Tree结构都在经典B+Tree的基础上进行了优化,增加了顺序访问指针。
在B+Tree的每个叶子节点增加一个指向相邻叶子节点的指针,就形成了带有顺序访问指针的B+Tree。做这个优化的目的是为了提高区间访问的性能,例如上图中如果要查询key为从18到49的所有数据记录,当找到18后,只需顺着节点和指针顺序遍历就可以一次性访问到所有数据节点,极大提到了区间查询效率。
B-Tree和B+Tree的主要区别
- B-Tree和B+Tree的主要区别:
- B+Tree中每个结点内的关键子个数与指针个数相同;而B-Tree中的关键字个数比指针个数少1。
- B+Tree中每个内节点不存储data(data只存储在叶子结点),只存储key;而B-Tree中每个结点都存储data。
- B+Tree中的查找都结束于叶子结点;而B-Tree中的查找既可能结束于叶子结点,也可结束于内结点。
B+Tree更适合做外部索引
-
为什么B+Tree更适合做外部索引?:
-
数据库系统通常将一个节点的大小设为一个页,这样每个节点只需要一次I/O就可以完全载入 —— 因此磁盘IO次数和树的高度成正比。
B-Tree中一次检索最多需要h−1h−1次I/O(根节点常驻内存),即磁盘IO的时间复杂度为θ(h)=θ(logdN)θ(h)=θ(logdN)。
-
B-Tree中每个内结点都既存关键字也存数据,而B+Tree中每个内结点只存关键字,因此每个内结点的关键字个数如下所示,从而B+Tree相对于B-Tree的高度更小,所需的磁盘IO次数更少。
- B-Tree:dd = pagesizepagesize // (keysize+datasize+pointsize)(keysize+datasize+pointsize)
- B+Tree:dd = pagesizepagesize // (keysize+pointsize)(keysize+pointsize)
-
MySQL索引实现
- 在MySQL中,不同存储引擎对索引的实现方式是不同的。
MyISAM索引实现
-
MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址。下图为主索引的结构图:
-
在MyISAM中,主索引和辅助索引在结构上没有任何区别,只是主索引要求key是唯一的,而辅助索引的key可以重复。
InnoDB索引实现
-
InnoDB引擎使用B+Tree作为索引结构,主索引的叶节点的data域存放的是数据记录本身。
-
在InnoDB中,辅助索引的叶结点的data域存放的是对应的主键值,之后在主索引结构中继续查找对应的数据记录。
聚类索引和非聚类索引
-
聚类索引:聚类索引是指数据的物理顺序与键值的逻辑顺序相同。
一个表只能有一个聚集索引,因为一个表的物理顺序只有一种情况,所以对应的聚类索引只能有一个。如果某索引不是聚集索引,则表中的行物理顺序与索引顺序不匹配 —— InnoDB中的主索引就是聚类索引,因为数据内容存于该索引结构叶子结点的data域中。
-
非聚类索引:非聚类索引是数据的物理顺序与键值的逻辑顺序不同。例如MyISAM的索引以及InnoDB的辅助索引,因为这些索引结构的叶子结点的data域中实际存储的是指向数据内容的指针或主键值。