《MySQL实战45讲》学习笔记4——MySQL中InnoDB的索引

索引是在存储引擎层实现的,且在 MySQL 不一样存储引擎中的实现也不一样,本篇文章介绍的是 MySQLInnoDB 的索引。sql

下文将以这张表为例开展。缓存

# 建立一个主键为 id 的表,表中有字段 k,而且在 k 上有索引。
create table T(
  `id` int(11) AUTO_INCREMENT, 
  `k` int(11) NOT NULL, 
  `name` varchar(16),
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY (`k`),
  KEY `name_age` (`name`,`age`)
) ENGINE=InnoDB;

# 插入多条数据
insert into T values (100, 'Bob'),(200, 'Peter'),(300,'Mary');

1、InnoDB索引模型

InnoDB 中,表都是根据主键顺序以索引的形式存放的,也就是数据放在主键索引上,其余索引上保存的是主键 id,这种存储方式的表称为索引组织表性能

InnoDB 使用了 B+树 索引模型,因此数据都是存储在 B+树 中的。每个索引在 InnoDB 里面对应一棵 B+树优化

2、索引的类型

2.1 主键约束:主键索引和二级索引

主键索引的叶子节点存的是整行数据。在 InnoDB 里,主键索引也被称为聚簇索引code

非主键索引的叶子节点内容是主键的值。在 InnoDB 里,非主键索引也被称为二级索引辅助索引
blog

2.1.1 主键索引和非主键索引的区别

主键查询方式:只须要搜索 ID 这棵 B+树
普通索引查询方式:先搜索普通索引的 B+树,获得主键索引 ID 的值,再到 ID 索引树上搜索,这个过程称为回表排序

2.1.2 主键索引的选取规则

从空间的角度出发:主键列长度尽量短,每一个二级索引的叶子节点是主键,主键过长会致使二级索引占用空间更大。
从性能的角度出发:推荐使用自增索引,非自增主键在插入和删除的操做中,会致使页分裂和页合并。索引

2.1.3 非主键索引的优化:覆盖索引

先看下面这个 sql内存

select * from T where k = 100;

这个 sql 语句会在 k 索引树上找到 k=100 的记录,取得 ID=15
再到 ID 索引树查到 ID=15 对应的记录,发生了回表,若是将 sql 语句改成it

select id from T where k = 100;

由于 ID 的值已经在 k 索引树上了,所以能够直接提供查询结果,不须要回表。

若是一个索引包含(或覆盖)全部须要查询的字段的值,称为覆盖索引,即只需扫描索引而无须回表。

2.1.4 业务字段作主键的条件

若是不使用自增 ID 作主键,用业务字段直接作主键,则须要知足:只有一个索引,且该索引为惟一索引。

因为没有其余索引,因此不用考虑其余索引的叶子节点大小的问题,把这个索引设置为主键,避免每次查询须要搜索两棵树。

2.1.5 索引的重建
  • 主键索引的重建
# 正确作法
alter table T engine=InnoDB

# 错误作法
alter table T drop primary key;
alter table T add primary key(id);

直接删掉主键索引会使得全部的二级索引都失效,而且会用 ROWID 来做主键索引。

  • 非主键索引的重建
alter table T drop index k;
alter table T add index(k);

索引可能由于删除,或者页分裂等缘由,致使数据页有空洞,重建索引的过程会建立一个新的索引,把数据按顺序插入,这样页面的利用率最高,达到省空间的目的。

2.2 索引字段数量:联合索引和单列索引

在上面的建表语句中,能够看到有两个索引,一个是 k 索引,一个是name_age 索引,不难看出,前者是单列索引,然后者就是联合索引了。

为何会有联合索引呢?当查询条件为2个及以上时,好比当常常要用 nameage 去查询数据时:

select * from T where name = 'Job' and age = 28;

建立一个 (name,age) 的联合索引,至关于建立了 namename、age 这两个组合的索引,能够加速检索。

2.2.1 最左前缀原则

顾名思义是最左优先,以最左边的为起点任何连续的索引都能匹配上。

联合索引的示例图以下:

索引项是按照索引定义里的字段顺序来排序的,所以在建立联合索引时,要根据业务需求,where子句中使用最频繁的一列放在最左边

当已经有了 (a,b) 这个联合索引后,通常就不须要单独在 a 上创建索引了。所以,第一原则是,若是经过调整顺序,能够少维护一个索引,那么这个顺序每每就是须要优先考虑采用的。

当建立 (a,b,c) 联合索引时,至关于建立了 (a) 单列索引、(a,b) 联合索引以及 (a,b,c) 联合索引。
想要索引生效的话,只能使用 aa,ba,b,c 三种组合;a,c 组合也能够,但实际上只用到了 a 的索引,并无用到 c

2.2.2 索引下推

MySQL 5.6 引入索引下推优化(index condition pushdown),能够在索引遍历过程当中,对索引中包含的字段先作判断,直接过滤掉不知足条件的记录,减小回表次数。

好比根据(name,age)联合索引查询全部知足名称以“张”开头的索引,而后直接再筛选出年龄小于等于10的索引,以后再回表查询全行数据。

注意:innodb 引擎的表,索引下推只能用于二级索引。

2.3 惟一约束:惟一索引和普通索引

普通索引容许被索引的数据列包含重复的值,建立惟一索引的目的通常不是为了提升访问速度,而只是为了不数据重复。

2.3.1 change buffer机制

当须要更新一个数据页时,若是数据页在内存中就直接更新,而若是这个数据页尚未在内存中的话,在不影响数据一致性的前提下,InnoDB 会将这些更新操做缓存在 change buffer 中,这样就不须要从磁盘中读入这个数据页了。在下次查询须要访问这个数据页的时候,将数据页读入内存,而后执行 change buffer 中与这个页有关的操做。经过这种方式就能保证这个数据逻辑的正确性。

2.3.2 惟一索引和普通索引的选择

不推荐使用惟一索引,这是由于:

从查询的角度出发:

  • 若是查询结果全在内存上:惟一索引在数据页中查找知足查询条件的第一条记录便可返回;普通索引须要再获取下一条记录,因为索引项是有序的且内存操做,多一次判断的时间损耗可忽略不计;
  • 若是查询结果不在内存上:先把数据页加载到内存中,再按照查询结果全在内存的流程处理。

从更新的角度出发:

  • 若是须要更新的记录全在内存上,直接更新内存记录并返回;
  • 若是须要更新的记录不在内存上以及部分在内存上:惟一索引须要先将须要更新的记录从磁盘中加载到内存,更新内存记录并写 redolog;普通索引将更新操做写入 change buffer,通知执行器更新完成;在下次读相关记录的时候,先把原记录读取到内存,再将 change buffer 上的操做在内存记录上回放,并写 redolog
  • 普通索引在更新时,节省了更新时从磁盘读取记录的时间,而惟一索引在更新时,若记录不在内存,须要从磁盘读取记录到内存。

结论:change buffer 只适用于普通索引,而不适用于惟一索引。

后记

关于索引的知识点比较多,但每看一遍专栏都会有新的收获。fighting!💪

相关文章
相关标签/搜索