「这是我参与11月更文挑战的第19天,活动详情查看:2021最后一次更文挑战」
在进行数据查询的时候,为了加快数据的查询,我们都会在表中加上索引,索引除了主键索引外,又分为普通索引和唯一索引,如果有这样的一个场景:用户注册账号信息,一个手机号码只能够注册一个账号,在我们进行登录验证的时候,会采用手机号码进行查询,就需要执行下面这样的SQL:
SELECT * FROM t_user WHERE phone ='xxxxx';
通过手机号码查询,业务中存在这样的SQL,那么就肯定会考虑给电话号码字段加索引,很多人想必会考虑加唯一索引,因为电话号码在业务上必须保证唯一,加唯一索引既能够起到索引的位置,也能够保证我们存入的电话号码具有唯一性。 那么如果我们能够从业务上保证电话号码插入数据的唯一性,那么唯一索引的查询与普通索引的性能哪个会快一些呢?如果没法从业务上没法保证电话号码数据插入的唯一性,那么接下来的内容不用看了,直接考虑唯一索引吧,如果是我,都只会考虑唯一索引,这样可以保证数据的唯一性,至少业务数据不会错。分析两者的性能,接下来从两个方面分析:查询性能、更新性能。
查询性能
大家都知道MySQL数据库会根据索引字段建立B+树索引,从顶部开始依次往下查找,依次查到满足条件为止
- 普通索引:查询到第一个符合条件的数据,不会马上终止查询,会继续往下查询,指导查询到不符合条件的数据才会终止
- 唯一索引:查询到第一个符合条件的数据后,会马上终止查询,直接返回查询到的结果,因为是唯一索引,符合条件的值只有一个
从上面的分析来看,唯一索引的查询性能确实比普通性能好一些,唯一索引只需要查询一个值后,就不需要继续往下查找,而普通索引为继续查找下一个数据。但是MySQL的索引数据都是按照页为单位来读写的,每页为16KB大小,会存很多个电话号码,对于普通索引来说,当查询到满足条件的第一个数据后,会有两种情况发生:
- 查找到的数据,不是最后一个数据,那么就直接从数据页中查询后面一个,因为数据页都在内存中,那么这种情况查询耗费的时间几乎忽略不计;
- 查询到的数据是最后一个数据 ,那么判断下一个数据的时候,需要从读取数据页数据到内存中来才能够查询,如果这个数据页在内存中,就不需要操作磁盘从磁盘中读取,运气不好的话,就需要从磁盘中将下一个数据页读取到内存中,但是这种情况概率比较低;
综合上面的分析:其实两者在查询的性能方面几乎都差不多,时间损耗可以忽略不计。
更新性能
为了提高更新的性能,在做更新的时候,当更新的数据如果数据页在内存中,那么数据库就直接在内存中更新数据,那在这种情况下,普通索引和唯一索引的更新性能是一样的。 当数据的数据页不在内存的时候,InnoDB会先将更新操作的记录缓存到change buffer中,这样做的目的是:如果数据不在内存中,就不需要去读取数据到内存中,只需要将数据更新操作记录到change buffer中,这样,可以快速响应用户的情况,后台会有定时任务线程将数据merge到磁盘中去,但是如果更新完成后,需要马上查询更新的数据,那这个时候就会立即merge更新内容到磁盘中。 但是有了唯一索引,更新和查询操作都采用上面那种情况的话,无法保证数据的唯一性,如果更新的电话号码已经在数据库存在,那么就违反了数据的唯一性,索引在数据更新的时候,会先将从数据库中查询一下判断记录是否存在数据库中,如果已经存在,那就不允许更新,违法唯一性约束。所以唯一索引在更新及插入的时候会比普通索引多一次查询操作。