「一文搞懂」MySQL索引原理及优化规则
csdh11 2024-11-30 19:55 4 浏览
本章内容
定义
索引(Index)是帮助MySQL高效获取数据的数据结构。
索引模型
索引常见的数据模型有哈希表、有序数组、搜索树。
哈希表
哈希表是一种以键 - 值(key-value)存储数据的结构,只要输入待查找的键(即:key),就可以找到其对应的值(即:value)。其实现思路是使用一个哈希函数将key换算成数组中的下标,并将value存储到该下标对应的位置。多个key值经过哈希函数换算后可能指向同一个下标,因此,采用一个链表来存储相同下标对应的value。
以人的身份证和姓名为例,如图所示:
图中,key2和key3对应的数组下标均为7,查找时,先根据key2或key3的哈希值定位到数组下标7,再遍历下标7对应的链表找到key2或key3对应的value2或value3。因此,哈希表比较适用于等值查询的场景。
有序数组
以人的身份证和姓名为例,如图所示:
图中,有序数组按照身份证号递增的顺序保存数组:
- 如果要查找card1对应的name,使用二分法可以快速得到card1对应的name1,时间复杂度为O(log(N))。
- 如果要查找card3~card4区间的数据,可以先用二分法找到card3(如果不存在card3,则找到大于card3的第一条数据),然后向右遍历,直到找到第一个大于card4的身份证号,退出循环。
- 如果要向有序数组中插入一条数据,则需要移动该记录后面的数据,成本相对较高。
因此,有序数组索引比较适用于静态存储引擎(即:不再修改的数据)。
搜索树
1)二叉搜索树
二叉搜索树的特点:父节点左子树所有结点的值小于父节点的值,右子树所有结点的值大于父节点的值。
以人的身份证和姓名为例,如图所示:
图中,如果要查找card1对应的数据,则搜索顺序为:userA -> userB -> userE -> user1。时间复杂度为O(log(N))。
2)多叉搜索树
树可以是二叉,也可以是多叉。多叉树的每个节点有多个子节点,子节点之间的大小保证从左到右递增。二叉树的搜索效率最高,但是实际上大多数数据库存储并不使用二叉树,其原因是索引不止存在内存中,还需要写到磁盘中。
假如一棵存在100万个节点的二叉树,树高为20。一次查询可能需要访问20个数据块,从磁盘随机读一个数据块需要10ms的寻址时间。那么对于一个100万行的表,如果使用二叉树来存储,单独访问一个行可能需要20*10ms的时间。因此,为了让一个查询尽量少的访问磁盘,就需要让查询过程尽量少的访问数据块(即:使用N叉树,其中N取决于数据块的大小)。
以InnoDB的一个整数字段索引为例,这个N差不多为1200。当多叉树的树高为4时,可以存储1200^3个值(即:1728000000)。那么访问一个存储10亿行数据的表整数字段索引时,最多只需要访问3次磁盘即可,大大减少访问磁盘的次数,提高搜索效率。
InnoDB索引模型
B+树
在InnoDB中,以B+树作为索引模型。表是根据主键顺序以索引的形式进行存储,这种存储方式的表称为索引组织表。
B+树结构,如图所示:
图中:
- 淡黄色表示磁盘块。
- 浅蓝色表示数据项。
- 深蓝色表示指向磁盘块的指针。
其中,磁盘块1包含:
- 数据项16和38。
- 指针P1、P2、P3:
- P1表示小于16的磁盘块。
- P2表示在16和38之间的磁盘块。
- P3表示大于38的磁盘块。
真实数据存储在叶子节点(即:3、5、8、10、13、15、27、29、36、62、74、85、91、96);非叶子节点不存储真实数据,只存储指引搜索方向的数据项,如:16、38并不真实存在于数据表中。
B+树查找过程
在上图中,要查找数据项27,查找过程如下:
- 1)将磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中使用二分查找确定27在数据项16和38之间,锁定磁盘块1的P2指针,内存操作时间非常短(相比磁盘的IO)可以忽略不计。
- 2)通过磁盘块1中P2指针的磁盘地址将磁盘块3由磁盘加载到内存,发生第二次IO,27在25和32之间,锁定磁盘块3的P2指针。
- 3)通过磁盘块3中P2指针的磁盘地址将磁盘块8由磁盘加载到内存,发生第三次IO,同时内存中做二分查找找到27,结束查询,总计三次IO。
通常情况下,三层的B+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能会有巨大的提升;如果没有索引,每个数据项都要发生一次IO,那么总共需要上百万次IO,查询成本将会非常高昂。
索引类型
根据B+树叶子节点的内容,索引类型分为主键索引和非主键索引:
- 主键索引的叶子节点存储的是整行数据。在InnoDB中,主键索引也被称为聚簇索引(clustered index)。
- 非主键索引的叶子节点存储的是主键的值。在InnoDB中,非主键索引也被称为二级索引(secondary index)。
其中:
- 主键索引查询,只需要按主键(如:id)搜索主键索引这颗B+树即可。
- 非主键索引(普通索引)查询,需要先搜索普通索引这颗B+树得到主键值,再根据主键值搜索主键索引B+树得到对应的数据,这个过程称为回表。
因此,基于非主键索引的查询需要多扫描一棵索引树。因此,在应用中应该尽量使用主键查询。
索引维护
创建表:
create table t_user(
id int primary key,
card int not null,
name varchar(16),
index idx_card(card)
)engine=InnoDB;
其中:
- id:主键索引。
- card:普通索引。
索引维护
向索引中插入数据,根据插入位置存在如下差异:
- 向末尾插入数据,将直接插入数据即可。
- 向中间插入数据,插入数据后需要移动该插入数据后面的数据,同时,如果插入数据所在的数据页已满,需要申请一个新的数据页,并挪动部分数据到新的数据页中,这个过程称为页分裂。因此,向中间插入数据除了影响性能外,页分裂操作还会影响数据页的利用率。即:原本放在一个数据页的数据需要分到两个页中,整体空间利用率降低大约50%。
注意:当相邻两个数据页由于删除数据导致利用率很低后,会对数据页进行合并。这个过程称为页合并,页合并为页分裂的逆过程。
自增主键索引维护
自增主键是指自增列上定义的主键,自增主键定义: NOT NULL PRIMARY KEY AUTO_INCREMENT。插入数据时可以不指定ID(自增列)的值,系统会获取当前ID最大值加1作为下一条记录的ID值。
自增主键的插入数据模式符合递增插入的场景,每次插入一条新数据都是追加操作,不涉及挪动其他记录,也不会触发叶子节点的页分裂。
非自增主键索引维护
非自增主键插入数据时,一般不保证数据有序插入,会因数据移动和页分裂而影响插入性能。同时,非自增主键占用的存储空间相对较大(如:身份证号码),通过以上B+树的查找过程可知,IO次数取决于B+树的高度h。假设当前数据表的数据量为N,每个磁盘块中数据项的数量为m,则树高h=log(m+1)N,当数据量N固定的情况下,m越大,h越小。而m = 磁盘块(数据页)的大小/数据项的大小,磁盘块大小固定,如果数据项占的空间越小,磁盘块中存储的数据项数量m就越多,B+树的高度h就越低。
覆盖索引
以上面的t_user表为例,初始化表数据:
insert into t_user values
(1,43252411115431,'南秋同学1'),
(2,43252411115432,'南秋同学2'),
(3,43252411115433,'南秋同学3'),
(5,43252411115435,'南秋同学5'),
(6,43252411115436,'南秋同学6'),
(8,43252411115438,'南秋同学8'),
(9,43252411115439,'南秋同学9');
执行语句:
select * from t_user where card between 43252411115433 and 43252411115438;
查询过程,如图所示:
处理流程:
- 1)从card索引树上查找card=43252411115433的记录,获得id = 3。
- 2)从主键(id)索引树查找id = 3对应的数据行。
- 3)重复步骤1和步骤2,直到找到card大于43252411115438的记录,结束循环,并将查询结果集返回给客户端。
以上处理流程中,查询card索引树5次,回表查询主键索引树4次。
覆盖索引指的是一个索引中包含了查询语句的查询结果,无需回表。如:select id from t_user where card between 43252411115433 and 43252411115438,该语句需要查询的id值存在于card索引树中,可以直接获得查询结果,无需回表。
由于覆盖索引可以减少树的搜索次数,显著提升查询性能,因此使用覆盖索引是一个常用的性能优化手段。
索引优化规则
索引最左前缀原则
最左前缀原则指的是在MySQL建立联合索引时,会遵守最左前缀匹配原则(即:最左优先),在检索数据时从联合索引的最左边开始匹配。
创建表:
create table t_user(
id int primary key,
name varchar(16),
age int not null,
gender varchar(1),
index idx_name_age_gender(name,age,gender)
)engine=InnoDB;
最左前缀原则示例:
- 检索('张'三,20,'F')这样的数据时,B+树会优先比较name来确定下一步的搜索方向,如果name相同,则再依次比较age和sex,最后得到检索的数据。
- 检索(20,'F')这样没有name的数据时,B+树无法确定下一步的搜索方向,因为建立搜索树时,name为第一个比较因子,必须要先根据name搜索才能确定下一步的搜索方向。
- 检索('张三','F')这样的数据时,B+树可以使用name来确定下一步的搜索方向,但下一个字段age缺失,所以只能先将name为张三的数据查询出来,再匹配性别为F的数据。
注意:最左前缀匹配规则可以是联合索引的最左N个字段,也可以是字符串索引的最左M个字符。
索引下推原则
MySQL5.6引入索引下推优化(index condition pushdown), 可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。
无索引下推,执行语句:
select * from t_user where name like '张%';
执行流程,如图所示:
图中,查询过程只会按顺序将name第一个字为'张'的记录取出来进行回表。因此,需要回表4次。
索引下推,执行语句:
select * from t_user where name like '张%' and age=20;
执行流程,如图所示:
图中,查询过程只会按顺序将name第一个字为'张'的记录取出来并判断age是否等于20,对于age不等于20的记录直接判断并跳过。因此,只需要对id为3和4的这两条记录进行回表(2次)。
【阅读推荐】
更多精彩内容,如:
- Redis系列
- 数据结构与算法系列
- Nacos系列
- MySQL系列
- JVM系列
- Kafka系列
请移步【南秋同学】个人主页进行查阅。内容持续更新中......
【作者简介】
一枚热爱技术和生活的老贝比,专注于Java领域,关注【南秋同学】带你一起学习成长~
- 上一篇:MySQL基础(索引分析和使用)
- 下一篇:mysql索引总结(一)
相关推荐
- Micheal Nielsen's神经网络学习之二
-
依然是跟着MichaelNielsen的神经网络学习,基于前一篇的学习,已经大概明白了神经网络的基本结构和BP算法,也能通过神经网络训练数字识别功能,之后我试验了一下使用神经网络训练之前的文本分类,...
- CocoaPods + XCTest进行单元测试 c单元测试工具
-
在使用XCTest进行单元测试时,我们经常会遇到一些CocoaPods中的开源框架的调用,比如“Realm”或“Alamofire”在测试的时候,如果配置不当,会导致“frameworknotfo...
- Java基础知识回顾第四篇 java基础讲解
-
1、&和&&的区别作为逻辑运算符:&(不管左边是什么,右边都参与运算),&&(如果左边为false,右边则不参与运算,短路)另外&可作为位运算符...
- 项目中的流程及类似业务的设计模式总结
-
说到业务流程,可能是我做过的项目中涉及业务最多的一个方面了。除了在流程设计之外,在一些考核系统、产业审批、还有很多地方,都用到相似的设计思路,在此一并总结一下。再说到模式,并不是因为流行才用这个词,而...
- 联想三款显示器首批获得 Eyesafe Certified 2.0 认证
-
IT之家7月31日消息,据外媒报道,三款全新联想显示器是全球首批满足EyesafeCertified2.0的设备。据报道,联想获得EyesafeCertified2.0认证的显...
- maven的生命周期,插件介绍(二) 一个典型的maven构建生命周期
-
1.maven生命周期一个完整的项目构建过程通常包括清理、编译、测试、打包、集成测试、验证、部署等步骤,Maven从中抽取了一套完善的、易扩展的生命周期。Maven的生命周期是抽象的,其中的具体任务都...
- 多线程(3)-基于Object的线程等待与唤醒
-
概述在使用synchronized进行线程同步中介绍了依赖对象锁定线程,本篇文章介绍如何依赖对象协调线程。同synchronized悲观锁一样,线程本身不能等待与唤醒,也是需要对象才能完成等待与唤醒的...
- jquery mobile + 百度地图 + phonegap 写的一个"校园助手"的app
-
1jquerymobile+百度地图+phonegap写的一个"校园助手"的app,使用的是基于Flat-UI的jQueryMobile,请参考:https://github.com/...
- Apache 服务启动不了 apache系统服务启动不了
-
{我是新手,从未遇到此问题,请各位大大勿喷}事由:今天早上上班突然发现公司网站出现问题。经过排查,发现是Apache出现问题。首先检查配置文件没有出问题后,启动服务发现Apache服务能启动,但是没法...
- 健康债和技术债都不能欠 公众号: 我是攻城师(woshigcs)
-
在Solr4.4之后,Solr提供了SolrCloud分布式集群的模式,它带来的主要好处是:(1)大数据量下更高的性能(2)更好扩展性(3)更高的可靠性(4)更简单易用什么时候应该使用Sol...
- Eye Experience怎么用?HTC告诉你 eyebeam怎么用
-
IT之家(www.ithome.com):EyeExperience怎么用?HTC告诉你HTC上周除了发布HTCDesireEYE自拍机和HTCRE管状运动相机之外,还发布了一系列新的智能手机...
- Android系统应用隐藏和应用禁止卸载
-
1、应用隐藏与禁用Android设置中的应用管理器提供了一个功能,就是【应用停用】功能,这是针对某些系统应用的。当应用停用之后,应用的图标会被隐藏,但apk还是存在,不会删除,核心接口就是Packag...
- 计算机软件技术分享--赠人玫瑰,手遗余香
-
一、Netty介绍Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。也就是说,Netty...
- Gecco爬虫框架的线程和队列模型 爬虫通用框架
-
简述爬虫在抓取一个页面后一般有两个任务,一个是解析页面内容,一个是将需要继续抓取的url放入队列继续抓取。因此,当爬取的网页很多的情况下,待抓取url的管理也是爬虫框架需要解决的问题。本文主要说的是g...
- 一点感悟(一) 初识 初读感知的意思
-
时间过得很快,在IT业已从业了两年多。人这一辈子到底需要什么,在路边看着人来人往,大部分人脸上都是很匆忙。上海真是一个魔都,它有魅力,有底蕴,但是一个外地人在这里扎根置业,真的是举全家之力,还贷3...
- 一周热门
-
-
Boston Dynamics Founder to Attend the 2024 T-EDGE Conference
-
IDC机房服务器托管可提供的服务
-
详解PostgreSQL 如何获取当前日期时间
-
新版腾讯QQ更新Windows 9.9.7、Mac 6.9.25、Linux 3.2.5版本
-
一文看懂mysql时间函数now()、current_timestamp() 和sysdate()
-
流星蝴蝶剑:76邵氏精华版,强化了流星,消失了蝴蝶
-
PhotoShop通道
-
查看 CAD文件,电脑上又没装AutoCAD?这款CAD快速看图工具能帮你
-
WildBit Viewer 6.13 快速的图像查看器,具有幻灯片播放和编辑功能
-
光与灯具的专业术语 你知多少?
-
- 最近发表
-
- Micheal Nielsen's神经网络学习之二
- CocoaPods + XCTest进行单元测试 c单元测试工具
- Java基础知识回顾第四篇 java基础讲解
- 项目中的流程及类似业务的设计模式总结
- 联想三款显示器首批获得 Eyesafe Certified 2.0 认证
- maven的生命周期,插件介绍(二) 一个典型的maven构建生命周期
- 多线程(3)-基于Object的线程等待与唤醒
- jquery mobile + 百度地图 + phonegap 写的一个"校园助手"的app
- Apache 服务启动不了 apache系统服务启动不了
- 健康债和技术债都不能欠 公众号: 我是攻城师(woshigcs)
- 标签列表
-
- serv-u 破解版 (19)
- huaweiupdateextractor (27)
- thinkphp6下载 (25)
- mysql 时间索引 (31)
- mydisktest_v298 (34)
- sql 日期比较 (26)
- document.appendchild (35)
- 头像打包下载 (61)
- oppoa5专用解锁工具包 (23)
- acmecadconverter_8.52绿色版 (39)
- oracle timestamp比较大小 (28)
- f12019破解 (20)
- np++ (18)
- 魔兽模型 (18)
- java面试宝典2019pdf (17)
- beamoff下载 (17)
- unity shader入门精要pdf (22)
- word文档批量处理大师破解版 (36)
- pk10牛牛 (22)
- server2016安装密钥 (33)
- mysql 昨天的日期 (37)
- 加密与解密第四版pdf (30)
- pcm文件下载 (23)
- jemeter官网 (31)
- iteye (18)