笔记记录自林晓斌(丁奇)老师的《MySQL实战45讲》

(本篇内图片均来自丁奇老师的讲解,如有侵权,请联系我删除)

9) --普通索引和唯一索引,应该怎么选择?

  假如你在维护一个市民系统,每个人都有唯一的身份证号,而且业务代码也已经保证了不会写入两个相同的身份证号。如果需要按身份证号来查找,你可能会执行类似这样的SQL语句:

select name from CUser where id_card = 'xxxxxxxxyyyyyzzz';

  由于身份证号id_card字段较长,不建议将身份证号当做主键,那么现在你有两个选择。要么给id_card建立一个普通索引,要么给它建立一个唯一索引。由于业务代码已经保证了身份证号的唯一性,这两个选择在逻辑上都是正确的。那么,从性能角度来考虑,你会选择普通索引还是唯一索引呢?为什么呢?

  前面我们提到过,在InnoDB中,索引是以B+树的形式存在的。查询语句在索引树的查找过程,先是通过B+树从树根开始,按层搜索到叶子节点,然后再进行判断.(当然如果查询条件比较多,可能需要回表,具体请参考之前的文章。)

  •   对于普通索引来说,查找到满足条件的id_card之后,需要再去查找下一个记录,直到碰到id_card不符合要求的记录。
  •   对于唯一索引,由于索引保证了唯一性,查找到第一个符合条件的id_card之后就会立即返回,不再进行检索了。

  那么这两个索引之间的不同带来的性能差异会有多少呢?答案是,微乎其微。InnoDB的数据是按数据页为单位来读写的。也就是说,当需要一条记录的时候,并不是将这个记录本身从磁盘读出来,而是以页为单位,将其整体读入内存。在InnoDB中,每个数据页的大小默认是16KB。因为引擎是按页读写的,所以,当找到符合id_card的记录的时候,它所在的数据页就都在内存中了。那么,对于普通索引来说,只不过是多做一次“查找和判断下一条记录”的操作,只需要一次指针寻找和一次计算。当然,如果这条记录恰好是这个数据页的最后一条,那么就必须读取下一个数据页才能进行判断。但,一个数据页可以放下很多个key,这种情况出现的概率会很低。所以,对于查询来说,这两种索引的性能差异是不大的。

更新过程:

  这里,我们需要先介绍一下Change buffer。当需要更新一个数据页的时候,如果这个数据页在内存中就直接更新,如果这个数据页不在内存中,在不影响数据一致性的前提下,InnoDB会将这些更新操作缓存在change buffer中,这样就不需要从磁盘中读入这个数据页了。在下次查询需要访问这个数据页的时候,将数据页读入内存中,然后执行change buffer中与这个页有关的操作。通过这种方式就能保证这个数据逻辑的正确性。需要说明的是,虽然名叫Change buffer,实际上它是可以持久化的数据。也就是说,change buffer在内存中有拷贝,也会被写入到磁盘上。

  将Change buffer中的操作应该用到原数据页,得到最新结果的过程叫做merge.除了访问这个数据页会触发merge外,系统后台线程会定期merge。在数据库正常关闭(shutdown)的过程中,也会执行merge操作。显然,如果能够将更新操作先记录在change buffer,减少读磁盘,语句的执行速度会得到明显的提升。而且,数据读入内存是需要占用buffer pool的,所以这种方式还能避免占用内存,提高内存利用率。那么,什么条件下可以使用change buffer呢?

  对于唯一索引来说,所有的更新操作都要先判断这个操作是否违反了唯一性约束。比如要插入某条记录,就要先判断现在在表中是否已经存在了索引相同的记录,而这必须要将数据页读入到内存中才能进行判断。如果都已经读入到内存中了,那么直接更新内存会更快,没必要使用change buffer了。因此,唯一索引的更新就不能使用change buffer,实际上也只有普通索引可以使用?

  change buffer用的是buffer pool里的内存,因此不能无限增大。change buffer的大小,可以通过参数innodb_change_buffer_max_size来动态设置。这个参数设置为50的时候,表示change_buffer的大小最多只能占用buffer pool的50%。那么,如果要在一张表里插入一条新记录的时候InnoDB的处理流程是怎样的呢?

  第一种情况,这个记录要更新的目标在内存中。这时,InnoDB的处理流程如下:

  •   对于唯一索引来说,找到要插入的位置,判断有没有冲突,插入这个值,语句执行结束
  •   对于普通索引来说,找到要插入的位置,插入这个值,语句执行结束。

  这样看来,两种方式的差别知识一个判断,影响很小。我们再来看看第二种情况,这个记录要更新的目标不再内存中,这时,处理的流程如下:

  •   对于唯一索引,需要将数据页读入内存,判断有没有冲突,插入这个值,语句执行结束。
  •   对于普通索引,将更新记录在change buffer,语句执行结束。

  将数据从磁盘读入内存涉及随机IO访问,是数据库里成本最高的操作之一。Change buffer因为减少了随机磁盘访问,所以对更新的提升是明显的。

索引的选择和实践:

  这两类索引再查询能力上是没差别的,主要考虑的是对更新性能的影响。所以建议尽量选择普通索引。如果所有的更新后面,都马上伴随着对这个记录的查询,那么你应该关闭change buffer.而在其他情况下,change buffer都能提升更新性能。实际上,普通索引和change buffer的配合使用,对于数据量大的表的更新优化是很明显的。

