你真的懂数据库的索引吗(下篇)

===============================================

普通索引和唯一索引的选择

查询过程

SELECT id FROM T WHERE A=1;对于上述这条select语句,首先从B+树的树根开始,按层搜索到叶子节点,然后再根据二分法来定位记录。

  • 对于普通索引来说,查到满足条件的记录后,还会继续查找下一个记录,直到碰到第一个不满足条件的记录。
  • 对于唯一索引来说,由于索引具有唯一性,查找到第一个满足条件的记录后,就会停止继续检索。

只能说两者的查询效率差别不大,InnoDB的数据是按照页为单位来读写的,当读到一条记录的时候,并不是把这个数据从磁盘读出来,而是以页为单位,读到内存中,对于在内存中多检索几条记录,消耗很低。但是有一种特殊情况,就是这个检索的key刚好位于数据页的最后一个数,所以读取下一个记录的时候,需要把另一个数据页加载进内存。

更新过程

这里需要先引入一个概念:change buffer。当更新一个数据页的时候,如果数据在内存中就会直接更新,如果数据不在内存中的话,在不影响一致性的前提下,InnoDB会把这些更新操作缓存在change buffer中,这样就不需要从磁盘中读入数据页。当这个数据页被读进内存的时候,这个时候就会执行change buffer中与这个页有关的操作,通过这种方式来保证这个数据逻辑的准确性。
change buffer的数据和数据页数据合并的过程叫做merge,系统的后台线程会定期把数据merge到磁盘中的数据页,在数据库关闭的时候,也会进行merge操作。

  • 对于唯一索引来说,更新操作需要判断这个操作是否满足唯一性约束,所以需要把整个数据页读入到内存中才能判断,而数据页加载到内存中,就没必要使用change buffer,直接更新还来得快一些。
    实际上change buffer也只有普通索引能用。

了解完这个change buffer的概念,接下来看看两种索引更新的过程
数据页在内存中的情况

  • 唯一索引,直接找到对应位置,判断有无冲突,并插入值,语句执行结束
  • 普通索引,找到对应位置,插入值,语句结束

数据页不在内存中的情况

  • 唯一索引,需要把数据页加载进内存中,判断有无冲突,插入值,语句结束
  • 普通索引,把数据记录到change buffer,语句结束

如何选择这两种索引

普通索引和唯一索引在查询上区别不大,主要区别在于更新操作。普通索引具有chang buffer,而唯一索引没有。对于写多读少的场景,change buffer能够极大的提高效率。所以考虑业务场景,一般来说选择普通索引的可能性比较大

关于change buffer 一开始是写内存但是机器突然故障重启,这时候的情况分析:
change buffer有一部分数据在内存中,有一部分数据在ibdata中,做清除的时候,会把change buffer里面的数据持久化到ibdata中。
redo log日志记录了数据页的修改和change buffer中写入的消息。
如果这个时候故障,分情况来分析:
1、change buffer里面的数据已经持久化到磁盘中,这个时候就不用管了
2、没持久化到磁盘中,这个时候就分为以下几种情况

  • change buffer写入了,redo log已经fsync但是没有commit,binlog没有fsync到磁盘,这部分数据就会出现丢失
  • change buffer写入,redo log写入但是没有commit,binlog已经fsync到磁盘,这个时候从binlog恢复redo log,再从redo log回复change buffer
  • change buffer写入,redo log和binlog都已经fsync,那么直接从redo log恢复

MySQL对索引的选择

一张数据库表可能有多个索引,但是你写SQL语句的时候,并没有主动指定使用哪个索引,说明这个索引是MySQL来确定的。
选择索引是优化器的工作,找到最优执行方案。并用最小代价去执行语句。优化器的判断标准由临时表、是否排序,扫描行数等因素决定的。
这里讲讲MySQL是怎么判断扫描行数的
在还没执行语句之前,MySQL会根据统计信息来估计记录数,这个统计信息其实就是索引的区分度
那何为索引的区分度呢?
一个索引上不同值个数被称之为基数,这个基数越大,索引的区分度就越好。
那这里又有一个问题了,就是MySQL怎么得到索引的基数呢?
这里采用的是采样统计的方式,InnoDB默认选择N个数据页,统计页面上的不同值,得到一个平均值,将这个平均值乘索引的页面数,就可以得到其基数。数据表会不断更新,索引统计信息也不是一层不变,当变更的数据行达到一定量的时候,就会重新触发一次索引统计。因为采用的是采样统计,所以这个数据是不准确的,只是一个大概的值。
当然这个基数只是优化器的一个考虑因素,普通索引的话会有一个回表操作,这个性能消耗优化器也是会考虑在内的。当然优化器的选择不一定是正确的,但是大部分情况还是能够做出正确选择的

索引选择异常和处理

  • 方法一:采用force index强行选择一个索引
  • 方法二:修改SQL语句,引导MySQL使用我们期待的索引
  • 方法三:新建更为合适的索引,来给优化器做选择,或者删除误用的索引

给字符串加索引

  1. 直接创建完整索引,这样比较占用空间
  2. 创建前缀索引,节省空间,但是覆盖索引将失效
  3. 倒序存储,再创建索引,可以绕开正序字符串区分度不够的情况
  4. 创建hash字段索引。

注:3、4点都不支持范围扫描

你真的懂数据库的索引吗(上篇)

猜你喜欢

转载自blog.csdn.net/weixin_42683077/article/details/106959302