MySQL之单表访问方法

概念

一般我们查询想要的数据,只需要将特定的SQL规则告诉MySQL即可,具体MySQL如何访问找到数据是MySQL的事情,用户不需要关心,

不过像开车去目的地一样,走一环、走二环都可以到,无非是距离远近,时间长短的问题,MySQL也是一样,如何查询都可以,只要能查到正确的数据即可,

MySQL将对数据不同的访问方式称为 access method (访问方法),同一个SQL可以用不同的访问方法执行,不过不同的方法花费的成本差异也是巨大的,本篇则是介绍不同的访问方法,以及何时使用它们。

首先建立一张表:

CREATE TABLE `single_table` (
  `id` int(11) NOT NULL,
  `key1` varchar(100) COLLATE utf8mb4_bin DEFAULT NULL,
  `key2` int(11) DEFAULT NULL,
  `key3` varchar(100) COLLATE utf8mb4_bin DEFAULT NULL,
  `key_part1` varchar(100) COLLATE utf8mb4_bin DEFAULT NULL,
  `key_part2` varchar(100) COLLATE utf8mb4_bin DEFAULT NULL,
  `key_part3` varchar(100) COLLATE utf8mb4_bin DEFAULT NULL,
  `common_field` varchar(100) COLLATE utf8mb4_bin DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_key2` (`key2`) USING BTREE,
  KEY `idx_key1` (`key1`) USING BTREE,
  KEY `idx_key3` (`key3`) USING BTREE,
  KEY `idx_key_part` (`key_part1`,`key_part2`,`key_part3`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

复制代码

const

当我们使用主键或者唯一索引(唯一索引这里有特殊情况,等下说)进行数据访问时,访问方法一般就是const。

const代表常数级别,这种方法速度是非常的快的,访问代价基本可以忽略不计,如果主键或者唯一索引由多个列组成,那么要求每一列必须等参与常数等值比较才可以达到这种效果。

image.png

image.png

如果查询唯一索引的null值时,访问方法则不是const了,因为唯一索引并不限制null值的数量,查询null值可能存在多行数据。

image.png

ref

在使用普通索引进行查询时,生成的扫描区间是一个单点扫描区间时,访问方法就是ref,什么是单点扫描区间?

select * from single_table where key1 = '1'

如上sql,where条件中 key1 = '1', 最终生成的扫描区间就是一个 ['1', '1'],这个就叫做单点扫描区间,即定位到索引中第一条等于1的数据,一直扫描直到不符合数据为1的数据。

image.png

唯一索引使用is null时,一般情况下也是ref

image.png

联合索引只要最左边连续的列使用等值查询,也是ref

image.png

image.png

image.png

联合索引最左边连续的列如果不是使用等值查询,则非ref

image.png

ref_or_null

当使用二级索引进行等值查询时,同时带上了null值数据的查询,方法则是ref_or_null。

image.png

range

当索引列出现多个单点扫描区间或者出现范围扫描区间,方法就是range。

image.png

image.png

index

当使用联合索引中,非最左边的列查询时,无法形成合适的扫描区间,但是该查询符合如下两个条件,方法就是index:

  1. 查询列表只有key_part1、key_part_2、key_part3 三个列,索引idx_key_part正好包含这3个列。

  2. 搜索条件只有 key_part_2 这个列,这个列也在索引中。

虽然这种查询不符合索引最左匹配原则,需要扫描全部索引,但是因为扫描二级索引比扫描聚簇索引代价低(因为二级索引并不包含所有行内容),同时不用回表,所以给这种查找方法称为index。

image.png

在InnoDB存储引擎中,全表扫描并且使用了 order by 主键的写法,也会被认定为index。

image.png

all

直接查询全表数据。

image.png

索引合并 (index_merge)

Intersection 索引合并

如下SQL:

select * from single_table where key1 = '1' and key3 = '2'

因为key1 和 key3都是索引,一般有两种方案:

  1. MySQL选择key1作为索引查询
  2. MySQL选择key3作为索引查询

以上两种方案,不管用哪个,都需要进行一次回表,判断另一个列是否满足条件再返回,有没有一种可能,两个索引都利用起来?

有的,就是 Intersection 索引合并,原理很简单,就是分别去查询 key1为1的索引对应的主键集合,和key3为2的索引对应的主键集合,然后取二者的交集,

例如key1 = '1'的主键有[2,3,4], key3 = '2'的主键有[3,5,6],并集就是[3],也就是说只有主键是3的这条数据符合这个查询条件,

此时进行一次对主键3的回表操作,返回这条记录就可以了。

image.png

union 索引合并

select * from single_table where key1 = '1' or key3 = '2'

这里与 Intersection 很像,只不过 and 变成了 or,这里有点棘手,倒无法像上面的方案1、2一样,选择其中一个,然后回表查询,因为是or,可能key3=2的数据并不仅仅限于key1=1的记录里,反过来也是如此,

意味着两个索引不管选哪一个,另一个索引条件都需要进行一次全表扫描,因此我们可以借鉴上面的思路,两个索引都查,既然是or,取并集就行了,

例如key=1的记录有[1,2,3] , key3=2的记录有[3,4,5],取并集就是[1,2,3,4,5],使用这个5个id回表查询记录返回即可。

image.png

不论是union 还是 Intersection索引合并,都需要主键是有序排列的,这样才可以方便的进行并集和交集的操作。

sort-union 索引合并

select * from single_table where key1 < '10' or key3 > '10'

注意这里也是or,但是和 union 相比,索引条件查询方法是范围,这意味着查询出来的主键很大程度不是有序的,这样就无法进行并集的操作了,因为并集需要保证主键有序,

这个也好办,就是将key1<10和key3>10的主键查询出来后进行排序,然后取并集即可,相比union多了一步排序的操作。

image.png

为什么没有 sort-intersection ?

?select * from single_table where key1 < '10' and key3 > '10'? (可以任取一个然后回表判断)

根据个人的理解,因为排序是一个很费时的操作,如果and条件使用范围查询,完全可以取其中一个索引,然后进行回表操作即可,进行排序后又取交集再回表,可能性能上不相上下甚至有所降低,

但是or操作不一样,无法通过取其中一个索引再回表的操作,另一个条件可能全表都有,此时就要全表扫描,这样性能较差,不如两个索引都查然后排序一下再取并集,这样性能比全表扫描效果要好,这就是为啥 union 有 sort,但是 intersection 没有的原因吧,鄙人愚见。

结语

如果在 EXPLAIN 查看type的过程中,发现出现的type与上述情况不同,读者需要知道的是,MySQL会根据数据量、查询成本做出最终评估,比如你认为的range查询,MySQL觉得用ALL全表扫描更合适更快,就直接用ALL了,如下:

image.png

当然如果你可以让MySQL强制走索引(FORCE index (索引名称,并非索引列名称)),如下:

image.png

Supongo que te gusta

Origin juejin.im/post/7076077203741966343
Recomendado
Clasificación