【MySQL】InnoDB存储引擎篇(二)索引结构

数据库的索引,相信大家都不陌生了。这篇文档我会从头到尾的把索引讲清楚,而不是死记硬背的八股模式。死记硬背固然可以应付面试,但其实不会对索引的理解很深刻。

我也是在深入学完索引的时候才感慨,还是加深对技术的理解,面试的时候才可以侃侃而谈,答出其他竞争者答不到的点,从而让面试官对你高看一眼(实话)。

下面我会从 B+树结构,聚簇索引,二级索引,联合索引的顺序展开。

1. B+树

我们先不直接讲 B+树 的定义,而是通过讲 InnoDB 的存储方式上理解其结构。

前文我们说 InnoDB 引擎存储数据的基本单位是 ,我们存储的一条一条记录都是存放到数据页里的。

那么 B+树 上的每个节点其实就是页,是一个一个数据页组成的一棵树。但不是所有的页都存放用户记录。

我们简单的建一个表,先不建索引。

CREATE TABLE cs_user (
  id INT PRIMARY KEY,
  age INT,
  name VARCHAR(50),
);

INSERT INTO cs_user (`id`, `age`, `name`) VALUES (1, 22, '张三');
INSERT INTO cs_user (`id`, `age`, `name`) VALUES (2, 25, '李四');

SELECT * FROM cs_user;
SELECT * FROM cs_user WHERE id = 1;

先抛出两个问题:

  • 创建了一张表并插入了两条记录,那么在我们不创建索引的情况下,进行查询操作的时候,数据库是如何快速找到指定的记录的呢?
  • InnoDB 引擎自动帮我们创建了一个聚簇索引(一会详细说),然后根据这个索引快速的定位。
  • 我们知道在每个页中,有页目录可以通过二分法快速定位到要找的那条记录。但是每个页固定为 16KB,数据库很庞大的话,那么必然会有很多个页,这样又该如何快速找到指定的页呢?
  • 这就要从 B+树 的结构说起了,B+树是一个多叉树,在 InnoDB 引擎中,只有最后一层也就是叶子节点才存放用户记录,上面的那些层包括根节点其实都相当于是一个目录页,只不过越靠近根节点,囊括的范围越广。就比如说我想查找一个学生的班级,那么我肯定先查他是哪个学校的,再查他是哪个年级的,最后再查他是哪个班级的,如下图所示:只有叶子节点才存储数据(即班级)

2. 聚簇索引

同理,B+树前几层都是目录页,用于更快速的定位查询记录所在的页,然后一层一层向下查询,直到找到记录所在的数据页,再根据上一篇文档讲的页目录,查询到具体的记录。

下面这个是一个聚簇索引构成的 B+树,叶子节点存的是完整的记录

这里引用下《MySQL是怎样运行的》图示,不知道 Infimum 和 Supremum 是啥的请看上一篇文档。

通过这张图片,可以看到:

  • 不同页之间按照主键的大小顺序排成一个双向链表
  • 不同记录之间按照主键的大小顺序排成一个单向链表
  • 非叶子节点(也就是目录页),第二行的值是这个目录项记录指向的那个页第一条记录的主键值。比如第二层的页30,1 -> 5 -> 12 -> 200,1是页10的第一个主键值,5是页28的主键值...,以此类推。
  • 非叶子节点,第三行的值是这个目录项记录指向的那个页的页号,图中显而易见。
  • 叶子节点(也就是数据页),第一行是主键值(id),第二行是年龄(age),第三行是名字(name),也就是说,聚簇索引的叶子节点包含一条记录完整的字段。

比如我执行这条语句:SELECT * FROM cs_user WHERE id = 8;

  • 首先从根节点开始,由于 1 < 8 < 320,所以去页30查询
  • 再查第二层目录,由于5 < 8 < 12,所以去页28查询
  • 最后查叶子节点,从页28 id = 5 的记录向后查,查到下一个就是 id = 8 的记录

