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 全文索引使用指南
May 25 MySQL
Mysql 用户权限管理实现
May 25 MySQL
MySQL Router实现MySQL的读写分离的方法
May 27 MySQL
浅谈mysql返回Boolean类型的几种情况
Jun 04 MySQL
MySQL REVOKE实现删除用户权限
Jun 18 MySQL
MySQL Shell import_table数据导入的实现
Aug 07 MySQL
MySQL数据库10秒内插入百万条数据的实现
Nov 01 MySQL
mysql创建存储过程及函数详解
Dec 04 MySQL
MySQL 表锁定 LOCK和UNLOCK TABLES的 SQL语法
Apr 18 MySQL
Mysql索引失效 数据库表中有索引还是查询很慢
May 15 MySQL
MySQL优化之慢日志查询
Jun 10 MySQL
MySQL数据库实验之 触发器和存储过程
Jun 21 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微信模板消息操作示例
2017/06/29 PHP
动态加载js文件 document.createElement
2006/10/14 Javascript
关于viewport,Ext.panel和Ext.form.panel的关系
2009/05/07 Javascript
namespace.js Javascript的命名空间库
2011/10/11 Javascript
AJAX跨域请求json数据的实现方法
2013/11/11 Javascript
在Iframe中获取父窗口中表单的值(示例代码)
2013/11/22 Javascript
在百度知道团队中快速审批新成员的js脚本
2014/02/02 Javascript
js实现的GridView即表头固定表体有滚动条且可滚动
2014/02/19 Javascript
jQuery中extend函数详解
2015/07/13 Javascript
jquery实现LED广告牌旋转系统图片切换效果代码分享
2015/08/26 Javascript
JQuery datepicker 用法详解
2015/12/25 Javascript
基于javascript实现图片左右切换效果
2016/01/25 Javascript
深入浅析JS Function()构造函数
2016/08/22 Javascript
微信小程序 图片边框解决方法
2017/01/16 Javascript
jQuery DOM节点的遍历方法小结
2017/08/15 jQuery
小程序接口的promise化的实现方法
2019/12/11 Javascript
JavaScript使用prototype属性实现继承操作示例
2020/05/22 Javascript
[38:41]2014 DOTA2国际邀请赛中国区预选赛 LGD VS CNB
2014/05/22 DOTA
python合并已经存在的sheet数据到新sheet的方法
2018/12/11 Python
python 模拟银行转账功能过程详解
2019/08/06 Python
python+opencv实现车牌定位功能(实例代码)
2019/12/24 Python
Python简单实现区域生长方式
2020/01/16 Python
微软澳洲官方网站:Microsoft Australia
2017/01/10 全球购物
方正Java笔试题
2014/07/03 面试题
植树节活动总结
2014/04/30 职场文书
应届大学生求职信
2014/07/20 职场文书
农村党员学习党的群众路线教育实践活动心得体会
2014/11/04 职场文书
2014年小学安全工作总结
2014/12/04 职场文书
奖金申请报告模板
2015/05/15 职场文书
鲁滨逊漂流记读书笔记
2015/06/26 职场文书
婚宴新郎致辞
2015/07/28 职场文书
学习十八大的感悟
2015/08/11 职场文书
劳动保障事务所个人工作总结
2015/08/12 职场文书
合同范本之电脑出租
2019/08/13 职场文书
Python djanjo之csrf防跨站攻击实验过程
2021/05/14 Python
Java十分钟精通进阶适配器模式
2022/04/06 Java/Android