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

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: MySQL的索引是一个非常重要的知识点,也基本上是面试必考的一个技术点,所以非常重要。那你了解MySQL索引的数据结构是怎么样的吗?为什么要采用这样的数据结构?现在化身为MySQL的架构师,一步步迭代设计出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;

行记录的格式简化如下:

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

  • record_type:记录头信息的一项属性,表示记录的类型, 0 表示普通记录、 2 表示最小记录、 3 表示最大记录、 1 暂时还没用过,下面讲。
  • next_record:记录头信息的一项属性,表示下一条地址相对于本条记录的地址偏移量,我们用箭头来表明下一条记录是谁。
  • 各个列的值:这里只记录在 index_demo 表中的三个列,分别是 c1 、 c2 和 c3 。
  • 其他信息:除了上述3种信息以外的所有信息,包括其他隐藏列的值以及记录的额外信息。

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

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

迭代一

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

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

  • 页中的数据根据主键按顺序排序
  • 不同页中的数据,下一页数据大于上一页数据
  • 新分配的数据页编号可能并不是连续的。它们只是通过维护者上一个页和下一个页的编号而建立了 链表 关系

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

  • key表示目录中最小的主键值。
  • page_on表示对应的页码。

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

  1. 先从目录项中根据二分法快速确定出主键值为 20 的记录在 目录项3 中(因为 12 < 20 < 209 ),它对应的页是页9。
  2. 再根据前边说的在页中查找记录的方式去页9中定位具体的记录。

迭代二

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

  • 目录项记录的 record_type 值是1,而 普通用户记录的 record_type 值是0。
  • 目录项记录只有主键值和页的编号两个列,而普通的用户记录的列是用户自己定义的,可能包含很多列 ,另外还有InnoDB自己添加的隐藏列。

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

  1. 先到存储目录项记录的页,也就是页30中通过二分法快速定位到对应目录项,因为 12 < 20 < 209 ,所以定位到对应的记录所在的页就是页9。
  2. 再到存储用户记录的页9中根据二分法快速定位到主键值为20的用户记录。

迭代三

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

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

  1. 我们现在的存储目录项记录的页有两个,即页30和页32 ,又因为页30表示的目录项的主键值的 范围是 [1, 320) ,页32表示的目录项的主键值不小于 320 ,所以主键值为 20 的记录对应的目录项记录在页30中。
  2. 通过目录项记录页确定用户记录真实所在的页。
  3. 在真实存储用户记录的页中定位到具体的记录。

迭代四

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

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

  1. 生成了一个存储更高级目录项的页33,这个页中的两条记录分别代表页30和页32, 主键20的记录在 [1, 320) 之间,则到页30中查找更详细的目录项记录。
  2. 在页30中通过二分法查找主键为20记录的用户记录页码。
  3. 在真实存储用户记录的页中定位到具体的记录。

迭代小结

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

  • 所有的叶子节点存放全量的用户记录信息,包含所有的字段。
  • 所有的目录节点只存放索引字段、主键以及对应的页码信息,要求信息越少越好,因为一页最多16kb,只有目录信息越少,每页存放的信息越多,树的层级就越小,树的层级越小,那么和磁盘的IO就越少,查询就会越快。一般来说,B+树4层,就可以存放上亿数据了。

索引结构总结

聚簇索引

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

特点:

  • 聚簇索引中的叶子节点存放了用户记录的全部数据,它就是innoDB中数据存放的格式,即数据即聚簇索引,聚簇索引即数据,这也是聚簇索引名字的由来吧,数据和索引聚集在一起。
  • InnoDB要求表必须有主键。如果没有显式指定,则MySQL系统会自动选择一个可以非空且唯一标识数据记录的列作为主键。如果不存在这种列,则MySQL自动为InnoDB表生成一个隐 含字段作为主键,这个字段长度为6个字节,类型为长整型,这样始终就会有一个聚簇索引。

非聚簇索引

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

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

  • 索引目录的内容由3部分组成,索引列的值+主键值+页码,通过索引列的值+主键值唯一确定新插入的列是在哪个页中,也可以唯一确定从那个页中查询。
  • 索引的叶子节点存放内容为索引列的值+主键值。

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

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

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

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

联合索引

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

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

  • 每条目录项都有c2、c3、主键、页号这4个部分组成,各条记录先按照c2列的值进行排序,如果记录的c2列相同,则按照c3列的值进行排序
  • B+树叶子节点处的用户记录由c2、c3和主键c1列组成。

索引优点和缺点

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

优点

  1. 提高数据查询的效率,降低数据库的IO成本。
  2. 通过创建唯一索引,可以保证数据库表中每一行数据的唯一性。
  3. 在使用分组和排序子句进行数据查询时,可以显著减少查询中分组和排序的时间,降低了CPU的消耗。

