在什么情况下该使用普通索引呢?什么情况下使用唯一索引呢?什么叫该使用哪种索引?指的是在确定查询语句后,在条件字段上应该建立哪种索引。
    假设有这么一张市民信息表,在这张表上有市民的姓名、身份证号等信息,现在我们要根据身份证号来查询姓名,那么就会写出类似下面的sql语句:

    1. select name from CUser where id_card = 'xxxxxxxyyyyyyzzzzz';

    假如我们会经常执行上面的sql语句,那么我们一定会在id_card字段上建立一个索引,那么我们会建立哪种索引呢?由于记录身份证号的字段占用空间比较大,如果在身份证号字段上建立主键索引,由于非主键索引的叶子节点中保存的是主键的值,所以非主键索引就会占用更大的空间,一般用哪种数据类型记录身份证号?一般用char(18)数据类型的字段来保存,18是因为身份证号是18位的,为什么用这种数据类型来保存呢?所以身份证号字段并不适合建立主键索引,那么我们在上面是建立唯一索引还是普通索引呢?
    什么样的字段上面才能建立唯一索引?这个字段的各个值都是不一样的才可以建立,那如果某个字段中存在重复值,在这个字段上建立唯一索引会怎么样呢?如果向一个建立了唯一索引的字段中插入了重复值又会发生什么呢?由于我们在业务代码中会保证插入到表中的新记录的身份证不会和表中已有的身份证号重复,也就是说保证了身份证号字段的各个值都是不同的,所以我们可以在身份证号字段上建立唯一索引,在所有的字段上都可以建立普通索引吗?什么是普通索引?普通索引和唯一索引的区别是什么?
    那么我们在身份证号字段上是建立唯一索引还是普通索引呢?既然都可以,那么建立哪种索引在更新数据和查询数据时的速度更快呢?
    首先分析建立两种索引后在执行查询语句时会经过哪些过程。
    假设我们执行的查询语句是select id from T where k=5,如果我们在k字段上建立的是普通索引,由于id字段是主键字段,所以并不会发生回表操作,只会在k字段对应的b+树上进行搜索,那么在b+树上的搜索过程是什么样的呢?首先会基于b+树的结构快速定位到第一个满足查询条件的数据行,然后一直读取下一个数据行,读取下一个数据行的依据是什么,是因为b+树中的叶子节点是有序的,而且使用链表相互连接,直到不满足查询条件,如果建立的是唯一索引,搜索数据的过程基本相同,区别只在于基于b+树的结构定位到第一个满足查询条件的数据行后,由于建立的是唯一索引,所以已知在表中不会再存在第二个k=5的数据行,从而直接返回查询到的数据行,而不需要继续读取下一个数据行并且判定其是否满足查询条件。
    所以在查询过程中普通索引和唯一索引的表现是基本上没有区别的。
    Innodb存储引擎在读取数据行和写入数据行时都是以数据页为单位的,当我们需要读取某个数据行时,并不是只将这一个数据行读取到内存中,而是将这个数据行所在数据页整个读取到内存中,一个数据页的默认大小是16kb,在b+树上搜索某条记录会将经过的数据页读取到内存中,最终所要查询的数据行所在的数据页自然也要读取到内存中,既然所要查询的数据行所在的数据页整个都被读取到内存中了,那么检查下一个数据行是否满足查询条件就完全是在内存中进行的,而且检查的次数通常不会很多,因为满足查询条件的数据行是有限的,遇到不满足查询条件的数据行就会停止了。
    即使定位到第一条数据行恰好是它所在数据页中的最后一个,我们也只需要多读取一个数据页,假设主键字段是bigint数据类型,那么一个16kb的数据页上可以存放1170个叶子节点,也就是说在b+树上定位到的第一条数据行恰好是它所在数据页的最后一个或者是最后几个的概率是很低的,这种情况在计算普通索引在查询过程中的额外耗时的期望时影响很小。
    那么在建立了普通索引的字段上更新数据会经过什么过程呢?在建立了普通索引的字段上更新数据会用到change buffer来加快更新过程,那么change buffer是怎么工作的呢?
    当我们更新一个数据行时,如果这个数据行就在内存中,那么就直接更新其中的数值,如果这个数据行不在内存中的话,Innodb会先把这个更新操作记录在change buffer中,从而避免从磁盘上读取数据行的耗时操作,当我们查询到这个数据行时,就必须要从磁盘上把这个数据行读取到内存中了,这时再把数据行读取到内存中,然后再在这个数据行上应用记录在change buffer中的操作,这样既可以保证更新数据时的速度,也能够保证数据的正确性。
    虽然这个模块的名字叫作change buffer,但是它并不只是定义在内存中的一个字节数组,在内存中记录更新操作后会写入到操作系统的缓存中,然后同步到磁盘上,所以记录的更新操作并不会因为掉电丢失。
    把change buffer中记录的更新操作应用到数据页上,这个过程被称为merge,那么什么时候会发生merge过程呢?当我们执行查询语句时会把一些数据页读取到内存中,如果在这些数据页中有change buffer中更新操作相关的,那么就会发生merge过程,而且在Mysql中也定义了后台线程来定期执行merge过程,在正常关闭Mysql时也会发生merge过程,怎么关闭Mysql叫作正常关闭Mysql?怎么关闭Mysql又叫不正常关闭Mysql呢?为什么我们要设置这么多发生merge过程的时机呢?因为我们想把merge过程尽可能的分散到多个时间段来执行,而不是挤在同一时刻来进行,从而会影响到Mysql执行语句的速度。
    更新操作还是更新语句的描述更准确?change buffer中是怎么记录的?
    所以说如果能够借助change buffer模块来更新数据,那么可以明显的加快语句执行的速度,那么在字段上建立的不管是普通索引或者唯一索引都可以借助change buffer模块来更新数据吗?并不是,如果在字段上建立的是唯一索引的话,那么在插入数据行时还需要保证字段的值的唯一性,那么怎么保证值的唯一性呢?就需要先判断在插入新的数据行时表中是否已经有将要插入的值存在,那么怎么判断表中有没有这个值呢?