3. 二级索引

上面说的是我们通过主键 id 来查询记录,现在假如我们多了一个需求,需要频繁通过 age 来筛选用户。

如果我们没有对 age 设置索引,那么数据库查询的时候,就会进行全表扫描,从第一个记录开始一个一个遍历,这种方式当数据量很大的时候是很费性能的。所以应对这种需求,我们一般会为 age 添加一个索引(二级索引)这样再通过 age 查询用户的时候,就可以用这个索引来查询用户记录,效率直接翻xxx倍。

还是上面那张表,这次我们为 年龄age 加一个索引

CREATE TABLE cs_user (
  id INT PRIMARY KEY,
  age INT,
  name VARCHAR(50)
  INDEX idx_age age;
);

INSERT INTO cs_user (`id`, `age`, `name`) VALUES (1, 22, '张三');
INSERT INTO cs_user (`id`, `age`, `name`) VALUES (2, 25, '李四');

SELECT * FROM cs_user;
SELECT * FROM cs_user WHERE id = 1;
SELECT * FROM cs_user WHERE age = 6;

二级索引整体结构上,和聚簇索引差不多,区别在于叶子节点上(数据页)。

  1. 原来聚簇索引中记录的所有字段(id, age, name),现在只有两个字段了(age, id)
  2. 第一行变成了 年龄age,而主键退居后方到了第二行。

也就是说,二级索引会针对定义了索引的这个字段,构造一棵新的 B+树,这棵树的叶子节点,其每个记录的第一行都是定义索引的这个字段二分法会根据这个索引字段的大小进行排序和查找。但是这个新的 B+树还是会携带上主键,这个主键的作用是为了回表(一会说)

比如我执行这条语句:SELECT * FROM cs_user WHERE age = 6;

  • 首先从根节点开始,由于 2 < 6 < 9,所以去页42查询
  • 再查第二层目录,由于5 < 6 < 7,所以去页36查询
  • 最后查叶子节点,从页36 age = 5 的记录向后查,查到下一个就是 age = 6 的记录

记录排列顺序

读者请注意看,叶子节点的 页35,当索引字段 age 相同时(都等于4),又该怎么排序呢?

当然不是随便排,第一行相同,那就比第二行主键值的大小,发现 4 < 10,那么 id = 10 的就排在 id = 4 的后面

  • 这样在我们插入 age = 4 时,主键值也是有序的,也是一种规范。不然会造成一种混乱,比如插入相同记录的时候,是该将 age = 4 的记录插入页34呢还是页35呢。
  • 除此之外,这样按一层一层的大小顺序排布,查询的时候也是有讲究的,下一章会具体讲解。

回表

我们现在知道,二级索引中是不存储完整记录的。那当我们想查询这个年龄对应用户的完整记录,又该如何操作呢。答案是从二级索引的B+树中拿到主键值,再通过这个主键值去聚簇索引中查询完整记录。

也就是说,通过二级索引查询完整记录,我们需要走两次B+树的查询流程。

  • 第一次走二级索引,查到 age = 6 对应的主键 id = 220
  • 第二次走聚簇索引,查到 id = 220 对应的完整记录 cs_user(220, 6, 'i')

当然,如果我们只需要查询 age (或者同时查询 主键值id 和 age)的话,那就不需要回表操作,因为二级索引的这棵B+树就包含了这两个字段。

4. 联合索引

假如傻x产品经理现在又多了一个需求,在筛选用户时,要通过用户的 name 和 age 一起查询。

我们现有的索引,聚簇索引(主键索引),age 的二级索引,都无法快速拿到这两个值。

  • 聚簇索引的叶子节点(数据页),是以主键 id 大小排序的,二分法也是用 id 值快速查询,无法用二分法快速通过 age 和 name 查询到指定记录。
  • age 的二级索引里面,又没有 name 字段,查询还得通过回表再查一次聚簇索引。

