索引是在存储引擎层实现的,且在 MySQL
不一样存储引擎中的实现也不一样,本篇文章介绍的是 MySQL
的 InnoDB
的索引。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');
在 InnoDB
中,表都是根据主键顺序以索引的形式存放的,也就是数据放在主键索引上,其余索引上保存的是主键 id
,这种存储方式的表称为索引组织表。性能
InnoDB
使用了 B+树
索引模型,因此数据都是存储在 B+树
中的。每个索引在 InnoDB
里面对应一棵 B+树
。优化
主键索引的叶子节点存的是整行数据。在 InnoDB
里,主键索引也被称为聚簇索引。code
非主键索引的叶子节点内容是主键的值。在 InnoDB
里,非主键索引也被称为二级索引、辅助索引。
blog
主键查询方式:只须要搜索 ID
这棵 B+树
;
普通索引查询方式:先搜索普通索引的 B+树
,获得主键索引 ID
的值,再到 ID
索引树上搜索,这个过程称为回表。排序
从空间的角度出发:主键列长度尽量短,每一个二级索引的叶子节点是主键,主键过长会致使二级索引占用空间更大。
从性能的角度出发:推荐使用自增索引,非自增主键在插入和删除的操做中,会致使页分裂和页合并。索引
先看下面这个 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
索引树上了,所以能够直接提供查询结果,不须要回表。
若是一个索引包含(或覆盖)全部须要查询的字段的值,称为覆盖索引,即只需扫描索引而无须回表。
若是不使用自增 ID
作主键,用业务字段直接作主键,则须要知足:只有一个索引,且该索引为惟一索引。
因为没有其余索引,因此不用考虑其余索引的叶子节点大小的问题,把这个索引设置为主键,避免每次查询须要搜索两棵树。
# 正确作法 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);
索引可能由于删除,或者页分裂等缘由,致使数据页有空洞,重建索引的过程会建立一个新的索引,把数据按顺序插入,这样页面的利用率最高,达到省空间的目的。
在上面的建表语句中,能够看到有两个索引,一个是 k
索引,一个是name_age
索引,不难看出,前者是单列索引,然后者就是联合索引了。
为何会有联合索引呢?当查询条件为2个及以上时,好比当常常要用 name
和 age
去查询数据时:
select * from T where name = 'Job' and age = 28;
建立一个 (name,age)
的联合索引,至关于建立了 name
和 name、age
这两个组合的索引,能够加速检索。
顾名思义是最左优先,以最左边的为起点任何连续的索引都能匹配上。
联合索引的示例图以下:
索引项是按照索引定义里的字段顺序来排序的,所以在建立联合索引时,要根据业务需求,where子句中使用最频繁的一列放在最左边。
当已经有了 (a,b) 这个联合索引后,通常就不须要单独在 a 上创建索引了。所以,第一原则是,若是经过调整顺序,能够少维护一个索引,那么这个顺序每每就是须要优先考虑采用的。
当建立 (a,b,c)
联合索引时,至关于建立了 (a)
单列索引、(a,b)
联合索引以及 (a,b,c)
联合索引。
想要索引生效的话,只能使用 a
和 a,b
和 a,b,c
三种组合;a,c
组合也能够,但实际上只用到了 a
的索引,并无用到 c
。
MySQL 5.6 引入索引下推优化(index condition pushdown),能够在索引遍历过程当中,对索引中包含的字段先作判断,直接过滤掉不知足条件的记录,减小回表次数。
好比根据(name,age)
联合索引查询全部知足名称以“张”开头的索引,而后直接再筛选出年龄小于等于10的索引,以后再回表查询全行数据。
注意:innodb
引擎的表,索引下推只能用于二级索引。
普通索引容许被索引的数据列包含重复的值,建立惟一索引的目的通常不是为了提升访问速度,而只是为了不数据重复。
当须要更新一个数据页时,若是数据页在内存中就直接更新,而若是这个数据页尚未在内存中的话,在不影响数据一致性的前提下,InnoDB 会将这些更新操做缓存在 change buffer 中,这样就不须要从磁盘中读入这个数据页了。在下次查询须要访问这个数据页的时候,将数据页读入内存,而后执行 change buffer 中与这个页有关的操做。经过这种方式就能保证这个数据逻辑的正确性。
不推荐使用惟一索引,这是由于:
从查询的角度出发:
从更新的角度出发:
redolog
;普通索引将更新操做写入 change buffer
,通知执行器更新完成;在下次读相关记录的时候,先把原记录读取到内存,再将 change buffer
上的操做在内存记录上回放,并写 redolog
;结论:change buffer 只适用于普通索引,而不适用于惟一索引。
关于索引的知识点比较多,但每看一遍专栏都会有新的收获。fighting!💪