深入浅出MySQL索引(一)常见的索引模型
深入浅出MySQL索引(二)InnoDB存储引擎的索引
InnoDB使用B+Tree来组织索引,每个索引都是一棵B+Tree,不过对于主键索引和普通索引有所不同,下面讲详细介绍
使用下面的语句创建一张表
create table users(
id int primary key,
k int not null,
name varchar(16),
index(k)
)engine=innodb;
其中我们指定id为主键索引,k为普通索引
我们插入数据指定id和k的值分别为(150,1)、(200,2)、(350,3)、(550,5) 和 (600,6)
MySQL将主键索引称为聚簇索引,为什么称为聚簇索引呢?因为数据表的数据和索引是一起构建在一棵B+Tree里的,也就是数据表的数据就存在于B+Tree的叶子节点中,例如上述表中id的索引如下所示
普通索引称为辅助索引,如上述表的k就是一个普通索引,普通索引于主键索引的区别是,普通索引的叶子节点存放的不是数据,而是主键值,如下所示
下面举个例子来说明如何使用普通索引来查询
执行下面的sql语句的查询过程是什么样的呢?
select * from users where k=2;
首先会通过k索引这颗B+Tree找到对应的主键为200,然后再使用200在主键索引这颗B+Tree检索出数据
这个操作称为回表
我们来看主键索引的B+Tree,如果添加一个主键值为700的数据行,那么只要再最后面追加数据就行了。但是如果添加一个主键值为400的数据行就比较麻烦了,此时需要将此页后面的数据往后移动,再插入数据,这是一个比较费劲的操作。更糟糕的情况是,如果此页满了,那么就需要分配新的页,原本放在一个页的数据就分为两个页存放,空间利用率就变低了,这个过程称为页分裂。当然如果两个页由于数据被删除导致利用率很低,就会发生页合并
基于上述的情况,一般将主键设置为自增主键是个比较合理的选择,自增主键可以通过NOT NULL PRIMARY KEY AUTO_INCREMENT
在建表的时候指定
另外从普通索引的角度来看,普通索引的叶子节点存放的是主键,如果主键越大,建立普通索引占用的空间就越大,所以从性能和存储的角度考虑,主键使用自增主键也是一个比较合理的选择。
我们在建立索引的时候,不仅仅可以使用一个列来作为一个索引,还可以使用多个列来一个索引,称为联合索引
如上述表
create table users(
id int primary key,
k int not null,
name varchar(16),
index(k)
)engine=innodb;
现在我们要查找k=5
的用户它的name,对应的sql语句如下
select name from users where k=5;
这条语句的查找过程是:首先要在k索引树上面查找对应的主键值,然后在主键索引上查找,最后检索出相应的数据行,也就是需要一次回表操作
假如现在我们建立(k,name)这样的联合索引,如下所示
create table users(
id int primary key,
k int not null,
name varchar(16),
index(k),
index(k,name)
)engine=innodb;
那么现在就有一棵新的索引树,这个索引树的key是(k,name)联合,根据列的顺序来进行排序,比如先比较k,k相等的情况下再比较name
此时的B+Tree如下所示
由于此时name的值已经存在于索引中,所以再执行下面的sql语句就不需要再进行回表操作了
select name from users where k=5;
同样的,主键id的值也存在于索引中,所以执行下面的sql语句也不需要进行回表操作
select id, name from users where k=5;
我们可以使用联合索引来加快查询,但是只有满足最左前缀原则的时候,才能使用联合索引来加速
最左索引原则解释起来可能会有点抽象,下面通过例子来说明
例如上面我们的建立来一个联合索引(k,name)
我们使用k来查询符合最左前缀原则,可以使用索引来加速,例如下面的sql语句
select * from users where k=5;
如果我们使用k和name来查询,符合最左前缀原则,可以使用索引来加速,例如下面的sql语句
select * from users where k=5 and name='xxx';
如果我们使用k和name的前面一部分来查询,符合最左前缀原则,可以使用索引来加速
比如表中有个一个数据,k和name的值为(5,‘Justin’),可以使用下面的sql语句来查询
select * from users where k=5 and name='jus%';
如果我们单纯使用name来查询则不符合最左前缀原则,不能使用(k,name)联合索引来加速,如下面的sql语句
select * from users where name="xxx";