所以,我们可以根据 age 和 name 建立一个联合索引

CREATE TABLE cs_user (
  id INT PRIMARY KEY,
  age INT,
  name VARCHAR(50)
  INDEX idx_age age;
  INDEX idx_age_name (age, name);
);

INSERT INTO cs_user (`id`, `age`, `name`) VALUES (1, 22, '张三');
INSERT INTO cs_user (`id`, `age`, `name`) VALUES (2, 25, '李四');

SELECT * FROM cs_user;
SELECT * FROM cs_user WHERE id = 1;
SELECT * FROM cs_user WHERE age = 6;
SELECT * FROM cs_user WHERE age = 4 AND name = 'o';

根据这两个字段建立联合索引后,InnoDB 引擎会再建一棵 B+树,叶子节点的顺序为 age - name - id

也就是先按 age 的大小排序,再按照 name 的大小排序,最后按照 id 的大小排序。

比如我执行这条语句:SELECT * FROM cs_user WHERE age = 4 AND name = 'o';

  • 首先从根节点开始,由于 2 < 4 < 9,所以去页59查询
  • 再查第二层目录,由于2 < 4 <= 4,所以去页50查询
  • 最后查叶子节点,从页50 age = 2 的记录向后查,查到后两个就是 age = 4 的记录,发现 name != 'o'
  • 再查页55,从第一个记录开始查,发现第一个符合,再查第二个 name = 'u',不符合,停止遍历。

可以发现,这样按照一层一层顺序排布的时候,查询效率是最高的,如果杂乱无章的排列,那么就得遍历所有 age = 4 的记录,如果 age = 4 的记录很多的话,就会浪费大量时间。

5. 面试官提问

1. 请你讲一下 B+ 树的结构(优点),为什么 InnoDB 引擎要用 B+ 树而不用 B树(红黑树)等等?

八股内容就不写了,大家可以自行查看 小林、JavaGuide 等或者直接询问 AI

2. 请你说一下索引的分类,都有哪些索引,分别介绍下

3. 数据库中的 B+树 一般建议几层,这样大概能存多少数据量?

4. 具体业务中,什么场景需要建索引?

5. 讲一下回表。

后端技术学习 文章被收录于专栏

这个专栏我将对后端技术进行总结和复盘,以在字节输出文档的态度来要求自己。包括但不限于八股、算法、架构、系统设计、场景、智力题等。

全部评论

相关推荐

