当然,这里也是画出的伪结构,因为不可能按照MySQL单节点16KB的尺寸1:1还原,毕竟画不下这么大(实际MySQL对于上述这些数据,一个节点就全放下了)。
索引键的大小会随着值长度变化吗?
这个问题很有趣,比如现在基于一个int类型的字段建立了一个索引,但目前的字段值是1,按理来说这个值只占1bits对不对?那在索引键中,或数据库中占多少呢?答案是4Bytes/32bits,这是因为一个int类型在操作系统中就会占用32bit空间,不会根据实际值而减少占用空间。
但大家也都知道,数据库中还有不少文本类型,例如varchar类型,它是固定的长度吗?并不是,它是一个变长类型,而非一个定长类型,也就是一个varchar字段值,占用的空间会随着实际所存储的数据而变化。
所以对于一个索引键的大小是否会发生变化,这要取决于你是基于什么字段类型建立的索引,如果是定长类型就不会变化,但如果是变长类型就会随之发生改变。
三、索引内部查询与维护的过程
建立索引时会发生的内部过程,上一段落已经阐述明白了,接着再来说说查询SQL执行时,如果选中了索引,索引内部的检索过程是什么样的呢?也包括当写类型的SQL更改表中数据后,MySQL又会如何维护索引的内部结构呢?
3.1、聚簇索引查找数据的过程
在《SQL执行篇》中聊到过,当查询SQL来到MySQL后,经过一系列处理后,最终会来到优化器,此时会由优化器来为SQL语句选择出一个合适的索引,当然,你也可以手动强制指定索引。那当SQL命中索引时,索引内部是如何查找对应的行数据的?
工作线程执行查询SQL时,首先会先看一下当前索引的结构,如果是Hash索引就很简单了,直接对索引字段的值进行哈希计算,然后直接根据哈希值,从索引中找到相应的索引信息,最后获取数据即可。
但如果索引结构是默认的B+Tree呢?内部又会发生什么工作?
如果当前SQL使用的是主键/聚簇索引,比如:
SELECT * FROM `zz_user` WHERE `ID` = 12;
复制代码
此时首先会根据条件字段,去内存中找到聚簇索引的根节点,然后根据节点中记录的地址去找次级的叶节点,最后再根据叶节点中的指针地址,找到最下面的叶子节点,从而获取其中的行数据,动画过程如下:

B+Tree结构的索引似乎查找过程也并不复杂对不对?但有一个细节点需要注意,B+Tree的单个节点可存储多个数据,也就是当磁盘IO发生后,MySQL一次读取的数据中有多个索引信息,此时MySQL会如果查找单个节点中的索引信息呢?全部判断一次嘛?
其实并不会全部判断一次,因为B+Tree是一种有序的数据结构,小的会放左边,大的会放右边,单个节点中的索引信息,同样遵循这个原理。既然单个节点中的数据也是有序的,所以MySQL同样会采用二分查找法去检索数据。对于单个节点中的索引信息,先从索引中间开始查询,然后判断一下当前SQL中ID=12这个条件,是大于还是小于最中间的索引键,小于则去节点左边取中间的索引键继续判断,大于则去右边.....,以此类推直至定位到单节点中对应索引键为止。
OK,如果是范围取值,比如取ID>=2的所有数据,则会先定位到ID=2的索引键,然后通过叶子节点之后的指针,直接将2之后的数据全部取出。
聚簇索引中,定位到了索引键,即代表着取到了数据,毕竟索引和行数据是一起存储的。
3.2、非聚簇索引查找数据的过程
相较于聚簇索引而言,非聚簇索引前面的步骤都是相同的,仅是最后一步有些许不同罢了,非聚簇索引经过一系列查询步骤后,最终会取到一个聚簇索引的字段值,然后再做一次回表查询,也就是再去聚簇索引中查一次才能取到数据。