MySQL 索引,优化,回表,执行计划等相关总结学习

一、MySQL 执行流程

MySQL 索引,优化,回表,执行计划等相关总结学习_第1张图片

innoDB表引擎:默认的事务型引擎,最重要最广泛的存储引擎,性能非常优秀,数据村粗在共享表空间,可以通过配置分开,主键查询性能高于其他引擎

myISM表引擎:5.1版本前这个是默认的存储引擎,拥有全文索引、压缩、空间函数。不支持事务和行级锁,不支持安全恢复安全性低),存储在MYD和MYI两个文件中

innoDB和myISM的区别:InnoDB支持行锁,myISM支持表锁,myISM存储在MYD和MYI两个文件中,InnoDB存在共享表空间,InnoDB支持事务处理,myISM不支持

InnoDB 存储引擎:B+ 树索引的叶子节点保存数据本身
MyISAM 存储引擎:B+ 树索引的叶子节点保存数据的物理地址

二、索引分类

分类标准 结果
数据结构 B+tree索引、Hash索引、Full-text索引
物理存储 聚簇索引(主键索引)、二级索引(辅助索引)
字段特性 主键索引、唯一索引、普通索引、前缀索引
字段个数 单列索引、联合索引

三、数据结构分类

索引类型 innoDB 引擎 MyISAM引擎 Memory引擎
B+ 索引
Hash 索引 ×(不支持Hash索引,但在内存结构中有一个自适应hash索引) ×
full-text 索引 ×

四、InnoDB 聚簇索引(主键索引)生成策略

  1. 根据主键ID 自动生成聚簇索引
  2. 没有主键 选择第一个不为 NULL 的唯一索引作为聚簇索引
  3. 以上均没有则会使用一个 6 个字节长整型的隐式字段 ROWID 构建聚簇索引(自增)

在建表时 InnoDB 存储引擎默认会创建一个主键索引,也就是聚簇索引,其它索引都属于二级索引

四、索引回表

定义:先通过索引扫描,再通过ID(主键)索引去取索引中未能提供的数据,即为回表

假设有表T 三个字段:id k name, 其中对 k 建立了单独索引
如果语句是 select * from T where id=200,即主键查询方式(聚集索引),则只需要搜索 ID 这棵 B+ 树,查询一表即可
如果语句是 select id, k from T where k=3,即普通索引查询方式,则只要搜索 k 索引树,这样的话查询一表即可。
如果语句是 select id, k , name from T where k=3,第一次通过普通索引查询方式得到 id 的值为 200,再到 id 索引树搜索一次(需要回表才能查到name这个数据)。这个过程称为回表。那如何避免回表,将k和name建成联合索引即可,当然,就算回表也会比没有建立索引快

也就是说,基于非主键索引的查询需要多扫描一棵索引树。因此,我们在应用中应该尽量使用主键(聚集索引)查询

如何避免回表:所以在查询时可以尽量用聚集索引来查(即主键查询)或根据业务需求建立的索引能满足索引查询字段。
但实际业务中很难建立一个索引就满足所有查询要求,所以,正常情况,回表也没事,只要能用到索引也能大大加快查询速度。

参考连接:
https://www.kancloud.cn/qq1014407916/huangwei123456/3019227

五、字段特性索引分类

类型 说明
主键索引 建立在主键字段上的索引,通常在创建表的时候一起创建,一张表最多只有一个主键索引,索引列的值不允许有空值
唯一索引 建立在 UNIQUE 字段上的索引,一张表可以有多个唯一索引,索引列的值必须唯一,但是允许有空值
普通索引 建立在普通字段上的索引,既不要求字段为主键,也不要求字段为 UNIQUE
前缀索引 指对字符类型字段的前几个字符建立的索引,而不是在整个字段上建立的索引,前缀索引可以建立在字段类型为 char、 varchar、binary、varbinary 的列上。使用前缀索引的目的是为了减少索引占用的存储空间,提升查询效率。创建前缀索引的方式如下

