Mysql数据库索引面试题(程序员基础技能)


Posted in MySQL onMay 31, 2021

引言

索引是Mysql的一块硬骨头,但是对于程序猿来说又是十分重要的基础技能。在平常的项目开发中,它是重要的SQL优化手段。在求职面试中,它是面试官常常用来考察求职者数据库性能优化方面的重要考量。因此透彻的掌握索引原理,并能够将其运用到数据库查询实战是每个程序猿必备的能力。本文将从索引原理、索引设计原则方面阐述Mysql索引。相信阅读完本文之后,在Mysql索引查询数据理解这块完全可以征服阿里面试官。准备好了吗?我们发车了。

索引原理

在进行索引设计以及优化之前,我们先深入理解下索引的原理。因为所有的设计以及优化一定是建立在你对原理的透彻理解的基础上。

很多人都知道,在进行SQL查询时,同样一张表、同样的数据。不加索引以及加索引进行数据查询。两者差别很多。那么到底是为什么有这种差距。简单来说,如果把业务数据比作为一本字典的话,那么索引就是这本字典的目录。如果我让你查一个字,在你不使用目录查的时候,那只能一页一页的翻,运气不好的话可能要翻到最后一页才能查到想要的字,这就是传说中的全表扫描。但是如果我们通过目录来查找,那么可以很快定位字所在页,进而查找到对应的字。看到了吧,索引的威力就在于提高数据查询的效率。好了,现在我们对于索引有了感性的认识。那么我们接下来就深入了解下。

我们都知道在Mysql中索引的数据结构是B+树(这里不再说明B树、Hash索引等结构的优劣,不是本文的重点),那么我们就一步一步来看看,索引在磁盘中的B+树是怎么长成的。

1、数据页

在日常的项目开发中,我们的业务数据大部分都存在关系型数据中。那么数据库中各个表中的数据最终也都是存储在服务器的硬盘当中的。不知道大家有没有想过这个数据到底是怎么存储的呢?实际上Mysql数据库中我们每天都在使用的数据库表是对于人来理解的逻辑表。它实际在磁盘当中是通过一页页的数据页进行存储的。数据页是磁盘与内存交互的基本单位,MysqlInnodb存储引擎,实际通过buffer pool与磁盘中的数据页进行交互,而不是直接操作磁盘中的数据页。数据页的结构如下图所示:

Mysql数据库索引面试题(程序员基础技能)

同时相邻的数据页之间通过双向链表来维护数据页之间的相互引用。如下图所示,橙红色部分即为数据页,中间的小框框可以理解为一条条具体的数据。MysqlInnoDB存储引擎数据页大小是16KBMysqlInnodb存储引擎通过页号来唯一定位一个数据页,因此每个数据页都有自己的页号。通过上图可知,每个数据页都有都有对应的Page Header,在Page Header中保存了当前数据页的页号,以及其下一页的页号和上一页的页号。

Mysql数据库索引面试题(程序员基础技能)

相邻的数据之间通过指针进行互相引用,指针标注数据页的页号,每个数据页中存储了连续的一段数据,每个数据行中的记录头部存有下一行记录真实数据的地址偏移量,简单理解为拥有指针指向下一行数据的地址。因此在数据页的内部,实际是关于数据行的单向链表。这个单向链表是关于主键id的,从小到大进行排列。

Mysql数据库索引面试题(程序员基础技能)

从上述的数据页结构可知,每次进行数据插入时User Records区域就会变大,相应的的User Record区域就会减少。当User Record区域消耗完之后,就会发生页分裂,形成新的数据页。这里需要注意的是,如果我们使用的是Mysql中的自增主键,那么可以保证按照id的增长顺序进行数据行排列,但是如果主键是我们自己设置的并不是自增长的,那么有可能出现后面插入的数据的主键值小于前面数据的主键值,那么在进行页分裂的时候,Mysql会按照主键大小重新进行排列。此处不知道大家有没有疑问,为什么一定要按照主键大小进行排列呢?实际上和后续的数据查询有关系,数据页中的数据按照主键顺序进行排列是索引可以正常运行的基础。大致的过程如下图所示:

