原文地址:https://amos-x.com/index.php/amos/archives/mysql-btree-index/
前言
索引是数据库提高查询效率的常用办法,那么索引为什么能提高查询效率,索引是怎么工作的,这就需要我们了解其本质上的工作原理,这也是很多面试时的常见问题。
下面我们就详细聊一下:索引
。 在开始前,先说明一下,以下所有内容,基于默认的Innodb
存储引擎,也是目前主流常用引擎。
正文
在开始聊索引
前,首先需要明白:InnoDB是一个将表中的数据存储到磁盘上的存储引擎,且其将数据划分为若干个页,以页作为磁盘和内存之间交互的基本单位,InnoDB中页的大小一般为 16 KB。也就是在一般情况下,一次最少从磁盘中读取16KB的内容到内存中,一次最少把内存中的16KB内容刷新到磁盘中。且各个数据页可以组成一个双向链表,而每个数据页中的记录会按照主键值从小到大的顺序组成一个单向链表,每个数据页都会为存储在它里边儿的记录生成一个页目录,在通过主键查找某条记录的时候可以在页目录中使用二分法快速定位到对应的槽,然后再遍历该槽对应分组中的记录即可快速找到指定的记录
。如下图:
其中页a、页b、页c … 页n 这些页可以不在物理结构上相连,只要通过双向链表相关联即可。
Tips: 以上为基础知识,你可以不用完全懂,知道它是这样的就行,下面出现不太懂的相关知识页可以先看着,后面看着看着就理解了。如果有兴趣的,可以查看 掘金小册 的第5,6章节,详细了解其数据结构。好了,然后我们下面开始
没有索引的查找
本集的主题是索引
,在正式介绍索引
之前,我们需要了解一下没有索引的时候是怎么查找记录的。为了方便大家理解,我们下边先只唠叨搜索条件为对某个列精确匹配的情况,所谓精确匹配,就是搜索条件中用等于=
连接起的表达式,比如这样:
SELECT [列名列表] FROM 表名 WHERE 列名 = xxx;
在一个页中的查找
假设目前表中的记录比较少,所有的记录都可以被存放到一个页中,在查找记录的时候可以根据搜索条件的不同分为两种情况:
- 以主键为搜索条件这个查找过程我们已经很熟悉了,可以在
页目录
中使用二分法快速定位到对应的槽,然后再遍历该槽对应分组中的记录即可快速找到指定的记录。 - 以其他列作为搜索条件对非主键列的查找的过程可就不这么幸运了,因为在数据页中并没有对非主键列建立所谓的
页目录
,所以我们无法通过二分法快速定位相应的槽
。这种情况下只能从最小记录
开始依次遍历单链表中的每条记录,然后对比每条记录是不是符合搜索条件。很显然,这种查找的效率是非常低的。
在很多页中查找
大部分情况下我们表中存放的记录都是非常多的,需要好多的数据页来存储这些记录。在很多页中查找记录的话可以分为两个步骤:
- 定位到记录所在的页。
- 从所在的页内中查找相应的记录。
在没有索引
的情况下,不论是根据主键列或者其他列的值进行查找,由于我们并不能快速的定位到记录所在的页,所以只能从第一个页沿着双向链表一直往下找,在每一个页中根据我们刚刚唠叨过的查找方式去查找指定的记录。因为要遍历所有的数据页,所以这种方式显然是超级耗时的,如果一个表有一亿条记录,使用这种方式去查找记录那要等到猴年马月才能等到查找结果。所以祖国和人民都在期盼一种能高效完成搜索的方法,索引
同志就要亮相登台了。
索引
为了故事的顺利发展,我们先建一个表:
mysql> CREATE TABLE index_demo(
-> c1 INT,
-> c2 INT,
-> c3 CHAR(1),
-> PRIMARY KEY(c1)
-> ) ROW_FORMAT = Compact;
Query OK, 0 rows affected (0.03 sec)
这个新建的index_demo
表中有2个INT
类型的列,1个CHAR(1)
类型的列,而且我们规定了c1
列为主键,这个表使用Compact
行格式来实际存储记录的。为了我们理解上的方便,我们简化了一下index_demo
表的行格式示意图:
我们只在示意图里展示记录的这几个部分:
record_type
:记录头信息的一项属性,表示记录的类型,0
表示普通记录、2
表示最小记录、3
表示最大记录、1
我们还没用过,等会再说~next_record
:记录头信息的一项属性,表示下一条地址相对于本条记录的地址偏移量,为了方便大家理解,我们都会用箭头来表明下一条记录是谁。各个列的值
:这里只记录在index_demo
表中的三个列,分别是c1
、c2
和c3
。其他信息
:除了上述3种信息以外的所有信息,包括其他隐藏列的值以及记录的额外信息。
为了节省篇幅,我们之后的示意图中会把记录的其他信息
这个部分省略掉,因为它占地方并且不会有什么观赏效果。另外,为了方便理解,我们觉得把记录竖着放看起来感觉更好,所以将记录格式示意图的其他信息
去掉并把它竖起来的效果就是这样:
把一些记录放到页里边的示意图就是:
一个简单的索引方案
回到正题,我们在根据某个搜索条件查找一些记录时为什么要遍历所有的数据页呢?因为各个页中的记录并没有规律,我们并不知道我们的搜索条件匹配哪些页中的记录,所以 不得不 依次遍历所有的数据页。所以如果我们想快速的定位到需要查找的记录在哪些数据页中该咋办?还记得我们