不愿透露姓名的神秘牛友
09-29 13:32
点赞 评论 收藏
分享
10-08 11:19
已编辑
四川大学 产品运营
在许多求职者眼中,中国航天科技集团和中国航天科工集团始终笼罩着一层神秘面纱。作为我国航天事业的核心力量,这两大集团并称为&amp;quot;航天双雄&amp;quot;,是无数优秀毕业生向往的殿堂。今天,就让我们一起揭开这两大军工央企的神秘面纱,为你的航天求职之路指明方向。🚀渊源与定位:同根同源,各有所长航天科技和航天科工集团均始于1956年10月成立的国防部第五研究院,历经第七机械工业部、航天工业部等多个历史阶段。1999年7月,原中国航天工业总公司分拆成了今天的航天科技和航天科工集团。航天科技集团主要从事民用航天产业,包括我国全部的运载火箭、应用卫星、载人飞船、空间站、深空探测飞行器等宇航产品,军用方面则负责全部战略导弹和部分战术导弹等武器系统。我们熟知的神舟飞船工程、嫦娥探月工程、空间站、北斗导航卫星等都是航天科技集团的杰作。航天科工集团主要从事国防军事工业,我国各类型的防空导弹和飞航导弹大都出自这里。同时,其自主研发的技术产品也护航了&amp;quot;神舟&amp;quot;、&amp;quot;天宫&amp;quot;等国家重大工程。简单来说,航天科技主要&amp;quot;送东西上天&amp;quot;,航天科工主要&amp;quot;保卫国家安全&amp;quot;。🚀招聘需求:专业对口,精准匹配航天科技和科工集团的招聘有着明显的专业倾向性,主要集中在以下几个领域:重点招聘专业:-&nbsp;航空宇航科学与技术(航天科技集团商业火箭公司等岗位需求)-&nbsp;控制科学与工程(航天科技集团商业火箭公司等岗位需求)-&nbsp;电子科学与技术(航天科技集团商业火箭公司、航天科工集团第十总体设计部等岗位需求)-&nbsp;计算机科学与技术(航天科技集团商业火箭公司、航天科工集团第十总体设计部等岗位需求)-&nbsp;信息与通信工程(航天科技集团商业火箭公司、航天科工集团第十总体设计部等岗位需求)-&nbsp;机械工程(航天科技集团商业火箭公司、航天科工集团第十总体设计部等岗位需求)-&nbsp;软件工程(航天科技集团商业火箭公司等岗位需求)-&nbsp;电气工程(航天科技集团商业火箭公司等岗位需求)-&nbsp;仪器科学与技术(航天科工集团第十总体设计部等岗位需求)-&nbsp;材料科学与工程(航天科工集团第十总体设计部等岗位需求)学历要求:两大集团对学历要求较高,硕士研究生是主流需求,部分科研岗位要求博士研究生。🚀求职指南:如何敲开航天事业大门招聘流程:航天系统的招聘一般遵循以下流程:网申→资格审查→考试→面试→综合考察→录用,部分单位在报名通过筛选后即可进入面试。应聘要点:1.&nbsp;政治素质过硬具有较高的政治素质,拥护党的路线、方针、政策,遵守国家法律、法规及集团公司相关制度,热爱航天事业是基本要求。2.&nbsp;专业能力扎实具有履行岗位职责所必需的专业知识,通常要求专业对口,成绩优秀。3.&nbsp;实践经验丰富部分岗位要求具备相关领域工作经验,如航天科保的规划设计岗要求&amp;quot;3年以上需求分析、功能设计、产品设计相关经验&amp;quot;。4.&nbsp;综合素质全面具有良好的职业素养,遵纪守法,廉洁从业,勤勉尽责,团结协作,诚实守信。具有良好的心理素质和能够正常履行职责的身体条件也是重要考察点。🚀薪酬福利:待遇优厚,保障完善航天系统提供的薪酬福利在行业内具有较强竞争力:-&nbsp;解决户口:部分北京单位可为应届毕业生解决北京海淀户口-&nbsp;薪酬待遇:行业内具有竞争力的薪资,具体薪酬根据岗位和单位有所不同,例如航天科工集团第十总体设计部的月薪范围为&amp;quot;15000及以上&amp;quot;-&nbsp;社会保障:七险两金等完善的社会保障-&nbsp;生活保障:青年公寓、住房补贴、餐费补助、员工食堂等-&nbsp;其他福利:节日福利、带薪休假、健康体检、各类教育培训等结语航天科技和航天科工集团作为我国航天事业的中流砥柱,代表着国家在航空航天领域的最高技术水平。加入这两大集团,不仅是一份职业选择,更是一种使命和荣誉。随着我国航天事业的飞速发展,商业航天时代的到来,&amp;quot;航天双雄&amp;quot;正迎来前所未有的发展机遇,也为广大优秀人才提供了更加广阔的职业舞台。希望这篇介绍能帮助有志于航天事业的求职者更好地了解这两大集团,预祝各位在星辰大海的征途中找到自己的位置!更多央国企岗位公告可查看求职精灵👉https://m.finsight.work/pages/activePage/general/index?pageCode=0F52EBAB~还有更多的真题题库随意练哦~
投递国航等公司10个岗位
点赞 评论 收藏
分享
评论
1
6
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务