流苏小筑

普通索引 vs 唯一索引

普通索引 vs 唯一索引

在不同的业务场景下,应该选择普通索引,还是唯一索引?

假设你在维护一个居民系统,每个人都有一个唯一的身份证号,而且业务代码已经保证了不会写入两个重复的身份证号。如果居民系统需要按照身份证号查姓名:

select name from CUser where id_card ='xxxxxxxyyyyyyzzzzz';

所以,你一定会考虑在id_card字段上建索引。

由于身份证号字段比较大,不建议把身份证号当做主键。现在有两个选择,要么给id_card字段创建唯一索引,要

么创建一个普通索引。如果业务代码已经保证了不会写入重复的身份证号,那么这两个选择逻辑上都是正确的。

从性能的角度考虑,你选择唯一索引还是普通索引呢?选择的依据是什么呢?

假设,我们有一个主键列为ID的表,表中有字段k,并且在k上有索引,假设字段 k 上的值都不重复。

这个表的建表语句是:

 create table test(
   id int primary key,
   k int not null,
   name varchar(16),
   index (k)
 )engine=InnoDB;

表中R1~R5的(ID,k)值分别为(100,1)、(200,2)、(300,3)、(500,5)和(600,6)。

1 查询过程

假设,执行查询的语句是 select id from test where k=5。

那么,这个不同带来的性能差距会有多少呢?答案是, 微乎其微 。

你知道的,noDB的数据是按数据页为单位来读写的。也就是说,当需要读一条记录的时候,并不是将这个记录

本身从磁盘读出来,而是以页为单位,将其整体读入内存。在noDB中,每个数据页的大小默认是16KB。因为引擎是按页读写的,所以说,当找到k=5的记录的时候,它所在的数据页就都在内存里了。那么,对于普通索引来说,要多做的那一次“查找和判断下一条记录”的操作就只需要一次指针寻找和一次计算

当然,如果k=5这个记录刚好是这个数据页的最后一个记录,那么要取下一个记录,必须读取下一个数据页,这个操作会稍微复杂一些。

但是,我们之前计算过,对于整型字段,一个数据页可以放近千个ky,因此出现这种情况的概率会很低。所以

我们计算平均性能差异时,仍可以认为这个操作成本对于现在的CPU来说可以忽略不计。

2 更新过程

为了说明普通索引唯一索引更新语句性能的影响这个问题,介绍一下change buffer。

当需要更新一个数据页时,如果数据页在内存中就直接更新,而如果这个数据页还没有在内存中的话, 在不影响数据一致性的前提下, InooDB会将这些更新操作缓存在change buffer中 ,这样就不需要从磁盘中读入这个数据页了。在下次查询需要访问这个数据页的时候,将数据页读入内存,然后执行change buffer中与这个页有关的操作。通过这种方式就能保证这个数据逻辑的正确性。 将change buffer中的操作应用到原数据页,得到最新结果的过程称为 merge 。除了 访问这个数据页 会 触 发merge外,系统有后台线程会定期 merge。在 数据库正常关闭(shutdown) 的过程中,也会执行 merge 操作。

如果能够将更新操作先记录在change buffer, 减少读磁盘 ,语句的执行速度会得到明显的提升。而 且, 数据读入内存是需要占用 buffer pool 的,所以这种方式还能够 避免占用内存 ,提高内存利用率。

那么,什么条件下可以使用change bufferl呢?

对于唯一索引来说,所有的更新操作都要先判断这个操作是否违反唯一性约束。比如,要插入(4,400这个记录
就要先判断现在表中是否已经存在k二4的记录,而这必须要将数据页读入内存才能判断。如果都已经读入到内存
了,那直接更新内存会更快,就没必要使用change buffer了。

因此,唯一索引的更新就不能使用change buffer,实际上也只有普通索引可以使用。

change buffer用的是buffer pool里的内存,因此不能无限增大。change buffer的大小,可以通过参数
innodb_change_buffer_max_size来动态设置。这个参数设置为58的时候,表示change buffer的大小最多只能占用ouffer pool的50%。

如果要在这张表中插入一个新记录(4,400)的话,nnoDB的处理流程是怎样的?

第一种情况是,这个记录要更新的目标页在内存中。这时:

第二种情况是,这个记录要更新的目标页不在内存中。这时:

当前页面是本站的「Google AMP」版。查看和发表评论请点击:完整版 »