普通索引和唯一索引在查询能力上没啥差别,主要考虑对更新性能的影响,要尽量选择普通索引。接下来分析两种索引在查询语句和更新语句对性能的影响。
MySQL InnoDB 是采用 B+ 树实现其索引结构。

image
如上图所示:现在需要查找 29 这个值。

image
假设用执行这么一个查询语句:
select id from T where k=5
现在索引树上查找,然后通过 B+ 树从树根开始,按层搜索到叶子节点,然后从数据页内部通过二分查找中定位记录。

image
change buffer 是一个特殊的数据结构,当二级索引的页面不在缓冲池中,change buffer 会缓存对二级索引的数据操作(update, insert, delete).主要减少磁盘的随机 I/O。
唯一索引不会使用 Change buffer ,如果索引设置了唯一属性,在进行插入或者修改操作时,InnoDB 必须进行唯一性检查,如果不读取索引页到缓冲池,无法校验索引是否唯一,但是可以进行缓冲删除操作。
当需要更新一个数据页时,如果数据页在内存中就直接更新,如果这个数据页还没有内存中的话,在不影响数据一致性的前提下,InnoDB 会将这些操作缓存在 change buffer 中,这样就不需要从磁盘读入数据页,下次查询需要访问这个数据页时,将数据页读入内存,然后执行 change buffer 中与这个页的操作。通过这种方式保证这个数据逻辑的正确性。
change buffer 实际上是持久化的数据,change buffer 在内存中有拷贝,也会被写入到磁盘上。将 change buffer 中的操作应用到原数据页,得到最新结果过程称为 merge,除了访问页这个数据页会触发 merge 操作,系统后台线程会定期 merge ,在数据库正常关闭过程中,也会执行 merge。
show variables like '%innodb_change_buffer_max_size%';

image
show variables like '%innodb_change_buffering%';

image
如果能够将更新操作先记录 change buffer ,减少读磁盘,语句的执行速度会得到明显提升,而且数据读入内存了需要 buffer pool ,所以使用这种方式能够避免占用内存,提高内存利用率。普通索引能够使用 change buffer ,但是唯一索引不行,因此 普通索引比唯一索引更新操作快。
redo log 主要节省的是随机写磁盘的 IO 消耗(转成顺序写),而 change buffer 主要节省的是随机读磁盘的IO消耗。
快速回忆一遍 redo log
redo Log 是 InnoDB 引擎特有的日志。如果每一次更新操作都需要写进磁盘,然后磁盘要找到那条记录,然后再更新,整个过程 IO 成本很高,查找成本很高。MySQL 采用了什么方式提高更新效率呢?
MySQL 采用 WAL 技术,Write Ahead Loging,关键点是先写日志再写磁盘,具体执行如下:当有一条记录需要更新的时候,InnoDB 引擎会先把记录写到 redo log里,并更新内存,这个时候更新就算完事了。当 InnoDB 引擎会在适当的时候,将这个操作记录更新到磁盘里面,这个更新一般是在空闲的时候做。