上期问题:

  上期问题可以点击这里查看 。两种方式为:

  

  

问题:

  change buffer一开始是写内存的,那么如果这个时候及其掉电重启,会不会导致change buffer丢失呢?change buffer丢失可不是小事,因为丢失以后就无法再进行merge了,等于是数据丢失了,会不会出现这种情况呢?

  

MySQL 笔记整理(9) --普通索引和唯一索引,应该怎么选择?的更多相关文章

  1. 【MySQL 读书笔记】普通索引和唯一索引应该怎么选择

    通常我们在做这个选择的时候,考虑得最多的应该是如果我们需要让 Database MySQL 来帮助我们从数据库层面过滤掉对应字段的重复数据我们会选择唯一索引,如果没有前者的需求,一般都会使用普通索引. ...

  2. MySQL 笔记整理(4) --深入浅出索引(上)

    笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> 4) --深入浅出索引(上) 一句话简单来说,索引的出现其实就是为了提高数据查询的效率,就像书的目录一样. 索引的常见模型 哈希表: ...

  3. 最全mysql笔记整理

    mysql笔记整理 作者:python技术人 博客:https://www.cnblogs.com/lpdeboke Windows服务 -- 启动MySQL net start mysql -- 创 ...

  4. MySQL 普通索引和唯一索引的区别

    该文为< MySQL 实战 45 讲>的学习笔记,感谢查看,如有错误,欢迎指正 一.查询和更新上的区别 这两类索引在查询能力上是没差别的,主要考虑的是对更新性能的影响.建议尽量选择普通索引 ...

  5. MySQL的几个概念:主键,外键,索引,唯一索引

    概念: 主键(primary key) 能够唯一标识表中某一行的属性或属性组.一个表只能有一个主键,但可以有多个候选索引.主键常常与外键构成参照完整性约束,防止出现数据不一致.主键可以保证记录的唯一和 ...

  6. Mysql索引介绍及常见索引(主键索引、唯一索引、普通索引、全文索引、组合索引)的区别

    Mysql索引概念:说说Mysql索引,看到一个很少比如:索引就好比一本书的目录,它会让你更快的找到内容,显然目录(索引)并不是越多越好,假如这本书1000页,有500也是目录,它当然效率低,目录是要 ...

  7. MySQL 普通索引、唯一索引和主索引

    1.普通索引 普通索引(由关键字KEY或INDEX定义的索引)的唯一任务是加快对数据的访问速度.因此,应该只为那些最经常出现在查询条件(WHEREcolumn=)或排序条件(ORDERBYcolumn ...

  8. Mysql主键索引、唯一索引、普通索引、全文索引、组合索引的区别

    原文:Mysql主键索引.唯一索引.普通索引.全文索引.组合索引的区别 Mysql索引概念: 说说Mysql索引,看到一个很少比如:索引就好比一本书的目录,它会让你更快的找到内容,显然目录(索引)并不 ...

  9. 如何选择普通索引和唯一索引《死磕MySQL系列 五》

    系列文章 一.原来一条select语句在MySQL是这样执行的<死磕MySQL系列 一> 二.一生挚友redo log.binlog<死磕MySQL系列 二> 三.MySQL强 ...

随机推荐

  1. Python函数式编程之装饰器

    原则:对修改是封闭的,对扩展是开放的,方法:一般不修改函数或者类,而是扩展函数或者类 一:装饰器 允许我们将一个提供核心功能的对象和其他可以改变这个功能的对象’包裹‘在一起, 使用装饰对象的任何对象与 ...

  2. mysql 删除指定字符

    mysql 删除指定字符 1.1 前言        实际需求中如果想删除指定的字符,一般需要使用到trim函数.trim函数默认删除字符的前后空格,如果想指定删除特定字符,则需要使用一下语句进行声明 ...

  3. 浅析Diffie–Hellman

    一.作者 这个密钥交换方法,由惠特菲尔德·迪菲(Bailey Whitfield Diffie).马丁·赫尔曼(Martin Edward Hellman)于1976年发表. 二.说明 它是一种安全协 ...

  4. [Swift]LeetCode145. 二叉树的后序遍历 | Binary Tree Postorder Traversal

    Given a binary tree, return the postorder traversal of its nodes' values. Example: Input: [1,null,2, ...

  5. [Swift]LeetCode468. 验证IP地址 | Validate IP Address

    Write a function to check whether an input string is a valid IPv4 address or IPv6 address or neither ...

  6. [Swift]LeetCode793. 阶乘函数后K个零 | Preimage Size of Factorial Zeroes Function

    Let f(x) be the number of zeroes at the end of x!. (Recall that x! = 1 * 2 * 3 * ... * x, and by con ...

  7. [Swift]LeetCode901. 股票价格跨度 | Online Stock Span

    Write a class StockSpanner which collects daily price quotes for some stock, and returns the span of ...

  8. [Swift]LeetCode978. 最长湍流子数组 | Longest Turbulent Subarray

    A subarray A[i], A[i+1], ..., A[j] of A is said to be turbulent if and only if: For i <= k < j ...

  9. Python面试真题第三节

    51.正则匹配,匹配日期2018-03-20 url='https://sycm.taobao.com/bda/tradinganaly/overview/get_summary.json?dateR ...

  10. Kubernetes 笔记 04 架构是个好东西

    本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫. Hi,大家好, ...