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 相关文章推荐
MySQL的join buffer原理
Apr 29 MySQL
MySql学习笔记之事务隔离级别详解
May 12 MySQL
详解MySQL中的主键与事务
May 27 MySQL
MySQL注入基础练习
May 30 MySQL
MySQL开启事务的方式
Jun 26 MySQL
MySQL索引是啥?不懂就问
Jul 21 MySQL
浅谈MySQL之select优化方案
Aug 07 MySQL
MySQL中几种插入和批量语句实例详解
Sep 14 MySQL
mysql分表之后如何平滑上线详解
Nov 01 MySQL
MySQL分区表管理命令汇总
Mar 21 MySQL
MySQL脏读,幻读和不可重复读
May 11 MySQL
前端传参数进行Mybatis调用mysql存储过程执行返回值详解
Aug 14 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中突破基于HTTP_REFERER的防盗链措施(stream_context_create)
2011/03/29 PHP
php使用base64加密解密图片示例分享
2014/01/20 PHP
PHP采用自定义函数实现遍历目录下所有文件的方法
2014/08/19 PHP
利用php做服务器和web前端的界面进行交互
2016/10/31 PHP
关于ThinkPhp 框架表单验证及ajax验证问题
2017/07/19 PHP
用javascript获得地址栏参数的两种方法
2006/11/08 Javascript
一实用的实现table排序的Javascript类库
2007/09/12 Javascript
jQuery第三课 修改元素属性及内容的代码
2010/03/14 Javascript
JavaScript闭包 懂不懂由你反正我是懂了
2011/10/21 Javascript
js图片延迟加载的实现方法及思路
2013/07/22 Javascript
Google Maps API地图应用示例分享
2014/10/23 Javascript
javascript实现随机读取数组的方法
2015/08/03 Javascript
JS打字效果的动态菜单代码分享
2015/08/21 Javascript
Jquery 效果使用详解
2015/11/23 Javascript
Vue resource中的GET与POST请求的实例代码
2017/07/21 Javascript
基于JavaScript实现报警器提示音效果
2017/10/27 Javascript
通过fastclick源码分析彻底解决tap“点透”
2017/12/24 Javascript
opencv 识别微信登录验证滑动块位置
2018/08/07 Javascript
vue实现打印功能的两种方法
2018/09/07 Javascript
对angularJs中自定义指令replace的属性详解
2018/10/09 Javascript
vue进入页面时滚动条始终在底部代码实例
2019/03/26 Javascript
JS访问对象两种方式区别解析
2020/08/29 Javascript
Python实现字符串匹配算法代码示例
2017/12/05 Python
Python Requests模拟登录实现图书馆座位自动预约
2018/04/27 Python
使用Python来开发微信功能
2018/06/13 Python
Python两个字典键同值相加的几种方法
2019/03/05 Python
Python实现代码统计工具
2019/09/19 Python
在keras中model.fit_generator()和model.fit()的区别说明
2020/06/17 Python
css3 实现元素弧线运动的示例代码
2020/04/24 HTML / CSS
html5 offlline 缓存使用示例
2013/06/24 HTML / CSS
详解HTML5 录音的踩坑之旅
2017/12/26 HTML / CSS
iframe在移动端的缩放的示例代码
2018/10/12 HTML / CSS
十八届三中全会宣传方案
2014/02/21 职场文书
单位考核聘任报告
2015/03/02 职场文书
单位政审意见范文
2015/06/04 职场文书
Vue3实现简易音乐播放器组件
2022/08/14 Vue.js