前缀索引
在这里插入图片描述

六、联合索引

通过将多个字段组合成一个索引,该索引就被称为联合索引。
联合索引遵循最左匹配原则

假设创建了一个 (a, b, c) 三字段的联合索引,那么如果查询条件是以下这几种,就可以匹配上联合索引:

…where a=x;
…where a=x and b=y and c=z;
…where a=x and b=y;

假设
a字段 是全局有序的(1, 2, 2, 3, 4, 88,99,104,106),而 b 是全局是无序的(12,78,89,26,3,88,5,2)。因此,直接执行where b = 12 这种查询条件没有办法利用联合索引的,利用索引的前提是索引里的 key 是有序的。

在 a 相同的情况下 b 才是有序的,比如 a 等于 2 的时候,假设此时 b 的值为(78,89),这时就是有序的,这个有序状态是局部的,因此执行where a = 2 and b = 7是 a 和 b 字段能用到联合索引的,也就是联合索引生效了。

联合索引之范围查询:

场景一: select * from t_table where a > 1 and b = 12 联合索引(a, b)哪一个字段用到了联合索引的 B+Tree

为 a

场景二: select * from t_table where a >= 1 and b = 12 联合索引(a, b)哪一个字段用到了联合索引的 B+Tree

a,b 都用到了 但其中 b 索引只在 a=1 时使用

场景三: select * from t_table where a between 2 AND 88 AND b = 12,联合索引(a, b)哪一个字段用到了联合索引的 B+Tree

(a,b)联合索引

场景四: select* from t_user where a like ‘2%’ and b = 78,联合索引(name, age)哪一个字段用到了联合索引的 B+Tree

(a,b)联合索引

七、执行流程SQL优化-索引下推

执行器与存储引擎之间的交互过程

主键索引查询
全表扫描
索引下推
MySQL 索引,优化,回表,执行计划等相关总结学习_第2张图片

  1. 对于联合索引(a, b),在执行 select * from table where a > 1 and b = 12 语句时只有 a 字段能用到索引
    在联合索引的 B+Tree 找到第一个满足条件的主键值(id 为 2)后还需要判断其他条件是否满足(看 b 是否等于 12),那么是在联合索引里判断还是在主键索引判断

  2. MySQL 5.6 之前,只能从 id 2 (主键值)开始一个个回表,到主键索引上找出数据行再对比 b 字段值

  3. MySQL 5.6 引入的索引下推优化(index condition pushdown) 可以在联合索引遍历过程中对联合索引中包含的字段先做判断,直接过滤掉不满足条件的记录从而减少回表次数。

当查询语句的执行计划里,出现了 Extra 为 Using index condition 说明使用了索引下推优化

联合索引之索引区分度
由于建立联合索引时的字段顺序对索引效率也有很大影响,越靠前的字段被用于索引过滤的概率越高
建立联合索引时,要把区分度大的字段排在前面,这样区分度大的字段越有可能被更多的 SQL 使用到

区分度计算方式某个字段所有值的不同个数 除以 总行数

如果索引的区分度很小,假设字段的值分布比较均匀,那么无论搜索哪个值都可能得到一半的数据,在这种情况下还不如不要索引
因为 MySQL 有一个查询优化器,查询优化器发现某个值出现在表的数据行中的百分比(常用的百分比界线是 30%)很高时,一般会忽略索引进行全表扫描!

八、常见索引优化

前缀索引优化:使用前缀索引是为了减小索引字段大小。但前缀索引有一定的局限性,例如:
order by 就无法使用前缀索引
无法把前缀索引用作覆盖索引

覆盖索引优化:执行SQl以达到索引覆盖

主键索引设置自增:每插入一条新记录都是追加操作,不需要重新移动数据,如果我们使用非自增主键,会出现页分裂,页分裂可能会造成大量的内存碎片,导致索引结构不紧凑,从而影响查询效率。

