网易首页 > 网易号 > 正文 申请入驻

MySQL 入门(2):索引

0
分享至

摘要

在这篇文章中,我会先介绍一下什么是索引,索引有什么作用。

之后会介绍一下索引的数据结构是什么样的,有什么优点,又会带来什么样的问题。

在分析完数据结构后,我们可以根据这个数据结构,研究索引的用法,以及如何设计更高效的缓存。

最后,我会对上一篇的内容进行补充,介绍change buffer的作用以及分析change buffer对性能的影响。

1 目的

在我们学习索引之前,我们要先了解它是什么,以及有什么作用。

官方对于索引的定义是这样的:

Indexes are used to find rows with specific column values quickly. Without an index, MySQL must begin with the first row and then read through the entire table to find the relevant rows.

也就是说,索引是用来快速查找具有特定值的一行数据(的一种数据结构)。如果没有索引,MySQL必须得从第一行开始逐行扫描数据。

尤其是当我们的数据量越来越大的时候,恰当的索引是可以帮助我们拥有更优秀的性能的。

这句话的另外一层含义在于:如果索引设计的不好,可能会使得我们的数据库性能变得更加的糟糕。

那么,索引到底是什么呢?我们接着往下看。

2 模型

在讲索引具体的数据结构之前,我们来想象一下我们在英文词典里面找一个单词。

如果我们需要找一个单词:“awesome”!

我们会在目录里面找到以字母 A 开头的一系列单词,然后从以字母 A 开头的一系列单词中找到 W ,然后是 E

就这样不断地往下查找,不断缩小我们的查找范围。如果我们不使用目录,直接在正文里面找这个单词,可能需要花费更多的时间。

况且,这个词典里面的单词是排好序的,如果我们找 Z 开头的字母,可能得找好几百页,才能最终找到。

这个例子不能说特别的准确,但是反映了索引的核心:减少查找的次数

我们都知道,MySQL的数据保存在了磁盘中。而磁盘的IO是最慢的。所以,减少磁盘的读写是提高性能必不可少的做法。虽然现在大多数计算机已经使用了SSD,不再需要寻道等,但是索引的原则还是成立的。

这里我们来看看InnoDB的B+树是怎么实现的(图来自于《高性能MySQL》):

可以看出,这是一颗N叉树,树中的每一个结点,都是MySQL中的一个数据页。

其实说白了这里的N叉树,和二叉查找树查找逻辑是一样的。只不过不同的地方在于这里的每一个结点,包含了比二叉查找树更多的数据与指针。这样做的目的是使得在数据量相同的情况下,B+树可以使得树的高度更低。

而又因为所有的数据页都是持久化保存在磁盘中的,所以更低的高度意味着查找一个数据需要进行磁盘IO的次数越少,效率变得更高。

注意,因为N叉树的N越大,对应的树的高度就会越低。而每一个结点(每一个数据页)的大小是固定的(默认是16K,可以使用innodb_page_size参数修改),所以当设置为索引的key越小的时候,N就会越大。

3 分类

在经过上面的介绍之后,我想你应该能理解索引的查找方法了。下面我们再来说说索引的分类:

主键索引非主键索引

主键索引,就是非叶子结点中存储的值都是主键的值,在查找的时候通过主键查找。直到查找到最后的叶子节点。在最后的叶子节点中保存了这个主键对应的整行数据

非主键索引,就是非叶子结点中存储的值都是索引的值,查找的时候通过这一个数值进行查找。查找到最后的叶子节点,保存了对应的主键ID。然后,MySQL会根据查到的主键,再查找主键索引对应的B+树,直到找到这一行的所有数据。而这个通过查找到主键,然后再利用主键来再次查找,或者这一行数据的过程,称为回表

注意,我们在新建一张表的时候,一定会有一颗以主键为索引的B+树。哪怕你没有设置主键,MySQL都会选一个不包含NULL的第一个唯一索引列作为主键列,并把它用作一个主键索引。如果没有这样的索引就会使用行号生成一个聚集索引,把它当作主键。

此外,每增加一个索引,MySQL就会多维护一颗B+树。维护B+树的过程也是很复杂的,涉及到了页的分裂等,我想在以后的文章进行介绍。

另外之前也提到了,影响MySQL性能的一个很重要的因素就是磁盘IO。而回表这个操作,无异于增加了很多的IO次数。

那么有什么办法可以减少这一部分的开销吗,我们接着往下看。

4 联合索引

我们在上面提到的索引,都是单个的数据进行查找。

这样的话,我们每次对其中一个列建立一个索引,就得多维护一颗B+树,同样对性能和空间造成了浪费。

