一般情况下,这两种索引的性能并没有什么区别。
查询过程
搜索B+ 树,找到叶子节点,然后在节点内部的数据页使用二分法查找来定位。
- 普通索引,查找到满足条件的第一个记录,然后逐个往下查找,直到碰到第一个不满足的记录;
- 唯一索引,由于定义了唯一性,查找到第一个满足条件的就会停止。
由于都是读取了数据页,所以在性能上几乎没有区别。在InnoDB 中,每个数据页大小是16KB,所以,可以存放跟多个key ,查询时碰到分页的情况的概率会很低。
更新
change buffer
当需要更新一个数据页时,如果数据页在内存中就直接更新,而如果这个数据页还没有在内存中的话,在不影响数据一致性的前提下,InnoDB 会将这些更新操作缓存在 change buffer 中,这样就不需要从磁盘中读入这个数据页了。在下次查询需要访问这个数据页的时候,将数据页读入内存,然后执行 change buffer 中与这个页有关的操作。通过这种方式就能保证这个数据逻辑的正确性。
change buffer 也是可以持久化的。将 change buffer中的操作应用到原数据页,得到最新结果的过程称为 merge。除了访问数据页会触发 merge,系统的后台线程也会定期 merge。数据库正常关闭也会执行 merge。
在唯一索引的情况下,所有的更新都要先判断这个操作是否违反了唯一性约束,就像开发导入功能时的事先数据判断,就要多执行一次数据库的读取操作。而判断的时候必须把数据页读取到内存中,如果读取到了内存中,直接更新内存会更快,就没有必要使用 change buffer了。
因此,唯一索引的更新不能使用 change buffer,实际上也只有普通索引可以使用。
(change buffer 用的是 buffer pool 里的内存,因此不能无限增大。change buffer 的大小,可以通过参数 innodb_change_buffer_max_size 来动态设置。这个参数设置为 50 的时候,表示 change buffer 的大小最多只能占用 buffer pool 的 50%。)
更新操作对于两种索引来说,性能的区别极小,只是一个判断,只会耗费微小的 CPU 时间。
在要更新的目标页不在内存中时,唯一索引需要将数据页读入内存,判断有没有冲突,插入这个值,语句执行结束。
普通索引则是将更新 记录在 change buffer 中,语句执行结束。
将数据从磁盘读入内存涉及随机 IO 的访问,是数据库里面成本最高的操作之一。change buffer 因为减少了随机磁盘访问,所以对更新性能的提升是会很明显的。
change buffer 使用场景
但是如果在更新之后马上就要读取,就会立即触发 merge,这样随机 IO 的次数不会减少,反而影响效率。
所以如果每次更新之后都有查询语句,应该立即关闭 change buffer。
更新语句的过程
mysql> insert into t(id,k) values(id1,k1),(id2,k2);
更新过程涉及到四个部分,内存、redo log(ib_log_fileX)、数据表空间(t.ibd)、系统表空间(ibdata1)。
更新操作:
- Page 1 在内存中,直接更新内存;
- Page 2 不在内存中,就在内存中的change buffer 区域,记录下 “我要在 Page2 插入一行” 这个信息;
- 将上述两个动作记录到 redo log。
在这之后的读操作,page1 可以直接从内存返回 ,不需要从 redo log 里把数据更新之后返回。
读取page 2 时就要从磁盘读入内存,然后应用 change buffer 里的操作日志,生产一个正确的版本并返回结果。
redo log 主要节省的是随机写磁盘的 IO 消耗(转成顺序写), 而 change buffer 主要节省的则是随机读磁盘的 IO 消耗。
网友评论