mysql系列【你不知道的索引优化】(面试必问)

前言:相信大家面试的时候肯定被问过这个问题,但很多都是因为不够深入,或者了解片面而导致好的机会和你失之交臂,其实这个问题答得好是非常加分的,好了不多说了开始…

1、什么是索引?

  • 索引:索引(Index)是帮助MySQL高效获取数据的数据结构。索引的目的在于提更高查询效率,可以类比字典。
  • 常见的MySQL主要有两种结构:Hash索引B+ Tree索引,我们使用的是InnoDB引擎,默认的是B+树
  • 一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上
  • 基本语法
    - 创建:
    - create index index_name on table(column(length))
    - alter table add index on (column(length))
    - 删除:drop index index_name on table
    - 查看:show index from table

1.1、B+Tree结构了解一下

这里简单的聊下BTree的数据结构,首先看下面一个图
在这里插入图片描述

【介绍】
首先一棵B+Tree,浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色所示)和指针(黄色所示),如磁盘块1包含数据项17和35,包含指针P1、P2、P3,P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。
真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。
非叶子节点不存储真实的数据,只存储指引搜素方向的数据项,如17、35并不真实存在于数据表中。
【查找过程】
如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次l0,在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,内存时间因为非常短(相比磁盘的IO)可以忽略不计通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次lO,同时内存中做二分查找找到29,结束查询,总计三次lO。

1.2、索引到底有哪些

普通索引:仅加速查询

唯一索引:加速查询 + 列值唯一(可以有null)

主键索引:加速查询 + 列值唯一(不可以有null)+ 表中只有一个

组合索引:多列值组成一个索引,专门用于组合搜索,其效率大于索引合并

全文索引:对文本的内容进行分词,进行搜索

覆盖索引,select的数据列只用从索引中就能够取得,不必读取数据行,换句话说查询列要被所建的索引覆盖

1.3、索引是把双刃剑

优点

  1. 通过创建唯一索引,可以保证数据库每一行数据的唯一性
  2. 可以大大提高查询速度
  3. 可以加速表与表的连接
  4. 可以显著的减少查询中分组和排序的时间

缺点

  1. 创建索引和维护索引需要时间,而且数据量越大时间越长
  2. 创建索引需要占据磁盘的空间,如果有大量的索引,可能比数据文件更快达到最大文件尺寸
  3. 当对表中的数据进行增加,修改,删除的时候,索引也要同时进行维护,降低了数据的维护速度

2、索引优化

  • 让索引效率更高:这其中就涉及到索引的选择性了,索引的选择性是指索引列中不同值的数目与表中记录数的比。如果一个表中有2000条记录,表索引列有1980个不同的值,那么这个索引的选择性就是1980/2000=0.99。一个索引的选择性越接近于1,这个索引的效率就越高

mysql中其实优化的方面有很多,但是最让我们容易想到的就是索引优化
,但在这之前你需要了解一个东西…

2.1、其中最重要的就是这个explain关键字

以下标红的为常用字段

字段 含义
id select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序
select_type 查询的类型
table 当前行所查的表
partitions 匹配的分区
type 访问类型(system>const>eq_ref>ref>range>index>all)
possible_keys 可能用到的索引
key 真实用到的索引,没有用到的为null
key_len 索引 key 的长度
ref 显示了之前的表在key列记录的索引中查找值所用的列或常量
rows 查询扫描的行数,预估值,不一定准确
filtered 查询的表行占表的百分比
extra 额外的查询辅助信息

这里说下这些常用的字段:
id:select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序
这里有三种情况:
情况一:id不同,如果是子查询,id的值会递增,id值越大,优先级越高
情况二:id相同,执行顺序从上到下
情况三:同属存在,id如果相同,可以认为是一组,从上往下顺序执行;在所有组中,id值越大,优先级越高,越先执行

type:显示查询使用了何种类型,从最好到最差依次是:

system const eq_ref ref range index all

一般来说,得保证查询至少达到range级别,最好能达到ref

  • system 表只有一行记录(等于系统表),这是const类型的特列,平时不会出现,这个也可以忽略不计

  • const表示通过索引一次就找到了,const用于比较primary key或者unique索引。因为只匹配一行数据,所以很快const 如将主键置于where列表中,MySQL就能将该查询转换为一个常量

  • eq_ref唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描
    ref非唯一性索引扫描,返回匹配某个单独值的所有行.

  • ref本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而,ref可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体

  • range只检索给定范围的行,使用一个索引来选择行。key列显示使用了哪个索引一般就是在你的where语句中出现了between、<、>、in等的查询range 这种范围扫描索引扫描比全表扫描要好,因为它只需要开始于索引的某一点,而结束语另一点,不用扫描全部索引。

  • index:Full Index Scan,index与ALL区别为index类型只遍历索引树。这通常比ALL快,因为索引文件通常比数据文件小。(也就是说虽然all和Index都是读全表,但index是从索引中读取的,而all是从硬盘中读的)

  • all:Full Table Scan,将遍历全表以找到匹配的行

