MySQL高级第一篇:索引的来源,优缺点及B+树的演化
- 一、为什么使用索引?
- 二、索引的优缺点?
- 优点
- 缺点
- 三、InnoDB索引演化
- 3.1. 索引之前的普通查找
- 1. 假设表的数据不多,数据可以被放在一个页,根据条件搜索时,有两种情况:
- 2. 大部分情况表中的记录比较多,需要分成多个页存储,这时查找记录分为两步:
- 3.2 索引是怎么设计的?
- 1. 目录项记录的页
- 2. 多个目录项记录的页
- 3. 目录页再抽取页
- 4. B+树
一、为什么使用索引?
- 索引是存储引擎用于快速找到数据记录的一种数据结构,在刚开始接触时,我们可以不严谨的把索引比作一本书的目录,通过目录可以快速定位到对应文章的页码。
- 在MySQL中也一样,如果有了索引,就不用再一条一条的全表扫描了,大大提高查询效率。
- 创建索引,目的就是为了减少磁盘I/O的次数,加快查询速率。
二、索引的优缺点?
优点
- 提高检索效率,降低磁盘I/O
- 唯一索引可保证数据的唯一性(唯一约束,自动添加唯一索引)
- 对于有依赖关系的子表和父表联合查询时,可以提高查询速度
- 在使用分组和排序查询时,可以减少分组和排序时间,降低了CPU的消耗
缺点
- 创建维护索引耗费时间
- 索引也需要存到磁盘上,占用磁盘空间
- 修改表数据也需要维护索引,降低更新表的速度
三、InnoDB索引演化
3.1. 索引之前的普通查找
SELECT [列名] FROM 表名 WHERE 条件
注意:表中的数据以页为单位存储,每个页的默认大小为16KB
1. 假设表的数据不多,数据可以被放在一个页,根据条件搜索时,有两种情况:
- 以主键作为搜索条件
- 一般在添加数据时,主键为自增长,也就是说主键是递增有序的,这时可以使用二分法查找
- 以其它列作为搜索条件
- 这种情况下没有什么规律,只能从最小记录开始依次遍历单链表中的每条记录,效率较差。(表中每条记录是以单链表的形式存储的)
2. 大部分情况表中的记录比较多,需要分成多个页存储,这时查找记录分为两步:
- 第一步:定位记录所在的页
- 第二步:在所在页以
情况一
的方式搜索
在没有索引时,我们不能快速定位到所在的页,只能从第一页沿着双向链表一直往下找,在每一个页中匹配,这是一种效率低下的查找方式,也就是索引存在的意义了。
3.2 索引是怎么设计的?
假设现在有一张表,表中有C1,C2,C3三列,以Compact行格式保存,如下:
record_type:0表示普通记录;1表示目录项页;2表示最小记录;3表示最大记录
所以,一个页的大概结构如下:
当记录一个页放不下时,会存放在多个页中,以双向链表连接,但无论几个页,主键ID都要保持递增,如果插入顺序不是按递增主键插入,会发生记录移动,以保持这种结构,多个页中的这种行为称为页分裂。当多个页查询时,我们难以定位到数据在哪一个页,只能从第一页开始遍历,基于这种情况,考虑可以给所有的页建立一个目录项,让目录项连续依次递增,每个目录项记录页的最小记录和页号
这样,我们就可以利用二分法查询目录项,快速定位到某一页,再在这个页这种查询,这就是索引演化的雏形。
1. 目录项记录的页
我们在上边已经了解了索引的雏形,但是在实际操作中存在两点问题:
1,随着页的越来越多,目录项也越来越多,而目录项存储在连续的空间,页越来越多,空间压力也越来越大;
2,数据页的增删使得目录项也需要增删,很不方便
基于这种情况,考虑将目录项以单链表的方式存储,所有目录项也存储到一个页中,把它叫做目录页
,如下:
2. 多个目录项记录的页
随着数据页的越来越多,一个目录页放不下所有的目录项了,这时,就考虑将目录项放在多个目录页中,目录页之间逻辑连续,采用双向链表链接
3. 目录页再抽取页
当目录页超过一个时,定位数据页先遍历目录页,目录页越多IO次数越多,考虑给目录页再生成一个更高级的页,这个页可以连续,使用二分法更方便确定目录页
一步步演化到这里,这其实就是我们所谓的 B+ 树了
4. B+树
经过上边的演化,我们得出了 B+ 树的结构,简化来说如下:
需要注意,B+ 树的层次越低,IO次数越少,一般B+树不超过4层