MySQL深入07-普通索引和唯一索引
# MySQL深入07-普通索引和唯一索引
普通索引:就是在创建索引时,不附加任何限制条件(唯一、非空等限制)。该类型的索引可以创建在任何数据类型的字段上。
唯一索引:就是在创建索引时,限制索引的值必须是唯一的。通过该类型的索引可以更快速地查询某条记录。
# 两种索引查询过程深入
两者在查询语句上的差异:
- 普通索引查找到第一个满足条件的记录后,继续向后遍历,直到第一个不满足条件的记录 。
- 唯一索引由于索引定义了唯一性,查找到第一个满足条件的记录后,直接停止继续检索。
那么这两者在查询过程中性能差距有多少呢?
InnoDB 的数据是按数据页为单位来读写的。也就是说,当需要读一条记录的时候,并不是将这个记录本身从磁盘读出来,而是以页为单位,将其整体读入内存。在 InnoDB 中,每个数据页的大小默认是 16KB。
由于引擎是按页读写的,那么查找到的记录所在的数据页是在内存中的。普通索引需要额外做的事情,就是找到下一个不满足条件的记录,而一般来说下一个不满足条件的记录也是在该数据页中。
如果这个记录刚好是这个数据页的最后一个记录,那么要取下一个记录,必须读取下一个数据页,这个操作会稍微复杂一些。
但是这个情况出现概率很低,所以在计算平均性能上,两者差异几乎微乎其微。
# 两种索引更新过程深入
在对两种索引在更新过程中性能差异探讨前,先了解一下change buffer
。
当需要更新一个数据页时,如果数据页在内存中就直接更新,如果数据页还没有在内存中,在不影响数据一致性的前提下,InnoDB 会将这些更新操作缓存在 change buffer 中,这样就不需要从磁盘中读入这个数据页了。在下次查询需要访问这个数据页的时候,将数据页读入内存,然后执行 change buffer 中与这个页有关的操作。通过这种方式就能保证这个数据逻辑的正确性。
change buffer实际上是可以持久化的数据,它在内存中有拷贝,也会被写入到磁盘上。
显然如果更新操作能先记录在change buffer,减少磁盘读写,语句的执行速度就会明显的提升。通过这种方式能够避免占用内存,提高内存利用率。
下面我们来分析一下两者在更新语句的差异:
1、如果更新的目标页在内存中:
- 对于唯一索引来说,找到对应的位置,判断到没有冲突,插入这个值,语句执行结束;
- 对于普通索引来说,找到对应的位置,插入这个值,语句执行结束。
如果更新的目标页在内存中,比较下来,唯一索引只是多了一个判断,只会耗费微小的CPU时间。
2、如果更新的目标页不在内存中:
- 对于唯一索引来说,需要将数据页读入内存,判断到没有冲突,插入这个值,语句执行结束;
- 对于普通索引来说,则是将更新记录在 change buffer,语句执行就结束了。
将数据从磁盘读入内存涉及随机 IO 的访问,是数据库里面成本最高的操作之一。change buffer 因为减少了随机磁盘访问,所以对更新性能的提升是会很明显的。
为什么唯一索引不使用change buffer
对于唯一索引来说,所有的更新操作之前都会进行一次唯一性的判断,而这个判断就需要把数据页读入内存,既然数据页已经读入内存了,那么直接更新内存就行了,就不需要使用change buffer。
# Change Buffer的使用场景
change buffer对普通索引在更新过程的加速作用是通过把更新语句进行了缓存,减少了磁盘的读取。那么对于普通索引的所有场景,使用change buffer都能起到加速的作用吗?
将 change buffer 中的操作应用到原数据页,得到最新结果的过程称为 merge。除了访问这个数据页会触发 merge 外,系统有后台线程会定期 merge。在数据库正常关闭(shutdown)的过程中,也会执行 merge 操作。
所以merge的时候才是数据真正更新的时刻,而change buffer是将记录的变更操作缓存下来,如果在merge之前,change buffer记录的变更越多,那么change buffer的收益就越大。
现在有两个业务场景:
1、对于写多读少的业务,比如日志之类的。页面在更新之后查询的概率比较小,此时change buffer这张草稿纸就能得到充分利用。
2、对于写少读多的业务,如果change buffer这张草稿纸还没有记录几条更新操作,就会因为马上要访问这个数据页导致触发merge过程。这样随机访问 IO 的次数不会减少,反而增加了 change buffer 的维护代价。所以,对于这种业务模式来说,change buffer 反而起到了副作用。
# 索引选择小结
- 如果是查询,普通索引和唯一索引几乎没有区别
- 如果是更新,尽量选择普通索引
- 如果是读少写多,就使用change buffer。
- 如果是读多写少,就关闭change buffer。