key:实际使用的索引。如果为NULL则没有使用,索引查询中若使用了覆盖索引,则该索引仅出现在key列表中

覆盖索引:就是select的数据列只用从索引中就能够取得,不必读取数据行,MySQL可以利用索引返回select列表中的字段,而不必根据索引再次读取数据文件,换句话说查询列要被所建的索引覆盖

Extra

  • Using filesort 说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。MySQL中无法利用索引完成的排序操作称为“文件排序”,简单来说查询用到了,排序没有用到
  • Using temporary使了用临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于排序order by和分组查询 grgup by。
  • 上面两个优化最好使用覆盖索引
    -Using index 表示相应的select操作中使用了覆盖索引(Covering Index),避免访问了表的数据行,效率不错!如果同时出现using where,表明索引被用来执行索引键值的查找;如果没有同时出现Using where,表明索引用来读取数据而非执行查找动作。
  • Using where表明使用了where过滤
  • Using join buffer :使用了连接缓存
  • impossiable where : where子句的值总是false,不能用来获取任何元组
  • distinct :优化distinct操作,在找到第一匹配的元组后即停止找同样值的动作
  • select tables optimized away:在没有GROUPBY子句的情况下,基于索引优化MIN/MAX操作或者对于MyISAM存储引擎优化COUNT(*)操作,不必等到执行阶段再进行计算,查询执行计划生成的阶段即完成优化。

注意: 如果要使用覆盖索引,一定要注意select列表中只取出需要的列,不可select *
因为如果将所有字段一起做索引会导致索引文件过大,查询性能下降

知道这些后想必你对mysql优化有进一步的了解,接下来看看有哪些情况会使索引失效

2.2、使用索引的注意事项

哪些优化情况下需要创建索引?

  1. 主键自动建立唯一索引
  2. 频繁作为查询条件的字段应该创建索引
  3. 查询中与其它表关联的字段,外键关系建立索引
  4. 单键/组合索引的选择问题,(在高并发下倾向创建组合索引)
  5. 查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度
  6. 查询中统计或者分组字段

哪些情况我们不必要创建它?

  1. 表记录太少。
  2. 经常增删改的列。
  3. 如果某个数据列包啥许多重复的内容,为它建立索引就没有太大的实际效果。

2.3、导致索引失效的原因哪些(索引优化方案)?

  1. 违背最佳左前缀法则(复合索引情况下:索引的顺序和查询的字段尽量保持一致)
  2. 不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描
  3. 存储引擎不能使用索引中范围条件右边的列(range后面的索引都失效)
  4. mysql在使用不等于(!=或者<>)的时候无法使用索引会导致全表扫描
  5. is null,is not null 也无法使用索引
  6. like以通配符开头(“%abc.…)mysql索引失效会变成全表扫描的操作
    解决:如果解决%xxx%索引失效,可以使用复合索引解决,就是查询的字段和 索引的列相同,或者是全文索引
  7. 字符串不加单引号索引失效
  8. 少用or,用它来连接时会索引失效(可以用union all代替)
  9. type等于range类型查询字段后面的素引无效。
  10. 索引单表优化:当 explain的type头中的数据是range的时候 那么这个索引后面的索引会失效
  11. 索引两表优化:左外连接的时候索引要建立在右边的字段上,因为left_join左边全查,右边的根据条件进行查询,所以右边为关键点一定要建立索引
  12. 索引三表优化:同两表
    尽可能减少Join语句中的NestedLoop的循环总次数;“永远用小结果集驱动大的结果集”。
  13. 对於单键索引,尽量选择针对当前query过滤性更好的索引
  14. 在选择组合索引的时候,当前Query中过滤性最好的字段在索引字段顺序中,位置越靠前越好。
  15. 在选择组合索引的时候,尽量选择可以能够包含当前query中的where字句中更多字段的索引
  16. 尽可能通过分析统计信息和调整query的写法来达到选择合适索引的目的

结言:欢迎评论区留言 ,喜欢博文可以给个👍呦,也可以关注我【jar壳虫】,后续会有更多分享🎈🎈

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章