Mysql - 索引分类相关

这里主要是主键索引,非主键索引,唯一索引,普通索引,联合索引等等。

  • 介绍一下索引的分类,以及他们的主要区别是什么?
  • 介绍一下什么是联合索引?什么样的情况下我们会使用联合索引?
  • 唯一索引了解吗?在使用的时候,有什么需要注意的不?
  • 我们有时候会听到索引下推,你知道什么是索引下推吗?那覆盖索引又是什么意思呢?


1. 介绍一下索引的分类,以及他们的主要区别是什么?

这里我主要分为主键索引非主键索引来讲。

在 InnoDB 中,主键索引也默认为聚簇索引,即主键值和行记录数据存储在一起。每个表都必须有个主键索引且只能有一个,用 PRIMARY KEY 关键字来声明。如果没有显示声明,则默认第一个非空且唯一的索引作为主键;如果没有符合这个条件的,InnoDB 会隐式生成一个隐藏主键。

主键索引具备非空且唯一的性质,因为其将主键值和数据放在一起,所以查询时不需要回表等操作,效率极高

除主键索引外,其他索引就是非主键索引,也可以视作辅助索引。这些索引是非聚簇索引,其中只存储索引值和对应的主键值,并没有具体的数据。

2. 介绍一下什么是联合索引?什么样的情况下我们会使用联合索引?

联合索引,就是两个或两个以上字段组成的索引,其遵循“最左匹配原则”。

例如这里有个市民信息表:

CREATE TABLE `tuser` (
  `id` int(11) NOT NULL,
  `id_card` varchar(32) DEFAULT NULL,
  `name` varchar(32) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `ismale` tinyint(1) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `id_card` (`id_card`),
  KEY `id_card_name` (`id_card`, `name`),
  KEY `name_age` (`name`,`age`)
) ENGINE=InnoDB

假如,如果有根据身份证号查询市民信息的需求,我们只需要在身份证号字段上建立索引即可。而在建立一个(身份证号,姓名)的联合索引,是否有点浪费空间了呢?

如果现在有个高频请求,要根据市民的身份证号查询姓名,那这个联合索引就很有意义了。它可以在这个高频请求上用到覆盖索引,不再需要回表查整行记录,减少语句的执行时间

还有,如果你的需求是查询某个人的名字,并且使用了左模糊查询,如 “where name like `姓%`”。这时,系统就会使用到 (name,age)这个联合索引了,只要满足最左前缀,就可以利用索引来加速检索。

3. 唯一索引了解吗?在使用的时候,有什么需要注意的不?

当一个字段不希望重复的时候,则可以考虑使用唯一索引,其具有唯一性质
我通过对比普通索引,来讲讲唯一索引在业务上有什么需要注意的地方吧

我们就普通索引和唯一索引在查询语句和更新语句的性能影响来分析

假设现在查询语句为 select id from T where k = 5;

  • 对于普通索引,查找到第一个满足 k = 5 的数据后,还需要继续查找下一个记录,直到碰到不满足 k = 5 的记录
  • 而对于唯一索引,因为其定义了唯一性,所以在碰到第一个满足 k = 5 的记录后就不会继续进行了

那么,这二者会有多少性能差距呢?答案是,微乎其微

InnoDB 的数据是按数据页为单位来读写的,当需要读一条记录时,并不是将这个记录本身从磁盘读出来,而是以页为单位,将其整体读入内存,InnoDB一个数据页的默认大小为 16KB

对于一个整型字段,一个数据页是可以存放许多 key的,所以连续相同的值充满整张数据页的概率是很低的,即代表普通索引可以在一个数据页中完成查询,所以二者查询性能并没有多少差距

而对于更新操作,其中关键点在于 change buffer 。当需要更新一个数据页中,而数据页又没在内存当中,就会将更新操作记录在 change buffer 中,当下次查询该数据页时,将数据页读入内存,就会执行 change buffer 中与这个页有关的操作。

对于唯一索引,如果需要更新的数据页已经在内存中了,就没必要把操作记录在 change buffer 中了,而是直接去数据页执行操作。如果需要更新的数据页没有在内存中,为了保证唯一索引的唯一性,则先要把数据页从磁盘加载到内存当中,然后进行判断更新的记录是否已经存在。

因此,唯一索引的根本就不能用 change buffer,实际只有普通索引能用。

举个例子,如果要在一张表中插入一条新数据

第一种情况,这个记录要更更新的目标页在内存中,InnoDB 的处理流程如下:

  • 唯一索引,找到目标位置,判断没有冲突,插入,执行结束
  • 普通索引,找到目标位置,插入,执行结束

这样来看,二者性能并没有什么差别

第二种情况,这个记录要更新的目标页不在内存中,InnoDB 的处理流程如下:

  • 唯一索引,需要将数据页从磁盘读入内存,找到目标位置,判读是否冲突,插入,执行结束
  • 普通索引,将更新操作记录在 change buffer 中,执行结束

要知道,将数据从磁盘读入内存,这是数据库里面成本最高的操作之一。而 change buffer 减少了磁盘访问的操作,所以对更新的性能提升是会很明显的

综上所述,普通索引和唯一索引主要是在更新操作上会有很大的性能差距,而查询操作基本没什么差距。

所以,如果不是业务需求的话,建议少使用唯一索引,优先考虑普通索引

4. 我们有时候会听到索引下推,你知道什么是索引下推吗?那覆盖索引又是什么意思呢?

索引下推是 Mysql 5.6 及之后引出的优化

还是第二个问题中的那个表,假设现在有这个个 sql 语句

mysql> select * from tuser where name like '张%' and age=10;

查询表中,名字第一个是张,而且年龄是 10 岁的所有小孩。

在 Mysql 5.6之前,则会找到所有姓张的小孩,然后根据主键值一个个回表查找出数据行,再对比字段。

而在 Mysql 5.6 推出索引下推之后,还会把 age 不符合条件的姓张的小孩给过滤掉,可以减少回表次数。这就是索引下推

那索引覆盖呢。假设现在有这么个 sql 语句(k上有索引)

mysql> select * from table where k between 3 and 5;

这样会在 k 索引树上一个一个找到 k 满足条件的主键值,再回到主键索引树上去回表查找。这样会导致许多磁盘 I/O 操作

如果将 sql 语句改为

mysql> select ID from table where k between 3 and 5;

这时,只需要查找 ID 的值,而 ID 的值已经在 k 索引树上了,因此可以直接提供查询结果,不需要回表。也就是说,在这个查询里面,索引 k 已经 “覆盖了” 我们的查询需求,我们就称为覆盖索引。


诚恳欢迎大家提出意见Orz

...... (待续未完

你可能感兴趣的:(Mysql,数据库,mysql,面试)