资讯

展开

一步步带你设计MySQL索引数据结构

作者:快盘下载 人气:

mysql的索引是一个非常重要的知识点;也基本上是面试必考的一个技术点;所以非常重要。那你了解MySQL索引的数据结构是怎么样的吗?为什么要采用这样的数据结构?

现在化身为MySQL的架构师;一步步迭代设计出MySQL的索引结构;保证你再也忘记不了索引的结构了;轻松通过面试。

索引介绍

MySQL表中存储的数据量非常大;可能有上亿条记录;如果一条条去匹配;就是所谓的全表扫描;会非常的慢。那么有什么办法呢?

想想我们生活中的例子;比如新华字典;我们有一个目录;目录根据拼音排序;内容包含了汉字位于字典中具体的的页码。聪明的你肯定也想到了;我们也可以借鉴这种思想;建立一个MySQL的目录;叫做“索引”。

所以你对“索引”做了抽象和定义;索引;Index;是帮助MySQL高效获取数据的数据结构。

索引是在存储引擎中实现的;因此每种存储引擎的索引不一定完全相同;MySQL有InnoDB、MyISAM、Memory等存储引擎;你想了下;就拿最常用的InnoDB作为存储引擎设计索引。

索引设计目标

你现在拼命转动大脑;开始去思考如何设计出这样的一个索引结构。你就在脑子里想;索引设计中需要解决哪些问题;以及要达成什么样的目标。

  1. 我要怎么样才能在索引目录;数据结构;中快速找到具体的某条数据记录呢?那么这个数据结构需要有顺序规律;我按照这个规律就可以定位到具体的某条数据。

  2. MySQL中的数据中的记录如何能够快速找到呢?是不是可以将记录进行排序;然后根据 二分法 快速找到对应的数据记录。

  3. MySQL中架构老大一开始定义数据是按照数据页存放的;每个数据页默认是16kb, 每次满了;就会重新有新的一页。我的索引目录数据应该也是放到页中;而且索引的数据尽量少些;这样每页可以放更多的目录信息。

  4. 我怎么样才能查询效率最高呢?其实每次慢都是慢在磁盘IO上;我再后面设计中一定要减少磁盘IO的访问;越少访问磁盘IO越好。

  5. 磁盘中的空间还是不连续的啊;那我还得有个指针去连接下一条记录的位置。

带着这些问题和思考;你开始设计啦。

索引设计迭代

你想着我就拿一个例子具象化的思考设计索引。

下面是一个新建的表;

CREATE TABLE demo(
	c1 INT,
	c2 INT,
	c3 CHAR(1),
	PRIMARY KEY(c1)
) ROW_FORMAT = Compact;

行记录的格式简化如下;

一步步带你设计MySQL索引数据结构

​我们只在示意图里展示记录的这几个部分;

把一些记录放到页里的示意图就是;

​注意;一页可以存放16kb的数据;并不是图上的3条数据;这里只是一个示例。

迭代一

我们为什么要遍历所有的数据页或者记录?因为各个页中的记录并没有规律;不知道这条数据出现在哪个数据页中。那么如何快速定位要查找的数据在哪个数据页中呢?我们需要建立一定的规律;如下;

1.下一个数据页中用户记录的主键值必须大于上一个页中用户记录的主键值。

索引数据结构

2.给所有的页建立一个目录项

MySQL

查找主键值为 20 的记录;具体查找过程分两步;

  1. 先从目录项中根据二分法快速确定出主键值为 20 的记录在 目录项3 中;因为 12 < 20 < 209 ;;它对应的页是页9。

  2. 再根据前边说的在页中查找记录的方式去页9中定位具体的记录。

迭代二

迭代一中的目录项是怎么存储的呢?我们是不是也可以用行记录格式存储到数据页中呢。答案是肯定的;我们通过行记录格式中的record_type等于1表示是目录记录;如下图所示;

索引数据结构

现在以查找主键为 20 的记录为例;根据某个主键值去查找记录的步骤就可以大致拆分成下边两步;

  1. 先到存储目录项记录的页;也就是页30中通过二分法快速定位到对应目录项;因为 12 < 20 < 209 ;所以定位到对应的记录所在的页就是页9。

  2. 再到存储用户记录的页9中根据二分法快速定位到主键值为20的用户记录。