那么我们有没有可能同时对多个数据进行排序,然后再进行查找呢?答案是可以的,我们可以采用联合索引。

4.1 最左前缀

以上面这张图为例:

我们建立了一个(姓名,年龄)的联合索引

如果我们需要找一个15岁的法外狂徒(误)张三:

select * from user where name = “张三” and age = 15;

因为此时我们的查找条件完全匹配了我们定义的索引,所以MySQL会先从查找的第一个条件开始,找到名为“张三”的数据,然后此时会继续判断第二个年龄为15岁的条件,因为此时大于第一个数据项中的10岁,且小于第二个数据项中的20岁,所以会从第二个指针往下寻找,查找大于10岁且小于20岁的“张三”。

这种条件和索引完全匹配的查找过程,称为全值匹配查询

但是,假设我们没有设置多个查找条件,只搜索名字为“张三”的人。

select * from user where name = "张三";

那么此时的查找过程不会去匹配年龄这一列,只会比较姓名这一列。所以,会从这颗B+树最左边的结点,8岁的张三开始,不断的向后遍历,直到这个数据的姓名不叫“张三”为止。

这样的查找过程,称为最左前缀查找。简单的来解释,就是查找的条件只要是符合这个联合索引,或者符合这个联合索引的最左边几项,索引就会生效,也就实现了“剪枝”的目的,加速了查找的速度。只有剩下的那些不符合最左前缀的条件,才会依次遍历来进行匹配。

也就是说,只要满足最左前缀,就可以利用索引来加速检索。这个最左前缀可以是联合索引的最左N个字段,也可以是字符串索引的最左M个字符。

那么,什么时候最左前缀不会生效呢?

假设有这么一个联合索引(a, b, c, d, e, f)。那么查找条件是(a)、(a, b)、(a, b, c)等都是称为符合最左前缀的。也就是说,一定要从索引的最左边开始,任意N个字段或者M个字符。

但是如果我们使用了(a, c, d)这样的查找条件,那么只会对(a)起作用,(c, d)是不会生效的。因为最左前缀被中断了。

而如果是(e, f)这样的查找条件,也同样不会生效,因为也不符合最左边的N个字段的规则,不属于最左前缀。

所以,索引的复用能力是我们在建立联合索引时候的一个评估标准。因为可以支持最左前缀,所以当已经有了(a,b)这个联合索引后,一般就不需要单独在a上建立索引了。因此,第一原则是,如果通过调整顺序,可以少维护一个索引,那么这个顺序往往就是需要优先考虑采用的。

但是,联合索引也不是越长越好。我们在前面提到过,要尽可能的让N叉树的N比较大,这样树的高度会比较低,以此来减少磁盘的IO次数。如果联合索引包含的字段比较多,在页面大小固定的情况下,会造成N值的减少,反而会减慢效率。

4.2 索引下推

继续上面的法外狂徒的例子。

假设我们的语句是这样的:

select * from user where name like "张%" and age = 15;

很好理解,我们会觉得MySQL会从名字以“张”开头的数据开始遍历,然后判断年龄是否为15。

但是最左前缀有一个非常重要的原则:MySQL会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配。

也就是说,此时我们的查询,age这个索引是用不上的。

所以,在MySQL5.6之前,只要找到了符合以“张”开头的名字这个条件,就会通过这个数据的主键ID,进行回表的操作,然后查找这个数据的年龄是否为15。

而MySQL 5.6 引入的索引下推优化(index condition pushdown), 可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。也就是说,直到找到了以“张”开头的名字并且年龄为15岁,才会进行回表。

此外,在回表之前,如果使用了Multi-Range Read (MRR)这个策略,在取出主键后,回表之前,会在对所有获取到的主键排序。

4.3 覆盖索引

还记得我们前面说到的吗,如果我们采用的是非主键索引,那么我们查到了这个数据之后,还需要根据叶子节点中的主键,再回表一次。

覆盖索引可以解决这个问题。比如我们前面查找“张三”的时候,我们也可以同时找到他的年龄。比如(a,b)这样的联合索引,在我们使用

select b form table_name where a = xxx;

这么一条语句的时候,找到了符合条件的a,不需要通过主键来进行回表,找到b的值,而是会直接返回记录在这颗B+树中的值。也就是说,在这个查询里面,索引(a,b)已经“覆盖了”我们的查询需求,我们称为覆盖索引。

5 唯一索引与普通索引

· 普通索引:加快对数据的访问速度

· 唯一索引:不允许重复的普通索引

5.1 查询

我们先来分析查询方面的性能。

