少年听雨歌楼上,红烛昏罗帐。壮年听雨客舟中,江阔云低,断雁叫西风。
而今听雨僧庐下,鬓已星星也。悲欢离合总无情,一任阶前,点滴到天明。
——《虞美人·听雨》
一、前言
索引是数据库很重要的一部分,今天这篇文章主要讲述几种树结构以及Mysql用到的树结构。
二、数据库索引的认识
数据库索引是一种为了加速数据表中行记录检索的数据结构。
数据库的索引存储于磁盘。
索引在 MySQL 数据库中分三类:
- B+ 树索引
- Hash 索引
- 全文索引
mysql查询某表表空间与所有空间语句:
select
CONCAT(ROUND(SUM(data_LENGTH)/(1024*1024),2),' M') AS '表空间',
CONCAT(ROUND(SUM(INDEX_LENGTH)/(1024*1024),2),' M') AS '索引空间'
FROM
information_schema.`TABLES`
WHERE TABLE_SCHEMA='push'
AND TABLE_NAME='message'
push-数据库名;message-表名。
三、几种树结构讲解
3.1 二叉树
首先,让我们先看一张图:
从图中可以看到,我们为 user 表(用户信息表)建立了一个二叉查找树的索引。
图中的圆为二叉查找树的节点,节点中存储了键(key)和数据(data)。键对应 user 表中的 id,数据对应 user 表中的行数据。
二叉查找树的特点就是任何节点的左子节点的键值都小于当前节点的键值,右子节点的键值都大于当前节点的键值。顶端的节点我们称为根节点,没有子节点的节点我们称之为叶节点。
如果我们需要查找 id=12 的用户信息,利用我们创建的二叉查找树索引,查找流程如下:
- 将根节点作为当前节点,把 12 与当前节点的键值 10 比较,12 大于 10,接下来我们把当前节点>的右子节点作为当前节点。
- 继续把 12 和当前节点的键值 13 比较,发现 12 小于 13,把当前节点的左子节点作为当前节点。
- 把 12 和当前节点的键值 12 对比,12 等于 12,满足条件,我们从当前节点中取出 data,即 id=12,name=xm。
利用二叉查找树我们只需要 3 次即可找到匹配的数据。如果在表中一条条的查找的话,我们需要 6 次才能找到。
3.2 平衡二叉树(AVL树)
平衡二叉树就是为了解决二叉查找树退化成一颗链表而诞生了。
AVL树图解:
平衡树具有如下特点:
1、具有二叉查找树的全部特性。
2、每个节点的左子树和右子树的高度差至多等于1。
上面我们讲解了利用二叉查找树可以快速的找到数据。但是,如果上面的二叉查找树是这样的构造:
这个时候可以看到我们的二叉查找树变成了一个链表。如果我们需要查找 id=17 的用户信息,我们需要查找 7 次,也就相当于全表扫描了。
导致这个现象的原因其实是二叉查找树变得不平衡了,也就是高度太高了,从而导致查找效率的不稳定。
为了解决这个问题,我们需要保证二叉查找树一直保持平衡,就需要用到平衡二叉树了。
平衡二叉树又称 AVL 树,在满足二叉查找树特性的基础上,要求每个节点的左右子树的高度差不能超过 1。
下面是平衡二叉树和非平衡二叉树的对比:
由平衡二叉树的构造我们可以发现第一张图中的二叉树其实就是一棵平衡二叉树。
平衡二叉树保证了树的构造是平衡的,当我们插入或删除数据导致不满足平衡二叉树不平衡时,平衡二叉树会进行调整树上的节点来保持平衡。具体的调整方式这里就不介绍了。
平衡二叉树相比于二叉查找树来说,查找效率更稳定,总体的查找速度也更快。
3.3 红黑树
红黑树是一种自平衡二叉查找树,是一种特殊的平衡二叉树,是计算机科学领域中的一种数据结构。
红黑树的应用:java中的TreeMap、HashMap等。
红黑树出现原因:
虽然平衡树解决了二叉查找树退化为近似链表的缺点,能够把查找时间控制在 O(logn),不过却不是最佳的,因为平衡树要求每个节点的左子树和右子树的高度差至多等于1,这个要求实在是太严了,导致每次进行插入/删除节点的时候,几乎都会破坏平衡树的第二个规则,进而我们都需要通过左旋和右旋来进行调整,使之再次成为一颗符合要求的平衡树。
显然,如果在那种插入、删除很频繁的场景中,平衡树需要频繁着进行调整,这会使平衡树的性能大打折扣,为了解决这个问题,于是有了红黑树。
红黑树图示:
红黑树的特性:
- 每个结点是黑色或者红色。
- 根结点是黑色。
- 每个叶子结点(NIL)是黑色。 [注意:这里叶子结点,是指为空(NIL或NULL)的叶子结点!]
- 如果一个结点是红色的,则它的子结点必须是黑色的。
- 每个结点到叶子结点NIL所经过的黑色结点的个数一样的。(确保没有一条路径会比其他路径长出两倍,所以红黑树是相对接近平衡的二叉树的!)
上面的这些约束保证了这个树大致上是平衡的,这也决定了红黑树的插入、删除、查询等操作是比较快速的。
- 如果新节点的父节点为黑色节点,那么插入一个红点将不会影响红黑树的平衡,此时插入操作完成。红黑树比AVL树优秀的地方之一在于黑父的情况比较常见,从而使红黑树需要旋转的几率相对AVL树来说会少一些。
- 如果新节点的父节点为红色节点,这时就需要进行一系列操作以保证整棵树红黑性质;例如左旋、右旋。
红黑树的左旋操作:
假设待左旋的结构中,P为父节点,S为孩子节点。左旋操作后,S节点代替P节点的位置,P节点成为S节点的左孩子,S节点的左孩子成为P节点的右孩子。红黑树的右旋操作:
假设待右旋的结构中,P为父节点,S为孩子节点。右旋操作后,S节点代替P节点的位置,P节点成为S节点的右孩子,S节点的右孩子成为P节点的左孩子。
扩展文章:红黑树原理
红黑树原理和算法介绍
四、B树
4.1 B树
表的数据和索引都是放在磁盘中的,和内存相比,读取磁盘的速度比读取内存要慢上百倍千倍,所以我们应尽量减少读取磁盘的次数。另外,从磁盘中读取数据时,都是按照磁盘块来读取的,并不是一条一条的读。如果我们用树这种数据结构作为索引的数据结构,那我们每查找一次数据就需要从磁盘中读取一个节点,也就是我们说的一个磁盘块。
按照少读取磁盘的原则,平衡二叉树就有缺点了:平衡二叉树,每个节点只存一个键值和数据,那说明每个磁盘块仅存一个键和数据,如果海量数据呢?可以想象节点会很多,高度也会特别高。查询数据时磁盘会经过很多次IO,查询效率会极低。
为了解决平衡二叉树的这个弊端,我们应该寻找一种单个节点可以存储多个键和数据的平衡树。也就是B树。
B树(Balance tree),又叫平衡多路查找树。
B树与二叉平衡树最大的不同在于,B树的结点可以有许多子女,从几个到几千个。
下图就是一颗B树:
图中的 p 节点为指向子节点的指针,二叉查找树和平衡二叉树其实也有,因为图的美观性,被省略了。
页:图中的每个节点称为页,页就是我们上面说的磁盘块,在 MySQL 中数据读取的基本单位都是页,所以我们这里叫做页更符合 MySQL 中索引的底层数据结构。
关键字:即上图中的17,35;关键字个数=子节点岔路树-1
从上图可以看出,B 树相对于平衡二叉树,每个节点存储了更多的键值(key)和数据(data),并且每个节点拥有更多的子节点,子节点的个数一般称为阶,上述图中的 B 树为 3 阶 B 树,高度也会很低。
基于以上特性,B树查找数据读取磁盘的次数会很少,数据查找效率会比平衡二叉树高很多。
假如我们要查找 id=28 的用户信息,那么我们在上图 B 树中查找的流程如下:
- 先找到根节点也就是页 1,判断 28 在键值 17 和 35 之间,那么我们根据页 1 中的指针 p2 找到页 3。
- 将 28 和页 3 中的键值相比较,28 在 26 和 30 之间,我们根据页 3 中的指针 p2 找到页 8。
- 将 28 和页 8 中的键值相比较,发现有匹配的键值 28,键值 28 对应的用户信息为(28,bv)。
五、B+数
B+ 树是对 B 树的进一步优化。让我们先来看下 B+ 树的结构图:
B+树与B树的区别:
- 根节点和支节点不报存数据区,数据区都保存在叶子节点的末尾上。
- 叶子节点中的数据是通过单向链表连接的,页与页之间形成双向链表,有利于排序、范围查找等功能。
根节点和支节点不保存数据区,数据区都保存在叶子节点的末尾上。这个区别解决了什么问题?
——拿InnoDb来说,根节点和支节点不保存数据区,InnoDb每一页大小为16k,每一页可以存的关键字就多了很多,减少了IO的读取。
上图中的 B+ 树索引就是 InnoDB 中 B+ 树索引真正的实现方式,准确的说应该是聚集索引(聚集索引和非聚集索引下面会讲到)。
B+树的优势:
- B+树是B-树的Plus版本
- IO效率高于B-树
- 基于索引的表扫描性能大于B-树
- 排序能力强于B树
- 基于索引的查询,B树更趋于稳定