首先,让我们先看一张图:
从图中可以看到,我们为 user 表(用户信息表)建立了一个二叉查找树的索引。
图中的圆为二叉查找树的节点,节点中存储了键(key)和数据(data)。键对应 user 表中的 id,数据对应 user 表中的行数据。
二叉查找树的特点就是任何节点的左子节点的键值都小于当前节点的键值,右子节点的键值都大于当前节点的键值。顶端的节点我们称为根节点,没有子节点的节点我们称之为叶节点。
如果我们需要查找 id=12 的用户信息,利用我们创建的二叉查找树索引,查找流程如下:
利用二叉查找树我们只需要 3 次即可找到匹配的数据。如果在表中一条条的查找的话,我们需要 6 次才能找到。
上面我们讲解了利用二叉查找树可以快速的找到数据。但是,如果上面的二叉查找树是这样的构造:
这个时候可以看到我们的二叉查找树变成了一个链表。如果我们需要查找 id=17 的用户信息,我们需要查找 7 次,也就相当于全表扫描了。 导致这个现象的原因其实是二叉查找树变得不平衡了,也就是高度太高了,从而导致查找效率的不稳定。为了解决这个问题,我们需要保证二叉查找树一直保持平衡,就需要用到平衡二叉树了。 平衡二叉树又称 AVL 树,在满足二叉查找树特性的基础上,要求每个节点的左右子树的高度差不能超过 1。
下面是平衡二叉树和非平衡二叉树的对比:
由平衡二叉树的构造我们可以发现第一张图中的二叉树其实就是一棵平衡二叉树。
平衡二叉树保证了树的构造是平衡的,当我们插入或删除数据导致不满足平衡二叉树不平衡时,平衡二叉树会进行调整树上的节点来保持平衡。具体的调整方式这里就不介绍了。平衡二叉树相比于二叉查找树来说,查找效率更稳定,总体的查找速度也更快。
因为内存的易失性。一般情况下,我们都会选择将 user 表中的数据和索引存储在磁盘这种外围设备中。但是和内存相比,从磁盘中读取数据的速度会慢上百倍千倍甚至万倍,所以,我们应当尽量减少从磁盘中读取数据的次数。另外,从磁盘中读取数据时,都是按照磁盘块来读取的,并不是一条一条的读。如果我们能把尽量多的数据放进磁盘块中,那一次磁盘读取 *** 作就会读取更多数据,那我们查找数据的时间也会大幅度降低。如果我们用树这种数据结构作为索引的数据结构,那我们每查找一次数据就需要从磁盘中读取一个节点,也就是我们说的一个磁盘块。我们都知道平衡二叉树可是每个节点只存储一个键值和数据的。那说明什么?说明每个磁盘块仅仅存储一个键值和数据!那如果我们要存储海量的数据呢?
可以想象到二叉树的节点将会非常多,高度也会极其高,我们查找数据时也会进行很多次磁盘 IO,我们查找数据的效率将会极低!
为了解决平衡二叉树的这个弊端,我们应该寻找一种单个节点可以存储多个键值和数据的平衡树。也就是我们接下来要说的 B 树。
B 树(Balance Tree)即为平衡树的意思,下图即是一棵 B 树:
图中的 p 节点为指向子节点的指针,二叉查找树和平衡二叉树其实也有,因为图的美观性,被省略了。
图中的每个节点称为页,页就是我们上面说的磁盘块,在 MySQL 中数据读取的基本单位都是页,所以我们这里叫做页更符合 MySQL 中索引的底层数据结构。
从上图可以看出,B 树相对于平衡二叉树,每个节点存储了更多的键值(key)和数据(data),并且每个节点拥有更多的子节点,子节点的个数一般称为阶,上述图中的 B 树为 3 阶 B 树,高度也会很低。
基于这个特性,B 树查找数据读取磁盘的次数将会很少,数据的查找效率也会比平衡二叉树高很多。
假如我们要查找 id=28 的用户信息,那么我们在上图 B 树中查找的流程如下:
B+ 树是对 B 树的进一步优化。让我们先来看下 B+ 树的结构图:
根据上图我们来看下 B+ 树和 B 树有什么不同:
通过上图可以看到,在 InnoDB 中,我们通过数据页之间通过双向链表连接以及叶子节点中数据之间通过单向链表连接的方式可以找到表中所有的数据。
MyISAM 中的 B+ 树索引实现与 InnoDB 中的略有不同。在 MyISAM 中,B+ 树索引的叶子节点并不存储数据,而是存储数据的文件地址。
摘自: http://www.liuzk.com/410.html
在MySQL中,建立一个索引并不一定就有一个B+树。这取决于表的存储引擎和索引类型。例如,在InnoDB中,表中的数据都会有一个主键(如果没有显示创建,则系统会隐式创建),主键对应的B+树就是聚集索引(聚簇索引),它将数据行直接存储在叶子节点上;而其他非主键列创建的索引就是非聚集索引(辅助索引),它将主键作为指针存储在叶子节点上。因此,在InnoDB中,一个表只有一个聚集索引(即主键对应的B+树),但可以有多个非聚集索引(即其他列对应的B+树)。理解Mysql索引的原理和数据结构有助于我们更好的使用索引以及进行SQL优化,索引是在存储引擎层面实现的,所以不同的引擎实现的索引也有一定的区别,但是在生产环境中,我们最常用的就是InnoDB引擎和B树索引,OK,那本文要讨论的重点也同样是 InnoDB引擎下的B树索引 。
我们建立一个表来进行测试,表的DDL如下所示,我们要关注的是表t_book上的主键索引id和name author publish_date三列组成的索引test_index。
Mysql中的B树索引是使用B+树实现的,关于B+树的数据结构个人认为美团点评技术博客中Mysql索引原理及慢查询优化一文中介绍的非常详实,B+树的数据结构如下图所示。
图中浅蓝色块即磁盘块,根节点磁盘块中存储17和35两个数据,其中指针P1指向小于17的数据,指针P2指向大于17小于35的数据,指针P3指向大于35的数据。显然通过B+树索引查询数据与B+树的高度有关,如上图的B+树索引查找一个叶子节点的数据只需要三次磁盘IO,对于Mysql来说三层的B+树可以索引上百万的数据,这对于查询效率的提升是巨大的。
总结起来Mysql中B树索引有以下关键特点:
Mysql中的B树索引有两种数据存储形式,一种为聚簇索引,一种为二级索引。
InnoDB一般会使用表的主键来作为聚簇索引,如果一个表没有主键(不建议这么玩)InnoDB会选用一个唯一非空索引来代替,如果没有这样的索引,InnoDB会隐式建立一个聚簇索引。聚簇的含义即是数据行和相邻的键值紧凑的存储在一起,占据一块连续的磁盘空间,因此通过聚簇索引访问数据可以有效减少随机IO,通常使用聚簇索引查找比非聚簇索引查找速度更快。以我们建立的表t_book为例,聚簇索引即为自增主键id,其B树索引数据结构可以用下图来表示。
聚簇索引有以下关键特点:
InnoDB的B树索引中除了聚簇索引,就都是二级索引了,二级索引的含义是索引的叶子节点除了存储了索引值,还存储了主键id,在使用二级索引进行查询时,查找到二级索引B树上的叶子节点后还需要去聚簇索引上去查询真实数据,但是这里有一种特殊情况,即查询所需的所有字段在二级索引中都可以获取,此时就不需要再去回表查数据了,这种情况就是索引覆盖(EXPLAIN中EXTRA列中会出现USING INDEX,本文只关注索引结构,不详细讨论索引覆盖等技术的使用,如果深入理解索引的数据结构,索引覆盖等技术也没有那么神秘)。
在我们的测试表t_book中,test_index即为二级索引,由于我们把除了主键id所有的列都作为一个联合索引,所以在这个表上的查询都可以使用索引覆盖技术,但是具体生产环境中也不建议总是采用这种做法,索引列的增加也会增大插入更新数据时的索引更新成本,具体的优化要视具体情况决策。t_book上的二级索引test_index的索引结构由下图表示。
通过以上结构,我们可以推断出二级索引的以下关键特点:
索引覆盖:
最左前缀匹配:
二级索引可以说是我们在Mysql中最常用的索引,通过理解二级索引的索引结构可以更容易理解二级索引的特性和使用。
最后聊点轻松的索引结构,哈希索引就是通过哈希表实现的索引,即通过被索引的列计算出哈希值,并指向被索引的记录。
哈希索引有如下特性:
Mysql索引原理及慢查询优化
高性能Mysql 第三版
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)