MySQL系列:索引(下)

作者: 阿琦 | 2021-10-05 | 阅读

   

1前言

上一篇:MySQL系列:索引(上)

  • 回表
  • 覆盖索引
  • 降序索引
  • 最左前缀原则
  • 解决 docker mysql 不能输入中文问题
  • 其它版本 MySQL 联合索引
  • 索引下推
  • 索引尽量不要做修改操作

2继续

init

[2021-09-04 15:21:55] Connected
use aaaqi
[2021-09-04 15:21:55] completed in 70 ms
aaaqi> create table aaaqi_demo5 (
       id int primary key,
       k int NOT NULL DEFAULT 0,
       s varchar(16NOT NULL DEFAULT '',
       index k(k))
       engine=InnoDB
[2021-09-04 15:22:57] completed in 115 ms
aaaqi> insert into aaaqi_demo5 values(100,1'aa'),(200,2,'bb'),(300,3,'cc'),(500,5,'ee'),(600,6,'ff'),(700,7,'gg')
[2021-09-04 15:22:576 rows affected in 75 ms

如果执行

select * from aaaqi_demo5 where k between 3 and 5;

需要执行几次树的搜索操作,会扫描多少行?

InnoDB的索引组织结构
InnoDB的索引组织结构

这条 sql 查询语句的执行流程:

  1. 在 k 索引树上找到 k = 3 的记录,取得 id = 300;
  2. 再到 id 索引树查到 id = 300 对应的 R3;
  3. 在 k 索引树取下一个值 k = 5,取得 id = 500;
  4. 再回到 id 索引树查到 id = 500 对应的 R4;
  5. 在 k 索引树取下一个值 k = 6,不满足条件,循环结束

这个过程中,回到主键索引树搜索的过程,叫回表。可以看到这个查询过程读了 k 索引树的 3 条记录(步骤 1、3、5),回表了 2 次(步骤 2、步骤 4)。

这个栗子中,由于查询结果所需要的数据只在主键索引上有,所以必须回表,不过这个是可以进行索引优化从而避免回表过程的。

3覆盖索引

select id from aaaqi_demo5 where k between 3 and 5;

这条 sql 只查询 id 的值,而 id 的值已经在索引树了,所以不需要回表。也就是说,这个查询里面的索引 k 已经覆盖了 查询需求,这个称为 覆盖索引

那个绿色的就是覆盖索引:

覆盖索引 可以减少树的搜索次数,显著的提升查询性能,所以这是一个常用的性能优化手段

4降序索引

MySQL 8.0 的新特性,主要用来解决多列排序可能无法使用索引的问题,从而可以覆盖更多的应用场景。

栗子 init:

aaaqi> CREATE TABLE `aaaqi_demo6`
       (
           `id`      int(11NOT NULL,
           `name`    varchar(32DEFAULT NULL,
           `age`     int(11)    DEFAULT NULL,
           PRIMARY KEY (`id`),
           KEY `age` (`age`)
       ) ENGINE = InnoDB
[2021-09-04 18:22:55] [HY000][1681Integer display width is deprecated and will be removed in a future release.
[2021-09-04 18:22:55] [HY000][1681Integer display width is deprecated and will be removed in a future release.
[2021-09-04 18:22:55] completed in 102 ms
去掉警告
去掉警告
默认升序
默认升序
看Extra
看Extra

如果是降序的话,无法使用索引,虽然可以相反顺序扫描,但性能会受到影响。

创建索引:

aaaqi> alter table aaaqi.aaaqi_demo6 add index age_desc(age desc)
[2021-09-04 18:33:24] completed in 79 ms
创建降序索引
创建降序索引
Using filesort
Using filesort

创建降序索引后出现了Using filesort ...

官网ORDER BY
官网ORDER BY

5最左前缀原则

合理设计索引可以减少索引个数,B+树这种索引结构,可以利用索引的最左前缀 来定位记录。

栗子,假设 用(name,age)这个联合索引来分析。

name,age索引示意图
name,age索引示意图

当查询所有名字是阿琦 的人时,可以快速定位到 id4,然后向后遍历得到所有所需的结果。

如果查询所有名字的第一个字 的人

mysql> explain select * from aaaqi_demo6 where name like '阿%';

这时,也能够用上这个索引,查到第一个符合条件的记录是 id4,然后 向后遍历,直到不满足条件为止。

也会使用索引
也会使用索引

6解决 docker mysql 不能输入中文问题

解决 docker mysql 不能输入中文问题
解决 docker mysql 不能输入中文问题

7联合索引

aaaqi> CREATE TABLE `aaaqi_demo7`
       (
           `id`      int NOT NULL,
           `name`    varchar(32DEFAULT NULL,
           `age`     int    DEFAULT NULL,
           `number`     int    DEFAULT NULL,
           PRIMARY KEY (`id`),
           KEY `age` (`name`,`age`,`number`)
       ) ENGINE = InnoDB
[2021-09-04 20:26:57] completed in 87 ms

上面 KEY age 害,直接拷贝的 aaaqi_demo6 表结构

注意截图里面的 KEY age 索引名。

mysql> explain select * from aaaqi_demo7 where name='阿琦' and age=10 and number=6;
mysql> explain select * from aaaqi_demo7 where name like '阿%' and age=10 and number=6;
mysql> explain select * from aaaqi_demo7 where name='阿琦' and age=10;
in select * from aaaqi_demo7 where  age=10 and name='阿琦';
mysql> explain select * from aaaqi_demo7 where age=10 and number=6;
mysql> explain select * from aaaqi_demo7 where number=6;
索引名是 age ,懒得改了
索引名是 age ,懒得改了

看到了吧,怎么写都是使用索引了的。

MySQL版本号 - Server version: 8.0.23 MySQL Community Server - GPL

官网描述:https://dev.mysql.com/doc/refman/8.0/en/index-merge-optimization.html

8其它版本 MySQL 联合索引

select * from test where a=? and b=? and c=?;查询效率最高,索引全覆盖。

select * from test where a=? and b=?;索引覆盖a和b。

select * from test where b=? and a=?;经过mysql的查询分析器的优化,索引覆盖a和b。

select * from test where a=?;索引覆盖a。

select * from test where b=? and c=?;没有a列,不走索引,索引失效。

select * from test where c=?;没有a列,不走索引,索引失效。

这个应该是 5.6、5.7版本的 MySQL,具体版本不详,懒得再弄个低版本的 MySQL 去测试了,网上基本都是这样。

那么问题来了,8.0.X 和 5.X 版本不一样,面试官问联合索引相关的问题得注意了。

9索引下推

假设 名字第一个字是阿,而且年龄是 10 岁的所有人:

select * from aaaqi.aaaqi_demo7 where name like '阿%' and age=10;
01 无索引下推执行流程
01 无索引下推执行流程
02 索引下推执行流程
02 索引下推执行流程

在两个图里面,每一个虚线箭头表示回表一次。

图 01 中, name,age 索引里面去掉了 age 的值,这个过程 InnoDB 并不会去看 age 的值,只是按顺序把“name 第一个字是”的记录一条条取出来回表。因此,需要回表 4 次。

图 01 、图 02 的区别是,InnoDB 在 name,age 索引内部就判断了 age 是否等于 10,对于不等于 10 的记录,直接判断并跳过。在这个例子中,只需要对 id104、id105 这两条记录回表取数据判断,就只需要回表 2 次。

MySQL 8.0 索引下推 这个也做了优化,详情见官网:https://dev.mysql.com/doc/refman/8.0/en/index-condition-pushdown-optimization.html

10索引尽量不要做修改操作

因为索引可能因为删除,或者页分裂等原因,导致数据页有空洞,重建索引的过程会创建一个新的索引,把数据按顺序插入,这样页面的利用率最高,也就是索引更紧凑、更省空间。

主键就别瞎搞了,不论是删除主键还是创建主键,都会将整个表重建。


从下午 14 左右一直淦到 21:40,完事估计 22:00 点多了... .


版权声明:本文由 阿琦 在 2021年10月05日发表。本文采用CC BY-NC-SA 4.0许可协议,非商业转载请注明出处,不得用于商业目的。
文章题目及链接:《MySQL系列:索引(下)》




  相关文章:


留言区:

TOP