Mysql索引数据结构和Explain介绍

105 阅读9分钟

介绍

索引是帮助MySQL高效获取数据的排好序的数据结构

常见的索引数据结构

二叉树

右边元素大于左边元素,查找的时间复杂度是O(logN),但是如果元素是依次递增或者递减的情况,会退化成链表,时间复杂度为O(N),跟不加索引一样

红黑树

image.png

二叉平衡树,虽然会自动平衡,因为只有两个叉,所以高度不可控,树的高度每增加一层,就是一次随机磁盘IO,性能差

Hash表

image.png key-value结构 查找等值时效率高,不适合范围查找(> in等),数据量大时会存在hash冲突,冲突多时,会退化成链表结构

B-Tree

每个节点都存数据,树高度太大,磁盘IO次数多

B+Tree

image.png

只有叶子节点存放数据,非叶子节点不存放数据(可以在一个磁盘数据页中存放更多索引,减少IO次数) 叶子节点依次递增存放数据,叶子节点之间用指针连接(双向链表),方便范围查询;B+树遍历整棵树,只要遍历叶子节点即可。

一个数据页大概16kb,一个索引8Byte,一个地址6Byte 所以一个数据页大概可以存16k/(8+6)Byte = 1170个索引, 假设叶子节点数据为1kb,那一个数据页可以存16行数据,3层的B+树就是1170*1170*16=2千万,所以一个表的容量最好是在2千万行左右,只需要进行3次磁盘IO就可以定位到数据,

查看数据页大小

image.png

聚集索引:用主键索引建立数结构,叶子节点存放的是整行数据,如果没有设置主键,Mysql会自己维护一个列row_id(性能差)

非聚集索引:叶子节点存放的是主键索引,查找数据时需要先找到主键索引,再到聚集索引找数据,这就是回表

索引维护:维护索引的有序性,所以建议主键自增,这样在维护时不需要频繁插入节点,效率高

覆盖索引:要查找的数据在一颗索引树上能全部拿到,对于非主键索引就是不需要再执行回表操作

最左前缀原理:索引按照顺序从左到右匹配,因为在进行索引维护时,数据会依次从左到右按照索引排序,如果查找时缺少左边的索引,就无法定位数据,需要全表扫描。

索引下推:利用覆盖索引上存的数据,判断是否满足查询条件,不符合条件的过滤掉,如果满足再进行回表查询,减少回表次数;(5.6后)

Explain工具

使用EXPLAIN关键字可以模拟优化器执行SQL语句,分析你的查询语句或是结构的性能瓶颈,执行查询会返回执行计划的信息,而不是执行这条SQL

注意:如果 from 中包含子查询,仍会执行该子查询,将结果放入临时表中

列介绍

image.png

一、id id列的编号是 select 的序列号,嵌套查询时,id列越大select执行优先级越高,id相同则从上往下执行,id为NULL最后执行

二、select_type

字段说明
simple简单查询。查询不包含子查询和union联合查询
primary复杂查询中最外层的 select
subquery包含在 select 中的子查询
derived包含在 from 子句中的子查询。MySQL会将结果存放在一个临时表中,也称为派生表
union在 union 中的第二个和随后的 select

image.png

三、table 表示当前explain语句正在访问哪个表

字段说明
<derivenN>从派生表查询,依赖explain结果中 id=N 的查询
<union1,2>1和2表示参与 union 的 select 行id

四、type 表示关联类型或访问类型,Mysql在定位数据行时大概查找范围,查询效率从上到下递减,优化索引最好到range级别,最好到ref

字段说明
NULLmysql能够在优化阶段分解查询语句,在执行阶段用不着再访问表或索引
system查询的表仅有一行,这是const类型的特列,平时不会出现,这个也可以忽略不计
const常量查询,一定是用到primary key 或者unique key
eq_ref连接查询中,从表只用主键查询
ref使用二级索引或覆盖索引的前缀
range使用索引检索范围
index扫描全部二级索引(遍历二级索引的叶子节点)
ALL全表扫描,扫描聚簇索引的所有叶子节点

image.png 两个查询都是常量查询,扫描行数是1,效率高

image.png 连接查询时,使用从表的主键(id)作为连接条件时,从表查询时因为使用主键索引,扫描行数为1行,简单查询中不会有该类型

image.png

查询语句使用非主键索引(二级索引)或者覆盖索引的部分前缀时,可能会查询到多行,出现该类型

image.png 范围扫描通常出现在 in, between ,> ,<, > 等操作中。使用一个索引来检索给定范围的行(如果指定的范围太大,效率低)。

image.png 查询时不加where条件,如果要查找的数据,同时在一级索引和二级索引中都存在,mysql优先使用二级索引,因为二级索引数据量小,取数据时扫描磁盘数据页少

