首页 > 其他分享 >什么是覆盖索引

什么是覆盖索引

时间:2022-09-04 10:33:44浏览次数:60  
标签:聚集 name 覆盖 什么 id 回表 索引 主键

前言

在了解索引覆盖前,我们先来看下,聚集索引,非聚集索引,回表等概念.

什么是聚集索引

聚集索引是按照每张表的主键构造一颗B+树,同时叶子节点中存放的就是整张表的行记录数据,也将聚集索引的叶子节点称为数据页。这个特性决定了索引组织表中数据也是索引的一部分

  1. 主键被定义了,那么这个主键作为聚集索引
  2. 主键没有被定义,那么该表的第一个唯一非空索引被作为聚集索引
  3. 主键没有定义,同时也没有合适的唯一索引,那么innodb内部会生成一个隐藏的主键作为聚集索引,这个隐藏的主键是一个6个字节的列,该列的值会随着数据的插入自增

什么是非聚集索引

在聚集索引之上创建的索引称之为非聚集索引,非聚集索引访问数据总是需要二次查找。叶子节点存储的不是行的物理位置,而是主键值。通过非聚集索引首先找到的是主键值,再通过主键值找到数据行的数据页,再通过数据页中的Page Directory找到数据行。

  1. 叶子节点并不包含行记录的全部数据,叶子节点除了包含键值外,还包含了相应行数据的聚簇索引键。
  2. 不影响数据在聚簇索引中的组织,所以一张表可以有多个非聚集索引

什么是回表

现在有这样一张表:

CREATE TABLE `user`  (
  `id` int(11) NOT NULL,
  `name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
  `sex` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
  `flag` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `idx_name`(`name`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Compact;

INSERT INTO `user` VALUES (1, 'shenjian', 'm', 'A');
INSERT INTO `user` VALUES (3, 'zhangsan', 'm', 'A');
INSERT INTO `user` VALUES (5, 'lisi', 'm', 'A');
INSERT INTO `user` VALUES (9, 'wangwu', 'f', 'B');

两个 B+ 树索引分别如图:

  • id 为主键,聚集索引,叶子节点存储行记录;
  • name 为索引,普通索引,叶子节点存储主键值,即 id


既然从普通索引无法直接定位行记录,那普通索引的查询过程是怎么样的呢?
通常情况下,需要扫码两遍索引树。
例如:

SELECT * FROM `user` WHERE name='lisi';

是如何执行的呢?

如粉红色路径,需要扫码两遍索引树:
先通过普通索引定位到主键值 id=5,在通过聚集索引定位到行记录
这就是所谓的回表查询,先定位主键值,再定位行记录,它的性能较扫一遍索引树更低

覆盖索引

如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称之为“覆盖索引”
我们知道 InnoDB 存储引擎中,如果不是主键索引,叶子节点存储的是主键+列值。最终还是要“回表”,也就是要通过主键再查找一次,这样就会比较慢。覆盖索引就是把要查询出的列和索引是对应的,不做回表操作

如何实现索引覆盖?

常见的方法是:将被查询的字段,建立到联合索引里去(或者说 查询的字段都已经建立了索引)。

还是用上边的例子 user 表

第一个SQL语句:

SELECT id, name FROM user WHERE name='shenjian';

能够命中 name 索引,索引叶子节点存储了主键 id,通过 name 的索引树即可获取 id 和 name,无需回表,符合索引覆盖,效率较高。
第二个SQL语句:

SELECT id, name, sex FROM user WHERE name='shenjian';

能够命中 name 索引,索引叶子节点存储了主键 id,但 sex 字段必须回表查询才能获取到,不符合索引覆盖,需要再次通过 id 值扫码聚集索引获取 sex 字段,效率会降低。
如果把 (name) 单列索引升级为联合索引 (name, sex) 就不同了:
再次执行,第二个SQL语句:

SELECT id, name, sex FROM user WHERE name='shenjian';

能够命中 联合索引,索引叶子节点存储了主键 id,通过 联合索引 的索引树即可获取 name 和 sex,无需回表,符合索引覆盖,效率较高。

回表优化

现在有一张订单信息表, sn字段建立了非聚集索引,有一条查询订单信息的sql:

SELECT o1.* FROM orders WHERE sn='XD12345678' LIMIT 10000,10

因为数据表是 InnoDB,根据 InnoDB 索引的结构,查询过程为:

  1. 通过二级索引查到主键值(找出所有 sn='XD12345678' 的 id)。
  2. 再根据查到的主键值通过主键索引找到相应的数据块(根据 id 找出对应的数据块内容)。
  3. 根据 offset 的值,查询 10010 次主键索引的数据,最后将之前的 10000 条丢弃,取出最后 10 条
    因为我们要查询 o1.*,前边丢弃的 10000 条数据,经过大量回表操作,造成了大量的 I/O 消耗,浪费了很多性能,导致查询时间变得很长。
    优化sql:
SELECT o1.* FROM orders o1
INNER JOIN (SELECT id FROM orders WHERE sn='XD12345678' LIMIT 10000,10) o2
ON o1.id = o2.id;

而这样的写法在 o2 分页查询时根本无需回表只查询 id,最后再做一个内连接根据主键取出数据,虽然增加了 SQL 语句的复杂度,但是性能非常好。

标签:聚集,name,覆盖,什么,id,回表,索引,主键
From: https://www.cnblogs.com/susuww/p/16654414.html

相关文章

  • Vuex是什么,每个属性是干嘛的,如何使用 ?
    Vuex是集中管理项目公共数据的仓库。Vuex大幅减少了组件通信的繁杂度,拥有state数据存储,mutations处理同步事件,modules模块的注册,actions处理异步事件,getters过滤数......
  • mysql索引优化
    一、分页查询优化很多时候我们业务系统实现分页功能可能会用如下sql实现:select*fromemployeeslimit10000,10;表示从表employees中取出从10001行开始的10行......
  • 网站使用CDN对SEO有什么影响?
    网站开启CDN主要原因为以下三点:1、给网站加速,让网站访问更快一些。2、隐藏网站IP,减少被ddos攻击。3、服务器安全配置不高的情况下,可通过CDN来配置安全措施,避免CC攻击和......
  • 什么是测试驱动开发(TDD)?
    什么是测试驱动开发(TDD)?自从我的软件工程之旅开始以来,我的脑海中就没有几个关键问题,特别是关于测试策略我是否编写了足够的测试,我的测试套件是否完全适合所有用户场景?我......
  • SQLServer重建索引
    Use[数据库名称]GoDECLARE@DBCCStringNVARCHAR(1000)DECLARE@TableNameVARCHAR(100)DECLARECur_IndexCURSORFORSELECTNameASTblNameFROMsysobjectsWHE......
  • 所以, 这也是什么特性
    ......
  • auto作为返回值类型与decltype推断有什么区别
    对使用auto来表明函数返回类型的情况,编译器使用模板类型推导,会消除引用。decltype推断则保留返回值类型。比如template<typenameContainer,typenameIndex>auto......
  • 并发的核心:CAS 是什么?Java8是如何优化 CAS 的?
    大家可能都听说说Java中的并发包,如果想要读懂Java中的并发包,其核心就是要先读懂CAS机制,因为CAS可以说是并发包的底层实现原理。今天就带大家读懂CAS是......
  • 并发的核心:CAS 是什么?Java8是如何优化 CAS 的?_2
    大家可能都听说说Java中的并发包,如果想要读懂Java中的并发包,其核心就是要先读懂CAS机制,因为CAS可以说是并发包的底层实现原理。今天就带大家读懂CAS是......
  • 说一下 new 创建实例的时候都做了什么
    创建一个空对象让这个新对象的圆形_proto_指向构造函数的原型prototype让this指向新对象,然后执行构造函数的代码如果函数没有return,或者return基本数据类型,则最终返......