索引
在前面的文章中,我们分析了索引的分类、创建、删除以及索引的创建原则等,知道了创建索引的目的就是为了加速对表中的数据行的检索而创造的一种分散存储的数据结构。那么索引的底层结构是什么呢?
底层实现
数据库索引是存储在磁盘上的,当数据量大时,就不能把整个索引全部加载到内存了,只能逐一加载每一个磁盘块(对应索引树的节点),索引树越低,越“矮胖”,磁盘I/O次数就少。
MySQL支持两种索引,一种的B-树索引,一种是哈希索引。 并且MySQL的索引是由存储引擎来实现,不同的存储引擎实现方式不同。目前主流的有 MyISAM(非聚集索引)和InnoDB(聚集索引)。
哈希索引
只有memory(内存)存储引擎支持哈希索引,哈希索引用索引列的值计算该值的hashCode,然后在hashCode相应的位置存执该值所在行数据的物理位置,因为使用散列算法,因此访问速度非常快,但是一个值只能对应一个hashCode,而且是散列的分布方式,因此哈希表只适用于数据都在内存上的存储引擎,数据不落盘(所以使用的很少),如果在磁盘上,一个桶里的节点代表一个磁盘IO的话,效率太低;并且哈希索引中的元素没有任务顺序可言,只能进行等值比较。所以使用的场景并不多。
B树
B树是平衡搜索多叉树,设树的度为2d(d>1),高度为h,B树要满足以下条件:
- 每个叶子结点的高度一样,等于h;
- 每个非叶子结点由n-1个key和n个指针point组成,其中d<=n<=2d,key和point相互间隔,结点两端一定是key;
- 叶子结点指针都为null;
- 非叶子结点的key都是[key,data]二元组,其中key表示作为索引的键,data为键值所在行的数据;
B树优点:在BTree的机构下,就可以使用二分查找的查找方式,查找复杂度为h*log(n),一般来说树的高度是很小的,一般为3左右,因此BTree是一个非常高效的查找结构。
B+树
B+树是B树的一个变种,设d为树的度数,h为树的高度,B+树和B树的不同主要在于:
- B+树中的非叶子结点不存储数据,只存储键值;
- B+树的叶子结点没有指针,所有键值都会出现在叶子结点上,且key存储的键值对应data数据的物理地址;
- B+树的每个非叶子节点由n个键值key和n个指针point组成;
1、B+树:平衡(Balance)树,最早的平衡二叉树演化而来,但是B+树不是一个二叉树。
2、B+树是为磁盘或其他直接存取辅助设备设计的一种平衡查找树,在B+树中,所有的记录节点都是按照键值大小顺序存在同一层的叶子节点,由叶子节点指针进行相连。
3、B+树在数据库中的特点就是高扇出,因此在数据库中B+树的高度一般都在2~4层,这也就是说查找一个值的记录时,最多只需要2到4次IO,当前的机械硬盘每秒至少可以有100次IO,2-4次IO意味着查询时间只需要0.02到0.04秒。
4、B+树索引并不能找到一个给定键值的具体行,B+树索引能找到的只是被查找的键值所在行的页,然后数据库把页读到内存,再内存中进行查找,最后找到要查找的数据。
5、数据库中B+树索引可以分为,聚集索引和非聚集索引,但是不管是聚集索引还是非聚集索引,其内部都是B+树实现的,即高度是平衡的,叶子节点存放着所有的数据,聚集索引和非聚集索引不同的是,叶子节点是否存储的是一整行信息。每张表只能有一个聚集索引。
6、B+树的每个数据页(叶子节点)是通过一个双向链表进行链接,数据页上的数据的顺序是按照主键顺序存储的。
B树和B+树
- 磁盘读写代价更低。
B-树的每一个节点,存了关键字和对应的数据地址,而B+树的非叶子节点只存关键字,不存数据地址,因此B+树的每一个非叶子节点存储的关键字是远远多于B-树的,B+树的叶子节点存放关键字和数据,因此,从树的高度上来说,B+树的高度要小于B-树,使用的磁盘I/0次数少,因此查询会更快一些。 - 查询数据的速度稳定
B-树由于每个节点都存储关键字和数据,因此离根节点进的数据,查询的就快,离根节点远的数据,查询的就慢;B+树所有的数据都存在叶子节点上,因此在B+树上搜索关键字,找到对应数据的时间是比较平均的,没有快慢之分。 - 可做区间查找
在B-树上如果做区间査找,遍历的节点是非常多的;B+树所有叶子节点被连接成了有序链表结构,因此做整表遍历和区间査找是非常容易的。
聚集索引和非聚集索引
MySQL中最常见的两种存储引擎分别是MyISAM和InnoDB,分别实现了非聚簇索引和聚簇索引。
-
聚集索引的顺序就是数据的物理存储顺序
-
非聚集索引顺序与数据物理排列顺序无关
MyISAM
- MyISAM存储引擎采用的是非聚集索引,非聚集索引的主索引和辅助索引几乎是一样的,只是主索引不允许重复,不允许空值,他们的叶子结点的key都存储指向键值对应的数据的物理地址。
- 非聚集索引的数据表和索引表是分开存储的。
- 非聚集索引中的数据是根据数据的插入顺序保存。因此非聚簇索引更适合单个数据的查询。插入顺序不受键值影响。
- 只有在MyISAM中才能使用FULLTEXT索引。(mysql5.6以后innoDB也支持全文索引)
InnoDB——聚集索引
- 聚集索引的主索引的叶子结点存储的是键值对应的数据本身,辅助索引的叶子结点存储的是键值对应的数据的主键键值。因此主键的值长度越小越好,类型越简单越好。
- 聚集索引的数据和主键索引存储在一起。
- 聚集索引的数据是根据主键的顺序保存。因此适合按主键索引的区间查找,可以有更少的磁盘I/O,加快查询速度。但是也是因为这个原因,聚集索引的插入顺序最好按照主键单调的顺序插入,否则会频繁的引起页分裂,严重影响性能。
- 在InnoDB中,如果只需要查找索引的列,就尽量不要加入其它的列,这样会提高查询效率。
聚集索引和非聚集索引的适用场景
-
使用主键索引的时候,更适合使用聚集索引,因为聚集索引只需要查找一次,而非聚集索引在查到数据的地址后,还要进行一次I/O查找数据。
-
因为聚集辅助索引存储的是主键的键值,因此可以在数据行移动或者页分裂的时候降低成本,因为这时不用维护辅助索引。但是由于主索引存储的是数据本身,因此聚集索引会占用更多的空间。
-
聚集索引在插入新数据的时候比非聚集索引慢很多,因为插入新数据时需要检测主键是否重复,这需要遍历主索引的所有叶节点,而非聚集索引的叶节点保存的是数据地址,占用空间少,因此分布集中,查询的时候I/O更少,但聚集索引的主索引中存储的是数据本身,数据占用空间大,分布范围更大,可能占用好多的扇区,因此需要更多次I/O才能遍历完毕。