五、possible_keys 本次查询中,可能使用的索引;当possible_keys 有值, 而key为null时,mysql认为索引对此次查询帮助不大,执行时不用索引,选择了全表查询,效率更高

六、key 表示Mysql实际使用哪个索引查找数据,如果没有使用索引,则该列是 NULL。如果想强制mysql使用或忽视possible_keys列中的索引,在查询中使用 force index、ignore index

七、key_len 表示mysql在索引里使用的字节数,通过这个值可以算出具体使用了联合索引中的哪些列。 下面的查询语句使用到了联合索引中的film_id,由于film_idint类型,占4个字节,所以key_len是4

image.png

计算规则

  • 字符串,char(n)和varchar(n),5.0.3以后版本中,n均代表字符数,而不是字节数,如果是utf-8,一个数字 或字母占1个字节,一个汉字占3个字节
    • char(n):如果存汉字长度就是 3n 字节
    • varchar(n):如果存汉字则长度是 3n + 2 字节,加的2字节用来存储字符串长度,因为 varchar是变长字符串
  • 数值类型
    • tinyint:1字节
    • smallint:2字节
    • int:4字节
    • bigint:8字节
  • 时间类型
    • date:3字节 timestamp:4字节
    • datetime:8字节
  • 如果字段允许为 NULL,需要1字节记录是否为 NULL 索引最大长度是768字节,当字符串过长时,mysql会做一个类似左前缀索引的处理,将前半部分的字符提取出来做索 引。

八、ref

表查找值所用到的列或常量,常见的有:const(常量),字段名(例:film.id)

九、rows

表示mysql估算要扫描的行数

十、Extra 展示额外信息 Using index:表示select查询的字段都能从这个索引树中获取(使用了覆盖索引),不需要回表

image.png

Using where: 使用了where,并且查询的列未使用到索引

image.png

Using index condition: 查询的列未被索引覆盖,where条件中是一个前导列的范围

image.png

Using temporary: mysql创建一张临时表处理查询,这种情况一般要进行有优化(建立索引)

image.png

建立索引后,因为索引树是有序的,所以建立索引后,扫描索引树就可以达到去重的目的

image.png

Using filesort:使用外部排序,数据量小时在内存中,否则需要在磁盘中完成优化(建立索引优化)

image.png

这里虽然建立了name的索引,但是没有覆盖所有字段,mysql认为走二级索引后还需要回表查询,效率低,所以不走name的索引,使用了外部排序(全表扫描)

索引使用

# 建立索引
KEY `idx_name_age_position` (`name`,`age`,`position`) USING BTREE

1、全值匹配 覆盖索引上的值全部使用到,效率更高

image.png 2、最左前缀法则 如果索引了多列,要遵守最左前缀法则。查询从索引的最左前列开始,不跳过索引中的列,下面查询跳过了name列,不符合最左前缀原则,无法使用索引

image.png

3、在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描

image.png 建立索引后,使用函数导致索引失效(因为索引树中只存有原始值,没有转换后的值),从而进行全表扫描

4、存储引擎不能使用索引中范围条件右边的列

image.png key_len是78,表示索引只使用了前两列,因为使用范围查询后,剩下的数据无序,没办法使用索引

5、尽量使用覆盖索引,减少 select * 语句

6、mysql在使用不等于(!=或者<>),not in ,not exists 的时候无法使用索引会导致全表扫描

< 小于、 > 大于、 <=、>= 这些,mysql内部优化器会根据检索比例、表大小、结果集等多个因素整体评估是否使用索引 image.png

7、is null,is not null 一般情况下也无法使用索引

在索引树中一般null的数据一般会集中存储,数据无序

8、like通配符开头,会使mysql索引失效,变成全表扫描操作

%在前,导致剩下的数据无序,无法使用索引树;使用覆盖索引可以使用到索引树 image.png

9、字符串不加单引号索引失效(隐式类型转换)

10、少用or或in,用它查询时,mysql不一定使用索引,mysql内部优化器会根据检索比例、表大小等多个因素整体评 估是否使用索引

image.png

image.png

11、范围查询优化

没走索引原因:mysql内部优化器会根据检索比例、表大小等多个因素整体评估是否使用索引。比如这个例子,可能是 由于单次数据量查询过大导致优化器最终选择不走索引

image.png

优化方法:可以将大的范围拆分成多个小范围

image.png

12、联合索引第一个字段使用范围不会走索引,Mysql认为第一个字段走范围,结果集很大,回表效率不高,不如全表扫描(可以用覆盖索引优化)

image.png 优化后走索引

image.png

like KK% 一般情况都会走索引(数据量大时不走索引) image.png

image.png

总结

image.png like KK%相当于=常量,%KK和%KK% 相当于范围