索引尽量设置为 NOT NULL:索引列存在 NULL 就会导致优化器在做索引选择的时候更加复杂,例如 count 会省略值为NULL 的行, NULL 值是一个没意义的值但会占用物理空间,至少会用 1字节

防止索引失效:通过执行计划分析 sql

九、索引失效

  1. 对索引使用左或者左右模糊匹配 (%xxx, %xxxxx%)
  2. 对索引使用函数 (例如点 length 的时候)
  3. 对索引进行表达式计算 (加减计算)
  4. 对索引隐式类型转换 (假设:设置了索引字段为 varchar 类型,但是写sql 时使用了 整形的写法,mysql 会通过默认的类型转换机制进行转换,即通过函数等方式,也就变相导致索引失效,但有时仅仅对值做了转换而没有对字段使用函数,那么即使做了潜在类型置换也不会导致索引失效)
    可以通过 select “10” > 9 的结果来知道MySQL 的数据类型转换规则是什么:
    如果规则是 MySQL 会将自动「字符串」转换成「数字」,就相当于 select 10 > 9,这个就是数字比较,所以结果应该是 1;
    如果规则是 MySQL 会将自动「数字」转换成「字符串」,就相当于 select “10” > “9”,这个是字符串比较,字符串比较大小是逐位从高位到低位逐个比较(按ascii码) ,那么"10"字符串相当于 “1”和“0”字符的组合,所以先是拿 “1” 字符和 “9” 字符比较,因为 “1” 字符比 “9” 字符小,所以结果应该是 0。
    实际上面的结果为 1,说明 MySQL 在遇到字符串和数字比较的时候,会自动把字符串转为数字,然后再进行比较

例如

# 字段是字符串写为整形
select * from t_user where aaabc = 123456;
# 相当于
select * from t_user where CAST(aaabc AS signed int) = 123456;
# 失效
# 字段为整形 写为 字符串
select * from t_user where id = "1";
# 相当于
select * from t_user where id = CAST("1" AS signed int);
# 有效

参考文献:
https://zhuanlan.zhihu.com/p/471209432?utm_id=0

  1. 联合索引非最左匹配(见上方联合索引说明)
  2. WHERE 子句中的 OR:在 WHERE 子句中,如果在 OR 前的条件列是索引列,而在 OR 后的条件列不是索引列,那么索引会失效

九、执行计划

执行计划中的参数可以参考如下

column 意义
id 序号,非Id
select_type 查询类型
table 查询表名
partitions 匹配分区
type 表示数据扫描类型,主要看此类型
processible_keys 可能选择的索引
key 实际选择的索引列,字段表示实际用的索引,如果这一项为 NULL,说明没有使用索引
key_len 索引长度
ref 与索引做比较的列
rows 预计要检索的行数
filtered 查询过滤的行数百分比
Extra 其他额外信息

type 扫描类型,执行效率从低到高为:

类型 (从低到高) 意义
All 全表扫描
index 全索引扫描
range 索引范围扫描
ref 非唯一索引扫描
eq_ref 唯一索引扫描
const 结果只有一条的主键或唯一索引扫描

索引长度计算说明:
MySQL 索引,优化,回表,执行计划等相关总结学习_第3张图片

使用,在 查询SQL 前加入关键字 EXPLAIN 即可执行查询对应sql 的执行计划

count(*) 和 count(1)

count() 是一个聚合函数,函数的参数不仅可以是字段名也可以是其他任意表达式,该函数作用是统计符合查询条件的记录中函数指定的参数不为 NULL 的记录有多少个

如果表里有二级索引时,InnoDB 循环遍历的对象就不是聚簇索引,而是二级索引。

count(1) 相比 count(主键字段) 少一个步骤,就是不需要读取记录中的字段值,所以通常会说 count(1) 执行效率会比 count(主键字段) 高一点

性能排序: count(*) = count(1) > count(主键字段) > count(字段)

你可能感兴趣的:(数据库,mysql)