Mysql数据库索引面试题(程序员基础技能)

2、页目录

每个数据页都有自己的页目录上面页结构中的Page Directory,这个页目录的作用实际上就是用来进行数据行定位的。数据页中的数据实际上是按组分配的,页目录中的不同的槽位,其实是对应了数据页中的不同的分组,查询数据时,通过id找到对应的槽,再根据对应的槽来知道对应在数据页中的数据行分组,遍历数据行分组中的数据直到找到对应的数据。

Mysql数据库索引面试题(程序员基础技能)

3、索引原理分析

(1)索引基础

有了上面两节的数据页的基础知识之后,我们再来探讨索引原理就更加容易理解了。在没有索引时,数据查询都是进行全表扫描。遍历查询数据页中的每个数据行,再遍历所有的数据页,知道找到符合条件的数据项。因此查询效率十分的低下。那么应该怎么才能提供数据查询的效率呢?能不能像字典的目录一样,也搞个主键目录来进行数据页号的定位呢?答案是肯定的,Mysql实际也正是这么做的。Mysql通过主键目录实际就是传说中的主键索引,实现数据的查询优化。在主键目录中包含了两个重要元素,一个是数据页中最小的主键,另一个是当前数据页的页号。这样可以通过这个主键目录方面的进行数据查询。

举个栗子,如果此时想要查询主键id=5的数据,那么首先在主键目录中进行查找。此时发现主键id=5大于主键id=1,但是又小于id=8,那么就可以确定实际上数据实际是在页号为1的数据页中的。

当然在实际在Mysql中会有很多的数据页,因此对应的主键索引也会很多,那么此时就需要通过二分查找的方式进行数据页定位,再查找到对应的数据。

Mysql数据库索引面试题(程序员基础技能)

(2)索引页

如今当下,各个互联网公司迅猛发展,对应的业务量也是十分巨大。因此数据库中的数据量也是十分庞大的。表中的数据几百万、上千万可能很常见,按照上述的主键目录,那么就需要存储大量的主键与数据页号。即便是进行二分查找,其数据查询效率也是比较低的。

Mysql实际是将索引说句存储在索引页中的,当数据量比较大时候,对应的索引也会比较多,因此通过专门的索引页来存储索引数据。另外在这些索引页的上层又通过主键与索引页号来继续进行索引页的查询定位,因此我们得到如下的结构。其中的id号指的是对应最小的id号。

Mysql数据库索引面试题(程序员基础技能)

如果索引页中的数据越来越多,索引页同样会进行页分裂。这样索引页也就形成了不同的层级,索引页层、索引页、数据页这三个页数据就形成了我们说的B+树。下图就是索引的B+树结构,通过它完成数据查询效率远高于全表扫描。B+的叶子节点才会存储数据,下图是一种主键索引,也叫聚簇索引。其实我们可以看出来,它的根本思想就是分而治之的思想。数据量很大是吧,那我就把数据分成很多的数据页,数据页很多是吧,那我就通过索引页来组织数据页,索引页很多是吧,那就再通过索引页来索引。

Mysql数据库索引面试题(程序员基础技能)

我们再来看下,数据查询在B+树中的查询过程。举个栗子,如当前需要查询id为3的数据,那么将在索引页中判断应该走索引页为3的索引页。那么在索引页为3中继续判断id=1应该走索引页为1的索引页,在索引页中判断应该页号为1的数据页,在此数据页中遍历最终查询到对应的数据。

Mysql数据库索引面试题(程序员基础技能)

以上通过索引页与数据页组成的B+树就是聚簇索引,当然我们也可以通过其他字段来建立普通索引。知识普通索引会的叶子节点存储的是对应的主键id,而不是具体的数据,索引会存在回表的问题,即查询到对应的id之后,还需要根据id继续到聚簇索引中查询具体的数据,通过这样的操作才能查询到select *的所有数据。当然我们可以通过覆盖索引的方式避免这样的查询浪费。

总结

本文通过一步步图解的方式,为大家拆解MysqlInnoDB的索引原理,同时构建出对应的B+树索引结构。阐述了数据查询的具体过程。相信大家对于索引这块有了更加深刻的理解,后面会从实战的角度出发,分析下如何设计索引以及如何应对索引失效的问题。

