什么是回表查询呢?
首先需要从InnoDB的索引实现说起,InnoDB有两类索引:
1、聚集索引(clustered index)
2.、普通索引,也叫二级索引(secondary index)
聚集索引的叶子节点储存的是行记录,且每张表有且仅有一个聚集索引:
(1)、如果表中定义了主键,那么该主键索引就是聚集索引。
(2)、如果表中没有定义主键,那么从第一个字段开始往后,第一个 not null的unique列为聚集索引。
(3)、如果(1)、(2)都不满足,那么InnoDB会创建一个隐藏的row-id作为聚集索引。
所以,主键查询特别的快,直接定位到行记录。
普通索引的叶子节点保存的是索引字段值和主键值,并不是完整的行记录。
假设我们现在有一张表:
表结构为 t (id PK , name KEY , sex , flag),其中id为聚集索引,name为普通索引。
表中有四条这样的数据:
(1, shenjian, m, A)、(3, zhangsan, m, A)、(5, lisi, m, A)、(9, wangwu, f, B )
上图分别为它们在索引中的储存状态:
(1)、聚集索引的叶子节点储存了整个行记录。
(2)、普通索引只储存了name字段值和id值。
在假设现在有一条查询sql select * from t where name = 'lisi';
索引是怎么执行的呢?
执行路径为上图中标红的步骤:
(1)、先通过普通索引定位到主键id=5。
(2)、在通过聚集索引定位到对应的行记录。
需要先定位聚集索引值,在通过聚集索引定位到行记录,这种现象就是
回表查询
。
那什么又是索引覆盖呢?
mysql官方没有对此进行定义,但是有这方面的概述: 只需要通过一颗索引树,就可以获取sql所需要的列数据,无需回表,速度更快。
仍旧选择之前的表,但是我们的sql换成: select id,name from t where name = 'lisi';
,查看explain计划:
可以看到,命中了name索引,索引储存了id和name,所以Extra
项的值为Using index
,无需回表查询,符合索引覆盖,效率高。
另外一条sql : select id,name,sex from t where name = 'lisi';
,查看explain计划:
可以看到,同样的,也命中了name索引,但是索引的叶子节点并没有储存sex字段值,所以Extra
项的值为Using index condition
,需要回表查询通过聚集索引获取到sex的值,不符合索引覆盖,效率相对较低。
解决的办法是,将name索引修改为name、sex的组合索引,这样第二条sql查询就也满足索引覆盖的要求了。