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


在MySQL索引你真的会用吗?这篇文章中我们已经讲解过MySQL中有哪些索引,并且讲解了该怎么去创建索引,今天我们就来讲一讲MySQL中普通索引和唯一索引的区别,相信小伙伴们在工作中都使用过这两种索引类型,那这两种索引到底有什么不一样呢?我们从CRUD这几个方面来逐一讲解,有问题欢迎大家讨论指正!

查询

  • 普通索引: 查找到满足条件的第一个记录后,需要检索下一个记录,直到碰到第一个不满足条件的记录(由于B+数的叶子节点的数据都是顺序存放的)
  • 唯一索引: 只要查找到满足条件的第一个记录后就停止检索

buffer pool

这两种查询方式的性能差距微乎其微,原因是InnoDB底层是以页才存储数据,每一页都是16KB,在读取数据的时候不是只是将符合条件的记录读取出来,而是将记录所在的整页数据都读取出来放到内存中,这个内存就是buffer pool, 这样当读取到满足条件的记录后,就不需要再去读磁盘了,只需要有一次内存指针寻址和计算,但是当读取到的第一条记录正好是这页数据的最后一条,当取下一条数据的时候就需要去磁盘中读一次,相对来说麻烦一些,但是这种情况出现的概率极低,可以忽略不计。

更新

唯一索引:
判断当前更新是否违反唯一性约束,也就是判断更新的值是否已经存在了,那就要把数据从磁盘读到内存中进行比较;
普通索引:
判断当前记录是否存在于buffer pool中,
如果在buffer pool中就直接更新内存数据,后台定时将更新同步到磁盘中;
如果不在buffer pool中就需要将数据从磁盘中读取出来放到内存中,更新内存,后台定时将更新同步到磁盘中;

插入

唯一索引:
与更新类似,也需要将数据从磁盘读取到内存中,判断新插入的值是否已经存在了,如果不存在就插入值(随机写磁盘),语句执行结束
普通索引:
找到位置直接插入值(随机写),语句执行结束

删除

唯一索引和普通索引一样,都需要找到对应的磁盘位置,然后删除(随机读、随机写


可以看到在“更新”和“插入”逻辑中,唯一索引为了保证唯一性都不可避免的需要将数据从磁盘中读到内存中进行判断,而普通索引则不需要进行唯一性判断,但是也需要将数据读取到内存中,都涉及了“随机读”;
插入操作都涉及了写磁盘操作;“删除”操作涉及了“随机读”和“随机写”。
众所周知“随机读”和“随机写”对性能的影响是很大的,因此InnoDB设计了ChangeBuffer,用来避免在执行SQL时的“随机读”和“随机写”。

ChangeBuffer

只对普通索引有效,因为唯一索引都需要将数据读取到内存中进行唯一性判断(除了删除操作),Change buffer属于buffer pool。

执行流程

  • 更新操作,changeBuffer是用于减少SQL执行过程中对磁盘的随机读带来的性能开销,当执行更新时,如果buffer pool中没有需要修改的记录(如果内存中已经有了,就直接更新内存中的数据了),就将本次修改记录到Change buffer中
    • 执行完更新后,在下次查询的时候,如果需要访问这个数据页,会将数据页读入内存中,然后执行数据合并操作,称为merge,merge完之后就会产生“脏页”,之后通过刷脏来将脏页的数据持久化到磁盘。
  • 插入操作,用于减少在SQL执行时对磁盘的随机写带来的性能开销,当执行插入时,只需要将本次插入的数据存入ChangeBuffer中,不需要立刻写磁盘
    • 执行完插入后,如果内存中数据都还在,查询逻辑与更新操作类似,也会执行merge操作
  • 删除操作,同样是用于减少在SQL执行时对磁盘的随机写带来的性能开销,当执行删除时,只需要将本次删除的数据存入ChangeBuffer中
    • 执行完插入后,如果内存中数据都还在,查询时也会执行merge操作

适用场景

上边讲到了ChangeBuffer的“被动”merge时机,当ChangeBuffer中的数据所在页涉及到查询时,就会被merge,假如说我们的业务是**“写多读少”**,那非常适合适用ChangeBuffer;如果我们的业务是写完立刻读的场景多,那ChangeBuffer就没有什么意义了,随机读写的次数不会减少,还增加了维护Change buffer的代价。

持久化

ChangeBuffer的持久化是通过Redo log来实现的,下边会进行讲解

与Redo log的区别

先来看执行插入的完整流程:

  1. 先判断插入数据所在页是否在内存中
  2. 没有在内存中就在ChangeBuffer区域中记录下这个插入信息
  3. 在内存中就直接更新内容
  4. 将上述操作写入redo log(更新内存与写ChangeBuffer存储格式不同)
  5. 将redo log更新顺序写入磁盘

redo日志有分几十种类型的。
redo做的事情,简单讲就是记录页的变化(WAL将页变化的乱序写转换成了顺序写)。页是分多种的,比如 B+树索引页(主键 / 二级索引)、undo页(数据的多版本MVCC)、以及现在的change buffer页等等,这些页被redo记录后,就可以不着急刷盘了。change buffer记录索引页的变化;但是change buffer本身也是要持久化的,而它持久化的工作和其他页一样,交给了redo日志来帮忙完成;redo日志记录的是change buffer页的变化。
change buffer持久化文件是 ibdata1,索引页持久化文件是 t.ibd,所以Redo log也可以用来恢复change buffer。

这样changebuffer与redo log的区别就显而易见了:

  • 职责上就进行了很明确的划分,changebuffer只保存数据更新信息,而redo log负责记录很多更新,包括数据更新和changebuffer更新,同时也提供crash safe能力;
  • 从性能优化角度考虑,changebuffer避免了随机读带来的性能消耗,redo log则是避免了随机**(同步随机写改为了异步随机写)**写带来的性能消耗



你可能感兴趣的:(MySQL,mysql,数据库,索引)