MySQL 相关文章推荐
left join、inner join、right join的区别
Apr 05 MySQL
MySQL8.0.24版本Release Note的一些改进点
Apr 22 MySQL
MySQL数据迁移相关总结
Apr 29 MySQL
MySQL 存储过程的优缺点分析
May 20 MySQL
.Net Core导入千万级数据至Mysql的步骤
May 24 MySQL
MySQL的安装与配置详细教程
Jun 26 MySQL
MySQL系列之三 基础篇
Jul 02 MySQL
MySQL系列之四 SQL语法
Jul 02 MySQL
MySQL 5.7常见数据类型
Jul 15 MySQL
SQL基础的查询语句
Nov 11 MySQL
SQL注入详解及防范方法
Dec 06 MySQL
教你使用VS Code的MySQL扩展管理数据库的方法
Jan 22 MySQL
MySQL CHAR和VARCHAR该如何选择
May 31 #MySQL
带你学习MySQL执行计划
May 31 #MySQL
MySQL完整性约束的定义与实例教程
MySQL注入基础练习
解决Navicat for MySQL 连接 MySQL 报2005错误的问题
MYSQL(电话号码,身份证)数据脱敏的实现
May 28 #MySQL
MySql开发之自动同步表结构
You might like
谷歌音乐搜索栏的提示功能php修正代码
2011/05/09 PHP
win2003服务器使用WPS的COM组件的一些问题解决方法
2012/01/11 PHP
php使用ZipArchive提示Fatal error: Class ZipArchive not found in的解决方法
2014/11/04 PHP
PHP实现的最大正向匹配算法示例
2017/12/19 PHP
PHP PDOStatement::setAttribute讲解
2019/02/01 PHP
php对mongodb的扩展(初识如故)
2012/11/11 Javascript
js图片自动切换效果处理代码
2013/05/07 Javascript
JavaScript类属性的访问方式详解
2014/02/11 Javascript
深入理解Javascript里的依赖注入
2014/03/19 Javascript
jquery获取选中的文本和值的方法
2014/07/08 Javascript
JavaScript中跨域调用Flash的方法
2014/08/11 Javascript
JavaScript判断表单提交时哪个radio按钮被选中的方法
2015/03/21 Javascript
javascript中判断json的方法总结
2015/08/27 Javascript
js实现左侧网页tab滑动门效果代码
2015/09/06 Javascript
jQuery AjaxUpload 上传图片代码
2016/02/02 Javascript
jQuery解析json格式数据示例
2018/09/01 jQuery
JS实现计算小于非负数n的素数的数量算法示例
2019/02/26 Javascript
angularjs1.X 重构controller 的方法小结
2019/08/15 Javascript
[06:24]DOTA2亚洲邀请赛小组赛第三日 TOP10精彩集锦
2015/02/01 DOTA
[01:14]辉夜杯战队访谈宣传片—NEWBEE.Y
2015/12/26 DOTA
Python连接mysql数据库的正确姿势
2016/02/03 Python
python 简单的多线程链接实现代码
2016/08/28 Python
python利用sklearn包编写决策树源代码
2017/12/21 Python
Django用内置方法实现简单搜索功能的方法
2020/12/18 Python
印尼最大的在线购物网站:MatahariMall.com
2016/08/26 全球购物
俄罗斯运动、健康和美容产品在线商店:Lactomin.ru
2020/07/23 全球购物
Linux中如何用命令创建目录
2015/01/12 面试题
结构工程研究生求职信
2013/10/13 职场文书
生物医学工程专业学生求职信范文分享
2013/12/14 职场文书
八年级英语教学反思
2014/01/09 职场文书
关于抽烟的检讨书
2014/02/25 职场文书
移风易俗倡议书
2014/04/15 职场文书
2014年检察院个人工作总结
2014/12/09 职场文书
python 实现德洛内三角剖分的操作
2021/04/22 Python
react合成事件与原生事件的相关理解
2021/05/13 Javascript
HTML5 新增内容和 API详解
2021/11/17 HTML / CSS