迭代三

随着数据量变多;势必一个目录项存放不下;因为一页只有16kb大小;就会分裂出多页;如下图所示;

索引数据结构

​那么现在查找主键值为 20 的记录;流程如下;

  1. 我们现在的存储目录项记录的页有两个;即页30和页32 ;又因为页30表示的目录项的主键值的 范围是 [1, 320) ;页32表示的目录项的主键值不小于 320 ;所以主键值为 20 的记录对应的目录项记录在页30中。

  2. 通过目录项记录页确定用户记录真实所在的页。

  3. 在真实存储用户记录的页中定位到具体的记录。

迭代四

如果我们表中的数据非常多则会产生很多存储目录项记录的页;如果直接这么查;也是很慢;我们是不是可以针对目录项记录的页再生成一个更高级的目录;就像是一个多级目录一样;如下图所示;

MySQL

​那么现在查找主键值为 20 的记录;流程如下;

  1. 生成了一个存储更高级目录项的页33;这个页中的两条记录分别代表页30和页32, 主键20的记录在 [1, 320) 之间;则到页30中查找更详细的目录项记录。

  2. 在页30中通过二分法查找主键为20记录的用户记录页码。

  3. 在真实存储用户记录的页中定位到具体的记录。

迭代小结

以上这个数据结构就是我们索引最终的数据结构;B;树; 图形描述如下;

MySQL

所有的叶子节点存放全量的用户记录信息;包含所有的字段。

索引结构总结

聚簇索引

我们按照前面的迭代推演出了基于主键的索引结构;是一颗B;树;我们把这种索引叫做聚簇索引。

MySQL

​特点;

非聚簇索引

既然有了聚簇索引;那么肯定有非聚簇索引;非聚簇索引也叫二级索引或者辅助索引。

它是在什么场景出现的呢?比如我们想以别的列作为搜索条件;总不能是从头到尾沿着链表依次遍历记录一遍;肯定要慢死了。这时候就需要建立非聚簇索引;那它的索引结构和聚簇索引有什么区别呢?

MySQL

那可能你有疑问了;只有主键值;我要查记录的其他信息怎么办呢?

我们根据这个以c2列大小排序的B;树只能确定我们要查找记录的主键值;所以如果我们想根 据c2列的值查找到完整的用户记录的话;仍然需要到 聚簇索引 中再查一遍;这个过程称为 回表 。也就 是根据c2列的值查询一条完整的用户记录需要使用到 2 棵B;树;

回表的过程会耗时;为什么不直接存放所有的数据记录呢?

如果把完整的用户记录放到叶子结点是可以不用回表。但是太占地方了;相当于每建立一课B;树都需要把所有的用户记录再都拷贝一遍;这就有点太浪费存储空间了。

联合索引

联合索引是一种特殊的非聚簇索引;那么它的数据结构又是怎么样的呢?

比方说我们想让B;树按照c2和c3列的大小进行排序;为c2和c3建立的索引的示意图如下;

索引数据结构

索引优点和缺点

我们在了解了索引的数据结构以后;就更加明白索引的优缺点了。

优点

  1. 提高数据查询的效率;降低数据库的IO成本。

  2. 通过创建唯一索引;可以保证数据库表中每一行数据的唯一性。

  3. 在使用分组和排序子句进行数据查询时;可以显著减少查询中分组和排序的时间;降低了CPU的消耗。

缺点

  1. 创建索引和维护索引要耗费时间;并且随着数据量的增加;所耗费的时间也会增加。

  2. 索引需要占磁盘空间;除了数据表占数据空间之外;每一个索引还要占一定的物理空间

  3. 降低更新表的速度。当对表中的数据进行增加、删除和修改的时候;索引也要动态地维护;这样就降低了数据的维护速度。

  4. 索引中的数据都是有序的;比如插入一条主键较小的数据;势必导致其他数据进行移动;页码发生调整;这种现象也叫做页分裂;这也是为什么推荐主键要求自增。

总结

本为让你亲身作为一个MySQL架构师的身份;一步步带你理解MySQL中索引的数据结构;现在是不是理解的很透彻了;如果对你有帮助的话;请留下一个赞吧。

加载全部内容

相关教程
猜你喜欢
用户评论
快盘暂不提供评论功能!