Innodb的覆盖索引实践

前言

MySQL的索引分为6种级别,级别如下

  • system:系统表,少量数据,往往不需要进行磁盘IO
  • const:常量连接
  • eq_ref:主键索引(primary key)或者非空唯一索引(unique not null)等值扫描
  • ref:非主键非唯一索引等值扫描
  • range:范围扫描
  • index:索引树扫描
  • ALL:全表扫描(full table scan)

6种级别中,type扫描方式由快到慢,system > const > eq_ref > ref > range > index > ALL

5.6X开始,现在的MySQL默认引擎是INNODB,innodb是索引组织结构,首先从主键去扫描,按照表的主键构造一颗B+树,非主键索引指向主键 管理叶子节点中存放的就是整张表的行记录数据 主键索引 又称为集簇索引非主键索引 称为辅助索引、二级索引

主键索引的点查询 【where 索引=XXX】隶属于const, 非主键索引的点查询 则属于ref, 非主键索引一般情况总是要回表 ,那么二级索引有没有可能 更快,有可能,那就是覆盖索引

实验环境

单表数据集 数据结构 查询SQL
100万 主键、索引 sum\group by
sql 复制代码
mysql> select version();
+-----------+
| version() |
+-----------+
| 5.7.34    |
+-----------+
1 row in set (0.01 sec)
​
​
mysql> select count(*) from tt2;
+----------+
| count(*) |
+----------+
|  1000000 |
+----------+
1 row in set (0.37 sec)
​
​
其中c0和c1是同样的数据,
CREATE TABLE `tt2` (
  `c0` int(11) NOT NULL DEFAULT '0',
  `c1` int(11) NOT NULL DEFAULT '0',
  `c2` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
  `c3` double NOT NULL
) 
​

主键查询

sql 复制代码
​
​
mysql> alter table  tt2 add primary key(c0);
​
​
      0.0169409732334316 |
|        0.24051994131878 |
+-------------------------+
1000000 rows in set, 1 warning (2.77 sec)
​
mysql> explain  select sql_no_cache sum(c3) from tt2     group by c0;
+----+-------------+-------+------------+-------+------------------------------------------------+---------+---------+------+--------+----------+-------+
| id | select_type | table | partitions | type  | possible_keys                                  | key     | key_len | ref  | rows   | filtered | Extra |
+----+-------------+-------+------------+-------+------------------------------------------------+---------+---------+------+--------+----------+-------+
|  1 | SIMPLE      | tt2   | NULL       | index | PRIMARY | PRIMARY | 4       | NULL | 997545 |   100.00 | NULL  |
+----+-------------+-------+------------+-------+------------------------------------------------+---------+---------+------+--------+----------+-------+
1 row in set, 2 warnings (0.01 sec)
​
​
​

非主键索引

scss 复制代码
创建普通索引
​
mysql> create index c1_idx1 on tt2(c1);
Query OK, 0 rows affected (1.95 sec)
Records: 0  Duplicates: 0  Warnings: 0

正确的覆盖索引

scss 复制代码
创建覆盖索引
​
mysql> create index  c3_c1_idx on   tt2(c1,c3);
Query OK, 0 rows affected (3.47 sec)
Records: 0  Duplicates: 0  Warnings: 0
​

错误的覆盖索引

scss 复制代码
同样创建覆盖索引,不过c1和c3的位置相反
mysql> create index  c1_c3_idx on   tt2(c3,c1);
Query OK, 0 rows affected (4.02 sec)
Records: 0  Duplicates: 0  Warnings: 0
​
​

错误的覆盖索引导致了filesort和temporary现象,filesort和temporary的技术原理如下。为什么会发生filesort和temporary,主要是innodb是索引组织的有序排序结构,不遵从它的玩法,内存管理上需要耗费额外多的性能。

  1. using filesort

filesort主要用于查询数据结果集的排序操作,首先MySQL会使用sort_buffer_size大小的内存进行排序,如果结果集超过了sort_buffer_size大小,会把这一个排序后的chunk转移到file上,最后使用多路归并排序完成所有数据的排序操作。

MySQL filesort有两种使用模式:

模式1: sort的item保存了所需要的所有字段,排序完成后,没有必要再回表扫描。

模式2: sort的item仅包括,待排序完成后,根据rowid查询所需要的columns。

很明显,模式1能够极大的减少回表的随机IO。

  1. using temporary

MySQL使用临时表保存临时的结构,以用于后续的处理,MySQL首先创建heap引擎的临时表,如果临时的数据过多,超过max_heap_table_size的大小,会自动把临时表转换成MyISAM引擎的表来使用。

从上面的解释上来看,filesort和temporary的使用场景的区别并不是很明显,不过,有以下的原则:

filesort只能应用在单个表上,如果有多个表的数据需要排序,那么MySQL会先使用using temporary保存临时数据,然后再在临时表上使用filesort进行排序,最后输出结果。

性能记录

主键查询(单位秒) 非主键索引 正确覆盖索引 错误覆盖索引
2.71 6.41 2.21 9.44

总结

  • 主键索引未必是最快的,未必比非主键索引快,根据各种场景而定。
  • 组合索引【覆盖索引】注意先后排序,否则使用了出现临时空间表、文件排序
  • MySQL的索引6种级别,我们做到ref就不错了,大部分的情况是index
  • 主键索引是innodb的核心性能的标配,可以选配UUID或者递增ID,熟悉MySQL的人都会选配递增ID,因为只有递增ID才能发挥innodb的性能。递增ID能够使innodb有序存放数据,但是大部分业务场景,我们可能无法使用递增ID做索引,基于安全的需要也不能暴露主键ID。 最后二级索引是常态,进一步的优化就是覆盖索引了。
相关推荐
Rust研习社2 小时前
组合真的优于继承吗?为什么 Rust 和 Go 都拥抱组合舍弃继承?
后端·rust·编程语言
IT_陈寒2 小时前
JavaScript的闭包把我坑惨了,说好的内存会自动回收呢?
前端·人工智能·后端
CaffeinePro3 小时前
Pydantic深度使用:数据校验、枚举、ORM映射
后端·fastapi
Chenyiax4 小时前
从 Chat 到 Responses:OpenAI API 抽象为什么变了?
后端
MariaH4 小时前
Koa和Express的区别
后端
MariaH4 小时前
Koa框架的使用
后端
luckdewei5 小时前
那个用 passlib 做认证的新同事,上线第一天就把用户密码写进了日志
后端
ping某6 小时前
为什么 Nginx 明明监听了 80,转发后端时却用了 4xxxx 端口?
后端·nginx
JustHappy6 小时前
我汇总了身边朋友的经历才发现,其实第一份实习是最难找的......
前端·后端·面试
uhakadotcom6 小时前
在python 的 工程化架构中 ,什么是 薄包装器层?
后端·面试·github