普通索引和唯一索引,应该怎么选择?
如果业务能保证唯一性的情况下,还是选择普通索引性能更好
select id from T where k=5
首先,我们看下
查询过程
对于普通索引来说,查询到满足条件的第一个记录后,需要查找下一个记录,直到碰到第一个不满足k=5条件的记录
对于唯一索引来说,由于索引上有唯一性,查询到第一个满足条件的记录后就停止检索了
所以在这里的区别就是普通索引会多查那么一下,那么这两种的性能差别有多大呢?
答案是微乎其微,为甚呢?
因为mysql在读数据的时候,比如说上面那条语句,如果没有在内存中,会去磁盘中把k=5这条数据所在的整个数据页都读入内存中,在innodb中,每个数据页的大小默认是16KB,因为引擎是按页读写的,所以说,当找到k=5的记录的时候,它所在的数据页都在内存里了,那么对于普通索引来说,要多做的那一次查询的操作,就只需要一次指针寻找和一次计算,当然,如果K=5这个记录刚好是这个数据页的最后一个记录,那么要取下一个记录,必须读取下一个数据页,这个操作会稍微复杂一些,但是对于整形字段,一个数据页可以放近千个key,因此出现这种情况的概率会很低,所以,我们计算平均性能差异时,扔可以认为这个操作成本对于现在的cpu来说可以忽略不计。
更新过程
首先我们说下change_buffer的概念,如果要更新的数据在内存中,那么就直接更新内存,如果要更新的数据没有在内存中,那么就会把更新记录存在change_buffer中,等到有查询来读取数据页的时候,就会执行change_buffe中有关这个数据页的操作。通过这种方式就能保证这个数据逻辑的正确性。
change_buffer也会持久化到磁盘,将change_buffer中的操作应用到原数据页,得到最新结果的过程称为merge,除了访问这个数据页会触发merge外,系统有后台线程会定期merge,在数据库正常关闭的过程中,也会执行merge
显然如果能够将更新操作记录在change_buffer,减少读磁盘,语句的执行速度会得到明显的提升。而且,数据读入内存是需要占用buffer pool的,所以这种方式还能够避免占用内存,提高内存利用率
那么什么条件下可以使用changebuffer呢?
对于唯一索引来说,每次更新都必须先判断这个操作是否违反唯一性约束,比如,要插入(4,400)这个记录,就要先判断现在表中是否存在这个记录,而这必须要将数据页读入内存才能判断,既然都读入内存了,那么直接更新内存会更快,就没必要使用change buffer了。
因此,唯一索引的更新就不能使用change buffer
change buffer用的是buffer pool里的内存,因为不能无限增大,change buffer的大小,可以通过参数innodb_change_buffer_max_size来动态设置,这个参数设置为50的时候,表示change buffer的大小最多只能占用buffer pool的50%
现在我们看下如果要在这张表中插入一个新记录(4,400)的话,innodb的处理流程是什么样的
第一种情况是,这个记录要更新的目标页在内存中,这时,innodb的处理流程如下:
- 对于唯一索引来说,找到3和5之间的位置,判断到没有冲突,插入这个值,语句执行结束
- 对于普通索引来说,找到3和5的位置,插入这个值,语句执行结束
这样看来,普通索引和唯一索引对更新语句性能影响的差别,只是一个判断,只会耗费微小的CPU时间。
但,这不是我们关注的重点
第二中情况是,这个记录要更新的目标页不在内存中,这时,流程如下:
- 对于唯一索引来说,由于需要判断唯一性,所以要从磁盘中读取所在的数据页到内存中,判断到没有冲突,插入值,结束
- 对于普通索引来说,则是将更新记录在change buffer,结束
将数据从磁盘读入内存涉及随机io的访问,是数据库里面成本最高的操作之一。changebuffer因为减少了随机磁盘访问,所以对更新性能的提升是会很明显的
但是change buffer的应用场景只是对于写多读少的的业务,页面在写完以后马上被访问的概率比较小,此时changebuffer的使用效果最好,这种业务模型常见的就是账单类,日志类的系统。
反过来,如果一个业务的更新模式是写完之后马上会做查询,那么即使满足了条件,将更新记录在change buffer,但之后由于马上要访问这个数据页,会立即触发Merge过程,这样随机访问io的次数不会减少,反而增加了change buffer的维护代价。所以,对于这种业务模式来说,change buffer反而起到了反作用。
所以,如果所有的更新后面,都马上伴随着对这个记录的查询,那么你应该关闭change buffer,而在其他情况下,change buffer都能提升更新性能。
小结
如果业务能保证一个字段的唯一性并且针对的是写多多少的业务模式,那么还是选择普通索引的好。