对于查询来说,如果这个是普通索引,那么在找到了符合条件的数据之后,会往后继续遍历,直到碰到不满足的数据为止。

如果是唯一索引,由于他的唯一性,只要找到了,那就直接返回就行,不需要继续往后遍历。

其实两者的性能差距微乎其微。

为什么呢?你可能会想:普通索引还需要继续遍历,有可能会更慢。但是,我们之前提到过,查询操作是需要把数据读到内存的,并且是以数据页的形式读到内存。而在内存中的遍历操作,速度方面的差距是特别小的。

就算普通索引的最后一项还是相同的,需要通过磁盘IO来读取下一页,这个时候可能是比较耗费时间的。不过因为一个数据页包含了特别多的数据,这种可能性是特别低的。

5.2 插入

在我们说到插入之前,我先要跟你介绍一下change buffer这个东西。

我在上一篇文章中提到:在我们需要更新数据的时候,先把数据从磁盘读到内存中,修改这个数据,然后修改redolog,增加binlog,等内存满了之后或者redolog写满了之后,再将脏页刷回磁盘。

那么插入数据呢?

在我们新增了一条数据之后,MySQL并不会将这个插入直接写入磁盘中,而是会将这个修改写入change buffer中。

在之后有关于这个数据页的查询请求的时候,才会读取这一个数据页,然后根据change buffer中关于这一页的记录,依次更新到读取到了内存中的数据页中,这个过程称为merge。在更新完毕之后,才把查询结果返回。

但是这样有什么用呢?

假设我们插入的普通索引不在内存中,此时有两个作用:

第一,因为我们在插入一条数据的时候,不需要通过磁盘的IO把需要写入的数据页调入内存中进行修改,而是会将这个插入行为记录下来,在之后才统一对脏页进行刷回磁盘的操作。也就是说,change buffer避免了每次都需要调入一个数据页进内存中进行修改,造成脏页过多的问题。

第二,也是最重要的,change buffer的设计避免了在每一次插入过程中为了寻找数据页而进行的随机IO。并且,在之后对脏页进行刷新的时候,MySQL会尽可能的让脏页可以是以顺序IO的方式刷新回磁盘中。

这个过程对于普通索引来说是提升的非常大的。

简单的来说,change buffer的主要目的就是将记录的变更动作缓存下来。所以在一个数据页做merge之前,change buffer记录的变更越多(也就是这个页面上要更新的次数越多),收益就越大。

但是对于唯一索引来说,因为唯一索引的约束是“数据唯一”。所以还是需要找到这个数据页,判断有无冲突,才会进行插入。这样的话,change buffer不起作用。

然后我们来把change buffer与之前提到的redo log联系在一起。

比如我们需要插入两条数据,其中一条数据所在的数据页在内存中,另外一条数据所在的数据页在磁盘中(还未读入内存),且这两条数据所用到的索引是普通索引(不需要验证是否重复)。

此时,对于在数据页在内存中的插入操作,直接修改内存,对于数据页不在内存中的插入操作,将这个插入操作记录在change buffer中。随后,将这两次的操作,记录在了redo log中,然后增加binlog。当这两个日志文件都写好后,返回,操作结束。

而对于何时将内存中的脏页刷回磁盘,是另外的一个操作。

此外,这里的change buffer也同样可以被持久化,也遵循checkpoint机制,即change buffer会标记哪些记录是已经merge到数据页中,哪些还没有。

在MySQL5.5以后,除了插入操作,更新操作和删除操作,也支持使用change buffer。也就是说,对于更新操作和删除操作,也会被change buffer记录下来,在之后才进行merge。

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
月之暗面回应创始人杨植麟套现数千万美元:消息不实

月之暗面回应创始人杨植麟套现数千万美元:消息不实

财联社
2024-04-23 14:16:12
伊朗和以色列的冲突,让全世界看到了真正的“纸老虎”长啥样!

伊朗和以色列的冲突,让全世界看到了真正的“纸老虎”长啥样!

翻开历史和现实
2024-04-23 13:27:27
外媒:“亲华派”获得压倒性胜利

外媒:“亲华派”获得压倒性胜利

环球时报新闻
2024-04-22 15:21:24
中国学者连续论“俄罗斯必败”!中国该如何做正确的事?

中国学者连续论“俄罗斯必败”!中国该如何做正确的事?

丰慢慢
2024-04-23 16:00:10
北京最年轻副市长落马!最后细节发人深省…

北京最年轻副市长落马!最后细节发人深省…

仕道
2024-04-23 10:54:47
女子陪男友坐硬座回家,因清纯甜美火出圈,网友:娶她是福气

