什么是覆盖索引?什么是回表查询?怎样实现覆盖索引?

The article summary

    • 1 什么是回表查询
        • 1.1 mysql的存储引擎
        • 1.2 InnoDb存储引擎
        • 1.3 聚集索引和普通索引的区别
    • 2 什么是覆盖索引
    • 3 怎样实现覆盖索引
        • 没有使用覆盖索引情况
        • 使用覆盖索引情况

1 什么是回表查询

前提:本次测试使用的是mysql 5.6版本。

1.1 mysql的存储引擎

mysql的存储引擎分类比较多,比较常用的是MyISAMInnoDb两种,具体各种存储引擎就不详说了。

1.2 InnoDb存储引擎

InnoDb存储引擎又有两大类索引

  • 聚集索引(clustered index)
  • 普通索引(secondary index)

1.3 聚集索引和普通索引的区别

InnoDb的聚簇索引的叶子节点存储行记录,因此InnoDb必须要有聚簇索引且仅有一个聚簇索引。聚簇索引也是有条件的,那就是必须是有序的,这里又要注意一点,有序不是连续,如:1,3,5,7是有序的,但不是连续的,依然可以构成聚簇索引的。如果是通过uuid生成的主键是不能构成聚簇索引的。

InnoDb叶子节点存储行记录,是一条完整的数据,MyISAM叶子节点存储记录指针。

  • 如果定义了PK,那PK就是聚簇索引。
  • 如果没有定义PK,那NOT NULL UNIQUE列就是聚簇索引
  • 否则 InnoDb会创建一个隐藏的row-id列做为聚簇索引。

大概了解了上述概念后,那什么是回表呢?假设有一张表t(id PK, name KEY, sex, flag),表中有4条记录。

CREATE TABLE `user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `sex` varchar(5) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `flag` varchar(5) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

什么是覆盖索引?什么是回表查询?怎样实现覆盖索引?_第1张图片

  • 两种不同的索引存储上述数据的区别
    • 聚簇索引存储(存储行记录)
      什么是覆盖索引?什么是回表查询?怎样实现覆盖索引?_第2张图片
    • 非聚簇索引 (存储PK)
      什么是覆盖索引?什么是回表查询?怎样实现覆盖索引?_第3张图片

假设我们执行下面的查询语句,mysql是如何执行的?

select * from user where name='ls';

什么是覆盖索引?什么是回表查询?怎样实现覆盖索引?_第4张图片
总结: 粉红色路径就就是查询的执行的过程,先通过普通索引name定位到主键值id=5,再通过聚簇索引定位到行记录。这就是所谓的回表查询,先定位主键值,再定位行记录,它的性能较扫一遍索引树更低。

2 什么是覆盖索引

  • 借用一下SQL-Server官网的说法。
    什么是覆盖索引?什么是回表查询?怎样实现覆盖索引?_第5张图片
  • MySQL官网,类似的说法出现在explain查询计划优化章节,即explain的输出结果Extra字段为Using index时,能够触发索引覆盖。
    在这里插入图片描述
  • 不管是SQL-Server官网,还是MySQL官网,都表达了:只需要在一棵索引树上就能获取SQL所需的所有列数据,无需回表,速度更快。

3 怎样实现覆盖索引

常见的方法是:通过建立复合索引覆盖被查询的字段,如何理解这句话可以通过explain工具分析下面两条查询语句。

没有使用覆盖索引情况

  • 表,注意是单值索引
create table user (
    id int primary key,
    name varchar(20),
    sex varchar(5),
    index(name)
)engine=innodb;
  • 查询语句
explain select id, name, sex from `user` where name = "ls"
  • explain分析结果
    在这里插入图片描述
  • 分析
    • keys这列的值是name,说明命中索引name,但是sex这一列的值要回表才能查询到。

使用覆盖索引情况

  • 表,注意是复合索引
create table user (
    id int primary key,
    name varchar(20),
    sex varchar(5),
    index(name, sex)
)engine=innodb;
  • 查询语句
explain select id, name, sex from `user` where name = "ls"
  • explain分析结果
    在这里插入图片描述
  • 分析
    • 都能够命中索引覆盖,无需回表。

你可能感兴趣的:(mysql)