走出舒适圈,你会成为最好的自己
文章目录
前言
今天被问到了回表,不知道这个是什么鬼,回来一查原来是这个啊,记录一下……
要了解这个需要先知道聚集索引、普通索引、联合索引
一、聚集索引是什么?
1.聚集索引(clustered index)
示例表:
create table user (
id int primary key,
name varchar(20),
sex varchar(5),
index(name)
)engine=innodb;
InnoDB聚集索引的叶子节点存储行记录,因此, InnoDB必须要有,且只有一个聚集索引:
- 如果表定义了PK,则PK就是聚集索引
- 如果表没有定义PK,则第一个out null unique列是聚集索引
- 否则,InnoDB会创建一个隐藏的row-id作为聚集索引
备注:所以PK查询非常快,直接定位行记录
InnoDB普通索引的叶子节点存储主键值。
备注:注意,不是存储行记录头指针,MyISAM的索引叶子节点存储记录指针。
2.聚集索引与普通索引区别(非聚集索引)
示例数据:
INSERT INTO user (id, name, sex) VALUES (1, 'shenjian', 'm');
INSERT INTO user (id, name, sex) VALUES (3, 'zhangsan', 'm');
INSERT INTO user (id, name, sex) VALUES (5, 'lisi', 'm');
INSERT INTO user (id, name, sex) VALUES (9, 'wangwu', 'f');
存储数据的区别
两个B+树索引存储分别如上图所示:
- id为PK,聚集索引,叶子节点存储行记录
- name为key,普通索引,叶子节点存储PK值,即id
备注:所以通过name查询所有字段时需要扫描两遍索引树。
如:
select * from t where name='lisi';
执行过程,如下:
如:粉红色路径,需要扫描两遍索引树:
- 先通过普通索引(非聚集索引)定位到主键值id=5
- 再通过聚集索引定位到行记录
注:这就是所谓的回表查询,先定位主键值,再定位行记录,它的性能较扫一遍索引树更低。
3. 如何实现索引覆盖
常用方法是:将被查询的字段,建立到联合索引里去。
使用user表:
- 能够命中name索引,索引叶子节点存储了主键id,通过name的索引树即可获取id和name,无需回表,符合索引覆盖,效率较高。
select id,name from user where name='shenjian';
2. 能够命中name索引,索引叶子节点存储了主键id,但sex字段必须回表查询才能获取到,不符合索引覆盖,需要再次通过id值扫码聚集索引获取sex字段,效率会降低。
select id,name,sex from user where name='shenjian';
优化:把name单列索引升级为联合索引(name,sex)效果就不同了
create table user (
id int primary key,
name varchar(20),
sex varchar(5),
index(name, sex)
)engine=innodb;
select id,name ... where name='shenjian';
select id,name,sex ... where name='shenjian';
以上两个都能命中索引覆盖,无需回表。
4. 那些场景可以优化
4.1 全表count查询优化
原表为:
user(PK id, name, sex);
直接:不能利用索引覆盖。
select count(name) from user;
添加索引:就能够利用索引覆盖提效。
alter table user add key(name);
4.2 列查询回表优化
select id,name,sex ... where name='shenjian';
就能够利用索引覆盖提效。
4.3 分页查询
select id,name,sex ... order by name limit 500,100;
将单列索引(name)升级为联合索引(name, sex),也可以避免回表。
总结
有些东西,还在需要整理记忆的