女子陪男友坐硬座回家,因清纯甜美火出圈,网友:娶她是福气

佑宛故事汇
2024-04-22 16:02:05
烦透了!上海市民大量投诉:刚折腾完又开挖…“拉链马路”有人管吗?负责人坦言:尽力了

烦透了!上海市民大量投诉:刚折腾完又开挖…“拉链马路”有人管吗?负责人坦言:尽力了

上观新闻
2024-04-22 11:04:36
华人老板与妻子在日本被杀,并被焚尸抛弃?嫌疑人浮出水面

华人老板与妻子在日本被杀,并被焚尸抛弃?嫌疑人浮出水面

新民晚报
2024-04-23 11:36:58
真没办法!苏群:湖人打的不可能比这一场更好了!

真没办法!苏群:湖人打的不可能比这一场更好了!

直播吧
2024-04-23 12:51:21
中将郑家概死亡,仅63岁,出身将门,死因曝光,半年前曾公开露面

中将郑家概死亡,仅63岁,出身将门,死因曝光,半年前曾公开露面

温柔看世界
2024-04-22 23:10:51
狼终于来了!黄金白银暴跌,千万别低估了美联储的决心

狼终于来了!黄金白银暴跌,千万别低估了美联储的决心

云姐闲聊
2024-04-23 08:26:59
伟伟道来|610亿美元对乌援助,两顶帽子背后的三层逻辑

伟伟道来|610亿美元对乌援助,两顶帽子背后的三层逻辑

经济观察报
2024-04-23 15:29:07
美国被曝正起草可能切断中国一些银行与全球金融体系联系的制裁措施 外交部回应

美国被曝正起草可能切断中国一些银行与全球金融体系联系的制裁措施 外交部回应

财联社
2024-04-23 15:50:07
你偷燃气十年起步,燃气偷你道歉言和,这是什么奇葩对比?

你偷燃气十年起步,燃气偷你道歉言和,这是什么奇葩对比?

走读新生
2024-04-22 21:47:54
完全看不见!穆雷谈绝杀:每个人都在大声喊叫 我才知道进了~

完全看不见!穆雷谈绝杀:每个人都在大声喊叫 我才知道进了~

直播吧
2024-04-23 14:00:14
空砍32分!浓眉末节0分成绝杀背景,跌落掘金替补席被詹姆斯拉走

空砍32分!浓眉末节0分成绝杀背景,跌落掘金替补席被詹姆斯拉走

体坛野秀才
2024-04-23 13:11:21
朱立伦开出访陆条件无回音,国民党多人退出访团,恐只剩傅崐萁一人

朱立伦开出访陆条件无回音,国民党多人退出访团,恐只剩傅崐萁一人

书山有路勤为径
2024-04-23 09:42:38
官推直言心痛:AD末节0分1板隐身成罪人 体能不支防穆雷压哨失败

官推直言心痛:AD末节0分1板隐身成罪人 体能不支防穆雷压哨失败

颜小白的篮球梦
2024-04-23 13:06:02
国产手机有多无耻?与联发科合谋将3年前的芯片改名重新上市

国产手机有多无耻?与联发科合谋将3年前的芯片改名重新上市

柏铭锐谈
2024-04-23 11:29:04
谈判再次破裂?我国或将关闭大使馆?外交部提醒:中方公民勿前往

谈判再次破裂?我国或将关闭大使馆?外交部提醒:中方公民勿前往

星辰故事屋
2024-04-22 18:16:54
2024-04-23 17:34:44
勤劳搬运工
勤劳搬运工
分享技术干货
102文章数 2409关注度
往期回顾 全部

科技要闻

Meta宣布开放这一系统,自比安卓对抗苹果

头条要闻

"消失的她"原型重返泰国:做8次手术 去年才重新站起来

头条要闻

"消失的她"原型重返泰国:做8次手术 去年才重新站起来

体育要闻

快船独行侠G1:猛踹瘸子那条好腿

娱乐要闻

赵丽颖晒照似初恋,拍照疑与儿子分享

财经要闻

千元“特供”酒 猫腻有几何

汽车要闻

哪吒首款大五座SUV 哪吒L售价12.99万起

态度原创

本地
游戏
艺术
房产
公开课

本地新闻

我和我的家乡|潍坊人真是什么都敢往天上放啊

钢岚:4月版本之机兵红黑榜公布!红榜第一真是没有任何悬念!

艺术要闻

蔚为大观,书香盛宴再启航!第三届嘉德国际艺术图书展正在呈现

房产要闻

供应量环跌71%!海口这个月又只发了5张证!

公开课

睡前进食会让你发胖吗?

无障碍浏览 进入关怀版