缺点

  1. 创建索引和维护索引要耗费时间,并且随着数据量的增加,所耗费的时间也会增加。
  2. 索引需要占磁盘空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间
  3. 降低更新表的速度。当对表中的数据进行增加、删除和修改的时候,索引也要动态地维护,这样就降低了数据的维护速度。
  4. 索引中的数据都是有序的,比如插入一条主键较小的数据,势必导致其他数据进行移动,页码发生调整,这种现象也叫做页分裂,这也是为什么推荐主键要求自增。

总结

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

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
9天前
|
缓存 关系型数据库 MySQL
MySQL索引策略与查询性能调优实战
在实际应用中,需要根据具体的业务需求和查询模式,综合运用索引策略和查询性能调优方法,不断地测试和优化,以提高MySQL数据库的查询性能。
|
1月前
|
存储 关系型数据库 MySQL
阿里面试:为什么要索引?什么是MySQL索引?底层结构是什么?
尼恩是一位资深架构师,他在自己的读者交流群中分享了关于MySQL索引的重要知识点。索引是帮助MySQL高效获取数据的数据结构,主要作用包括显著提升查询速度、降低磁盘I/O次数、优化排序与分组操作以及提升复杂查询的性能。MySQL支持多种索引类型,如主键索引、唯一索引、普通索引、全文索引和空间数据索引。索引的底层数据结构主要是B+树,它能够有效支持范围查询和顺序遍历,同时保持高效的插入、删除和查找性能。尼恩还强调了索引的优缺点,并提供了多个面试题及其解答,帮助读者在面试中脱颖而出。相关资料可在公众号【技术自由圈】获取。
|
1月前
|
存储 NoSQL 关系型数据库
为什么MySQL不使用红黑树做索引
本文详细探讨了MySQL索引机制,解释了为何添加索引能提升查询效率。索引如同数据库的“目录”,在数据量庞大时提高查询速度。文中介绍了常见索引数据结构:哈希表、有序数组和搜索树(包括二叉树、平衡二叉树、红黑树、B-树和B+树)。重点分析了B+树在MyISAM和InnoDB引擎中的应用,并讨论了聚簇索引、非聚簇索引、联合索引及最左前缀原则。最后,还介绍了LSM-Tree在高频写入场景下的优势。通过对比多种数据结构,帮助理解不同场景下的索引选择。
83 6
|
1月前
|
SQL 关系型数据库 MySQL
案例剖析:MySQL唯一索引并发插入导致死锁!
案例剖析:MySQL唯一索引并发插入导致死锁!
113 0
案例剖析:MySQL唯一索引并发插入导致死锁!
|
1月前
|
存储 关系型数据库 MySQL
Mysql(4)—数据库索引
数据库索引是用于提高数据检索效率的数据结构,类似于书籍中的索引。它允许用户快速找到数据,而无需扫描整个表。MySQL中的索引可以显著提升查询速度,使数据库操作更加高效。索引的发展经历了从无索引、简单索引到B-树、哈希索引、位图索引、全文索引等多个阶段。
64 3
Mysql(4)—数据库索引
|
23天前
|
监控 关系型数据库 MySQL
数据库优化:MySQL索引策略与查询性能调优实战
【10月更文挑战第27天】本文深入探讨了MySQL的索引策略和查询性能调优技巧。通过介绍B-Tree索引、哈希索引和全文索引等不同类型,以及如何创建和维护索引,结合实战案例分析查询执行计划,帮助读者掌握提升查询性能的方法。定期优化索引和调整查询语句是提高数据库性能的关键。
116 1
|
1月前
|
存储 关系型数据库 MySQL
如何在MySQL中进行索引的创建和管理?
【10月更文挑战第16天】如何在MySQL中进行索引的创建和管理?
67 1
|
24天前
|
监控 关系型数据库 MySQL
数据库优化:MySQL索引策略与查询性能调优实战
【10月更文挑战第26天】数据库作为现代应用系统的核心组件,其性能优化至关重要。本文主要探讨MySQL的索引策略与查询性能调优。通过合理创建索引(如B-Tree、复合索引)和优化查询语句(如使用EXPLAIN、优化分页查询),可以显著提升数据库的响应速度和稳定性。实践中还需定期审查慢查询日志,持续优化性能。
53 0
|
1月前
|
监控 关系型数据库 MySQL
MySQL数据表索引命名规范
MySQL数据表索引命名规范
68 1
|
1月前
|
存储 SQL 关系型数据库
mysql中主键索引和联合索引的原理与区别
本文详细介绍了MySQL中的主键索引和联合索引原理及其区别。主键索引按主键值排序,叶节点仅存储数据区,而索引页则存储索引和指向数据域的指针。联合索引由多个字段组成,遵循最左前缀原则,可提高查询效率。文章还探讨了索引扫描原理、索引失效情况及设计原则,并对比了InnoDB与MyISAM存储引擎中聚簇索引和非聚簇索引的特点。对于优化MySQL性能具有参考价值。
下一篇
无影云桌面