什么时候使用唯一索引和普通索引
Posted jimmyhe
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了什么时候使用唯一索引和普通索引相关的知识,希望对你有一定的参考价值。
唯一或普通索引的选择
业务需求
假设你在维护一个市民系统,每个人都有一个唯一的身份证号,而且业务代码已经保证了不会写入两个重复的身份证号。如果市民系统需要按照身份证号查姓名,就会执行类似这样的SQL语句:
select name from CUser where id_card = 'xxxxxxxyyyyyyzzzzz';
在不考虑身份证好字段大小的情况下,需要给id_card建立索引,是选择普通索引还是唯一索引呢?
查询过程
- 比如 select id from T where k=5
- 在B+树的树根开始,按层搜索找到叶子节点,先找到数据页,在根据二分法,在数据页中定位记录。
- 对于普通索引,查找到满足条件的第一个记录(5,500)后,需要查找下一个记录,直到碰到第一个不满足k=5条件的记录。
- 对于唯一索引来说,由于索引定义了唯一性,查找到第一个满足条件的记录后,就会停止继续检索。
- 引擎是按页读取的,对于普通索引来说,只是多做了一次查找和判断下一条记录的操作,如果该记录刚好是最后一条就会复杂一些,但是一个数据页可以存放近千个key,这种概率也很低,所以我们可以认为差距很小。
更新过程
- change buffer概念
- 当需要更新一个数据页时,如果数据页在内存中就直接更新,而如果这个数据页还没有在内存中的话,在不影响数据一致性的前提下,InooDB会将这些更新操作缓存在change buffer中,这样就不需要从磁盘中读入这个数据页了。。在下次查询需要访问这个数据页的时候,将数据页读入内存,然后执行change buffer中与这个页有关的操作。通过这种方式就能保证这个数据逻辑的正确性。
- 需要说明的是,虽然名字叫作change buffer,实际上它是可以持久化的数据。也就是说,change buffer在内存中有拷贝,也会被写入到磁盘上。
- 将change buffer中的操作应用到原数据页,得到最新结果的过程称为merge。除了访问这个数据页会触发merge外,系统有后台线程会定期merge。在数据库正常关闭(shutdown)的过程中,也会执行merge操作。
- 显然,如果能够将更新操作先记录在change buffer,减少读磁盘,语句的执行速度会得到明显的提升。而且,数据读入内存是需要占用buffer pool的,所以这种方式还能够避免占用内存,提高内存利用率。
- 使用change buffer的条件
- 对于唯一索引来说,所有的更新操作都要先判断这个操作是否违反唯一性约束。比如,要插入(4,400)这个记录,就要先判断现在表中是否已经存在k=4的记录,而这必须要将数据页读入内存才能判断。如果都已经读入到内存了,那直接更新内存会更快,就没必要使用change buffer了。
- 因此,唯一索引的更新就不能使用change buffer,实际上也只有普通索引可以使用。
- 当要更新的记录目标页不再内存中,如果是普通索引就会将更新记录在buffer上,语句执行结束,减少了磁盘读入内存的io访问,性能提升。
- change buffer的使用场景
- 对于写多读少的业务,使用change buffer的效果比较好,比如账单,日志类的系统。
- 对于读多写少的话,由于马上访问数据页,需要将buffer中的数据更新到数据页,会触发merge过程,使得io次数不会减少,增加了change buffer的维护代价,反而起了副作用。
- change buffer概念
change buffer 和 redo log的区别
- 例子:mysql> insert into t(id,k) values(id1,k1),(id2,k2);
- 这里,我们假设 当前k索引树的状态,查找到位置后,k1所在的数据页在内存(InnoDB bufferpool)中,k2所在的数据页不在内存中
唯一或普通索引的选择
业务需求
假设你在维护一个市民系统,每个人都有一个唯一的身份证号,而且业务代码已经保证了不会写入两个重复的身份证号。如果市民系统需要按照身份证号查姓名,就会执行类似这样的SQL语句:
select name from CUser where id_card = 'xxxxxxxyyyyyyzzzzz';
在不考虑身份证好字段大小的情况下,需要给id_card建立索引,是选择普通索引还是唯一索引呢?
查询过程
- 比如 select id from T where k=5
- 在B+树的树根开始,按层搜索找到叶子节点,先找到数据页,在根据二分法,在数据页中定位记录。
- 对于普通索引,查找到满足条件的第一个记录(5,500)后,需要查找下一个记录,直到碰到第一个不满足k=5条件的记录。
- 对于唯一索引来说,由于索引定义了唯一性,查找到第一个满足条件的记录后,就会停止继续检索。
- 引擎是按页读取的,对于普通索引来说,只是多做了一次查找和判断下一条记录的操作,如果该记录刚好是最后一条就会复杂一些,但是一个数据页可以存放近千个key,这种概率也很低,所以我们可以认为差距很小。
更新过程
- change buffer概念
- 当需要更新一个数据页时,如果数据页在内存中就直接更新,而如果这个数据页还没有在内存中的话,在不影响数据一致性的前提下,InooDB会将这些更新操作缓存在change buffer中,这样就不需要从磁盘中读入这个数据页了。。在下次查询需要访问这个数据页的时候,将数据页读入内存,然后执行change buffer中与这个页有关的操作。通过这种方式就能保证这个数据逻辑的正确性。
- 需要说明的是,虽然名字叫作change buffer,实际上它是可以持久化的数据。也就是说,change buffer在内存中有拷贝,也会被写入到磁盘上。
- 将change buffer中的操作应用到原数据页,得到最新结果的过程称为merge。除了访问这个数据页会触发merge外,系统有后台线程会定期merge。在数据库正常关闭(shutdown)的过程中,也会执行merge操作。
- 显然,如果能够将更新操作先记录在change buffer,减少读磁盘,语句的执行速度会得到明显的提升。而且,数据读入内存是需要占用buffer pool的,所以这种方式还能够避免占用内存,提高内存利用率。
- 使用change buffer的条件
- 对于唯一索引来说,所有的更新操作都要先判断这个操作是否违反唯一性约束。比如,要插入(4,400)这个记录,就要先判断现在表中是否已经存在k=4的记录,而这必须要将数据页读入内存才能判断。如果都已经读入到内存了,那直接更新内存会更快,就没必要使用change buffer了。
- 因此,唯一索引的更新就不能使用change buffer,实际上也只有普通索引可以使用。
- 当要更新的记录目标页不再内存中,如果是普通索引就会将更新记录在buffer上,语句执行结束,减少了磁盘读入内存的io访问,性能提升。
- change buffer的使用场景
- 对于写多读少的业务,使用change buffer的效果比较好,比如账单,日志类的系统。
- 对于读多写少的话,由于马上访问数据页,需要将buffer中的数据更新到数据页,会触发merge过程,使得io次数不会减少,增加了change buffer的维护代价,反而起了副作用。
- change buffer概念
change buffer 和 redo log的区别
- 例子:mysql> insert into t(id,k) values(id1,k1),(id2,k2);
- 这里,我们假设 当前k索引树的状态,查找到位置后,k1所在的数据页在内存(InnoDB bufferpool)中,k2所在的数据页不在内存中
这条语句操作如下
- Page 1在内存中,直接更新内存;
- Page 2没有在内存中,就在内存的change buffer区域,记录下“我要往Page 2插入一行”这个信息
- 将上述两个动作记入redo log中(图中3和4)。
- 接下来的读请求:
- 读Page 1的时候,直接从内存返回。WAL之后如果读数据,不要读盘,不用从redo log里面把数据更新以后才可以返回。虽然磁盘上还是之前的数据,但是这里直接从内存返回结果,结果是正确的。
- 要读Page 2的时候,需要把Page 2从磁盘读入内存中,然后应用change buffer里面的操作日志,生成一个正确的版本并返回结果。
- 可以看到,直到需要读Page 2的时候,这个数据页才会被读入内存。
所以,如果要简单地对比这两个机制在提升更新性能上的收益的话,redo log 主要节省的是随机写磁盘的IO消耗(转成顺序写),而change buffer主要节省的则是随机读磁盘的IO消耗。
以上是关于什么时候使用唯一索引和普通索引的主要内容,如果未能解决你的问题,请参考以下文章