【MySQL 读书笔记】普通索引和唯一索引应该怎么选择
通常我们在做这个选择的时候,考虑得最多的应该是如果我们需要让 Database MySQL 来帮助我们从数据库层面过滤掉对应字段的重复数据我们会选择唯一索引,如果没有前者的需求,一般都会使用普通索引。这篇文章将会站在性能的角度来分析一下两者的区别对性能的影响。
这里还是用一张之前分析索引用到的图。
查询过程
在我们查询的时候我们使用 select id from T where k=5。这个查询语句通过查询逐渐搜索到 B+Tree 的叶子节点,然后取到对应的数据页,然后在数据页内部找到对应记录。我记得没错的话数据页内部似乎是链表形式存储的。
对于普通索引来说,查找到对应满足的条件 500 之后,还需要找下一个记录,直到碰到第一个不满足 k=5 的条件记录。
对于唯一索引来说,查找到第一条满足条件的记录之后,就会立即停止继续检索。
这两者带来的性能差距是微乎其微的。
首先我们将数据页从磁盘里面读出来是读出整个16kb 的数据页,那么当我们在找到第一个满足条件的记录的时候其实绝大多数情况我们要读取的下一个记录也在内存中。即使不在,我们也只需要再读取一个数据页,并且我记得数据页之间是有指针直接可以取到下一个数据页位置的。这个优化进一步优化了读取连续数据页的性能,可以认为这样的操作成本很低。
更新过程
为了说明普通索引和唯一索引对更新语句性能的影响,我们先来普及一下 change buffer 这个概念。
当我们需要更新一个数据页的时候,如果数据也在内存中久直接更新,而如果这个数据页没有在内存中,在不影响数据一致性的情况下, InnoDB 会将这些更新操作缓存在 change buffer 中。这样就不需要立即取磁盘中读取这个数据页进行engine了。在下次需要访问这个数据页的时候,再将数据页读入内存,然后执行 change buffer 中相关数据页的操作。这种方式就能保证数据逻辑的正确性,并且节省随机读取 IO 消耗,而不是进行频繁随机读取。这里要特别注意,随机读写可能是数据库里面消耗最高的操作了。
需要说明的是,虽然名字叫作 change buffer 实际上它是可以持久化的数据。也就是说, change buffer 在内存中有拷贝,也会被写入到磁盘上。
将 change buffer 中的操作应用到原数据页,得到最新的结果的过程称为 merge。除了访问这个数据页会触发 merge 外系统有后台线程会定期 merge。在数据库正常关闭的过程中,也会触发 merge 操作。
数据读入内存是需要占用 buffer pool 的,如果我们需要更新的操作记录在 change buffer ,可以减少读磁盘,而且这种方式可以用避免短时间内占用内存,提高内存利用率。
那么哪些情况下可以使用 change buffer 呢?
对于唯一索引的情况所有的更新情况都要判断是否会违反唯一性约束,比如我们在插入一条记录的时候,我们需要先判断是否已经存在这条记录,只要有我们去扫表才能判断,我们就需要把对应的数据页读入内存,如果已经读入内存,如果已经读入内存就直接更新插入就行。没有必要去使用 change buffer ,反正都必须先读入内存。
因此,唯一索引的更新就不能使用 change buffer 这个东西。实际上就只有普通索引可以使用 change buffer.
change buffer 用的是 InnoDB buffer pool 里面的内存,change buffer 的大小可以通过参数 innodb_change_buffer_max_size 来动态设置。这个参数默认是 25。表示最多占用 buffer pool 百分之 25 应用于作 change buffer。
Change Buffer 的使用场景
通过上面的分析,你已经清楚了使用 change buffer 对更新过程的加速作用,也清楚了 change buffer 只限于用在普通索引的场景下,而不适用于唯一索引。那么,现在有一个问题就是:普通索引的所有场景,使用 change buffer 都可以起到加速作用吗?
因为 merge 的时候是真正进行数据更新的时刻,而 change buffer 的主要目的就是将记录的变更动作缓存下来,所以在一个数据页做 merge 之前,change buffer 记录的变更越多(也就是这个页面上要更新的次数越多),收益就越大。
因此,对于写多读少的业务来说,页面在写完以后马上被访问到的概率比较小,此时 change buffer 的使用效果最好。这种业务模型常见的就是账单类、日志类的系统。
反过来,假设一个业务的更新模式是写入之后马上会做查询,那么即使满足了条件,将更新先记录在 change buffer,但之后由于马上要访问这个数据页,会立即触发 merge 过程。这样随机访问 IO 的次数不会减少,反而增加了 change buffer 的维护代价。所以,对于这种业务模式来说,change buffer 反而起到了副作用。
所以实际情况中,如果是并发量不高的业务,还是在底层对数据做保护不要犹豫,直接选用唯一索引是比较好的选择。如果是对更新性能要求极高的场景,可以考虑建普通索引,然后在代码里面对唯一性的情况进行保护。
change buffer 和 redo log
理解了 change buffer 的原理,你可能会联想到我在前面文章中和你介绍过的 redo log 和 WAL。
在前面文章的评论中,我发现有同学混淆了 redo log 和 change buffer。WAL 提升性能的核心机制,也的确是尽量减少随机读写,这两个概念确实容易混淆。所以,这里我把它们放到了同一个流程里来说明,便于你区分这两个概念。
现在,我们要在表上执行插入语句:
mysql> insert into t(id,k) values(id1,k1),(id2,k2);
这里,我们假设当前 k 索引树的状态,查找到位置后,k1 所在的数据页在内存 (InnoDB buffer pool) 中,k2 所在的数据页不在内存中。如图 2 所示是带 change buffer 的更新状态图。
这条插入操作做了如下操作:
1. page 1 在内存中,直接更新内存。
2. page 2 没有在内存中,就在内存的 change buffer 区域,记录下「我要往 page 2 插入行」这个信息。
3. 将这两个动作记入 redo log 中。
做完这些事情事务就可以完成了。成本是 更新两处内存,一个 直接更新内存 一个 更新 change buffer。然后一次写 redo log 写磁盘操作。
同时图中虚线部分不是后台操作,不影响更新时间。
前面我说了触发 merge 除了定时 merge 正常 shutdown MySQL 以外 如果立即查询对应页上的数据也会立即触发和 change buffer 的 merge。
如果读语句发生在更新语句后不久,内存中的数据都还在,那么此时的这两个读操作就与系统表空间(ibdata1)和 redo log(ib_log_fileX)无关了。所以,我在图中就没画出这两部分。
从图中可以看到:
1. 读 Page 1 的时候,直接从内存返回。有几位同学在前面文章的评论中问到,WAL 之后如果读数据,是不是一定要读盘,是不是一定要从 redo log 里面把数据更新以后才可以返回?其实是不用的。你可以看一下图 3 的这个状态,虽然磁盘上还是之前的数据,但是这里直接从内存返回结果,结果是正确的。
2. 要读 Page 2 的时候,需要把 Page 2 从磁盘读入内存中,然后应用 change buffer 里面的操作日志,生成一个正确的版本并返回结果。
可以看到直到要读 page 2 的时候,这个数据页才会被读入内存。
所以,如果要简单对比这两个机制在提升更新性能上的收益的话, redo log 主要节省的是随机写的磁盘的 io 消耗。
由于唯一索引用不上 change buffer 的优化机制,因此如果业务可以接受。从性能的角度出发应该先考虑非唯一索引。但是我前面也说了,还是看业务来,如果对性能本就没有什么要求,并且代码质量才是头等大问题,那应该毫不犹豫的使用唯一索引。可以让你避免很多麻烦。
Reference:
本读书笔记皆来自发布在极客时间的 林晓斌(丁奇)的 MySQL 实战45讲:
极客时间版权所有: https://time.geekbang.org/ 版权所有:
https://time.geekbang.org/column/article/70848
【MySQL 读书笔记】普通索引和唯一索引应该怎么选择的更多相关文章
- MySQL 笔记整理(9) --普通索引和唯一索引,应该怎么选择?
笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> (本篇内图片均来自丁奇老师的讲解,如有侵权,请联系我删除) 9) --普通索引和唯一索引,应该怎么选择? 假如你在维护一个市民系统, ...
- MySQL 普通索引和唯一索引的区别
该文为< MySQL 实战 45 讲>的学习笔记,感谢查看,如有错误,欢迎指正 一.查询和更新上的区别 这两类索引在查询能力上是没差别的,主要考虑的是对更新性能的影响.建议尽量选择普通索引 ...
- MySQL的几个概念:主键,外键,索引,唯一索引
概念: 主键(primary key) 能够唯一标识表中某一行的属性或属性组.一个表只能有一个主键,但可以有多个候选索引.主键常常与外键构成参照完整性约束,防止出现数据不一致.主键可以保证记录的唯一和 ...
- Mysql索引介绍及常见索引(主键索引、唯一索引、普通索引、全文索引、组合索引)的区别
Mysql索引概念:说说Mysql索引,看到一个很少比如:索引就好比一本书的目录,它会让你更快的找到内容,显然目录(索引)并不是越多越好,假如这本书1000页,有500也是目录,它当然效率低,目录是要 ...
- MySQL 普通索引、唯一索引和主索引
1.普通索引 普通索引(由关键字KEY或INDEX定义的索引)的唯一任务是加快对数据的访问速度.因此,应该只为那些最经常出现在查询条件(WHEREcolumn=)或排序条件(ORDERBYcolumn ...
- mysql中怎样查看和删除唯一索引
mysql中怎样查看和删除唯一索引. 查看唯一索引: show index from mytable;//mytable 是表名 查询结果例如以下: 查询到唯一索引后,怎样删除唯一索引呢,使用例如以下 ...
- Mysql主键索引、唯一索引、普通索引、全文索引、组合索引的区别
原文:Mysql主键索引.唯一索引.普通索引.全文索引.组合索引的区别 Mysql索引概念: 说说Mysql索引,看到一个很少比如:索引就好比一本书的目录,它会让你更快的找到内容,显然目录(索引)并不 ...
- MYSQL学习笔记——sql语句优化之索引
上一篇博客讲了可以使用慢查询日志定位耗时sql,使用explain命令查看mysql的执行计划,以及使用profiling工具查看语句执行真正耗时的地方,当定位了耗时之后怎样优化呢?这篇博客会介绍my ...
- 如何选择普通索引和唯一索引《死磕MySQL系列 五》
系列文章 一.原来一条select语句在MySQL是这样执行的<死磕MySQL系列 一> 二.一生挚友redo log.binlog<死磕MySQL系列 二> 三.MySQL强 ...
随机推荐
- docker-compose-volumes的说明
docker-compose里两种设置方式都是可以持久化的 绝对路径的 ghost: image: ghost volumes: - ./ghost/config.js:/var/lib/ghost/ ...
- 前端笔记之移动端&响应式(中)视口&百分比布局&弹性盒模型&rem&fillpage
一.viewport视口 1.1什么是屏幕尺寸.屏幕分辨率.屏幕像素密度? 屏幕尺寸:指屏幕的对角线的长度,单位是英寸,常见的屏幕尺寸有3.5.3.7.4.2.4.7.5.0.5.5.6.0等. 屏幕 ...
- parsing:NLP之chart parser句法分析器
已迁移到我新博客,阅读体验更佳parsing:NLP之chart parser句法分析器 完整代码实现放在我的github上:click me 一.任务要求 实现一个基于简单英语语法的chart句法分 ...
- 2018~第三届南宁市网络安全技术大赛~nnctf~write-up
Web 1.超简单 分值:100 类型:WEB 已解决 题目:超简单的web题 http://gxnnctf.gxsosec.cn:12311/ 代码审计 <?php $white_list ...
- EasyUI List<T>转tree数据格式
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using S ...
- 杭电ACM2020--绝对值排序
输入n(n<=100)个整数,按照绝对值从大到小排序后输出.题目保证对于每一个测试实例,所有的数的绝对值都不相等. Input 输入数据有多组,每组占一行,每行的第一个数字为n,接着是n个整 ...
- HeadFirst设计模式读书笔记之工厂模式
1. 简单工厂 1. 你开了一家披萨店,点披萨的方法可能是这样: public Pizza orderPizza(String type) { Pizza pizza; if (type.equals ...
- NET4.6下的UTC时间转换
int UTCSecond = (int)((DateTimeOffset)DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local)).ToUnix ...
- Android设计模式总结
1.复合模式:三层架构.MVC.MVP.MVVM 2.设计模式-单例模式 配置类的使用. 3.设计模式-模板方法 通过抽象类或接口提前定义要实现的方法. 4.设计模式-观察者模式 消息的通知. 5.设 ...
- 如何快速清理 docker 资源
如果经常使用 docker,你会发现 docker 占用的资源膨胀很快,其中最明显也最容易被察觉的应该是对磁盘空间的占用.本文将介绍如何快速的清理 docker 占用的系统资源,具体点说就是删除那些无 ...