前言:相信大家面试的时候肯定被问过这个问题,但很多都是因为不够深入,或者了解片面而导致好的机会和你失之交臂,其实这个问题答得好是非常加分的,好了不多说了开始…
目录
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、索引是把双刃剑
优点
- 通过创建唯一索引,可以保证数据库每一行数据的唯一性
- 可以大大提高查询速度
- 可以加速表与表的连接
- 可以显著的减少查询中分组和排序的时间
缺点
- 创建索引和维护索引需要时间,而且数据量越大时间越长
- 创建索引需要占据磁盘的空间,如果有大量的索引,可能比数据文件更快达到最大文件尺寸
- 当对表中的数据进行增加,修改,删除的时候,索引也要同时进行维护,降低了数据的维护速度
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、使用索引的注意事项
哪些优化情况下需要创建索引?
- 主键自动建立唯一索引
- 频繁作为查询条件的字段应该创建索引
- 查询中与其它表关联的字段,外键关系建立索引
- 单键/组合索引的选择问题,(在高并发下倾向创建组合索引)
- 查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度
- 查询中统计或者分组字段
哪些情况我们不必要创建它?
- 表记录太少。
- 经常增删改的列。
- 如果某个数据列包啥许多重复的内容,为它建立索引就没有太大的实际效果。
2.3、导致索引失效的原因哪些(索引优化方案)?
- 违背最佳左前缀法则(复合索引情况下:索引的顺序和查询的字段尽量保持一致)
- 不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描
- 存储引擎不能使用索引中范围条件右边的列(range后面的索引都失效)
- mysql在使用不等于(!=或者<>)的时候无法使用索引会导致全表扫描
- is null,is not null 也无法使用索引
- like以通配符开头(“%abc.…)mysql索引失效会变成全表扫描的操作
解决
:如果解决%xxx%索引失效,可以使用复合索引解决,就是查询的字段和 索引的列相同,或者是全文索引 - 字符串不加单引号索引失效
- 少用or,用它来连接时会索引失效(可以用union all代替)
- type等于range类型查询字段后面的素引无效。
- 索引单表优化:当 explain的type头中的数据是range的时候 那么这个索引后面的索引会失效
- 索引两表优化:左外连接的时候索引要建立在右边的字段上,因为left_join左边全查,右边的根据条件进行查询,所以右边为关键点一定要建立索引
- 索引三表优化:同两表
尽可能减少Join语句中的NestedLoop的循环总次数;“永远用小结果集驱动大的结果集”。
- 对於单键索引,尽量选择针对当前query过滤性更好的索引
- 在选择组合索引的时候,当前Query中过滤性最好的字段在索引字段顺序中,位置越靠前越好。
- 在选择组合索引的时候,尽量选择可以能够包含当前query中的where字句中更多字段的索引
- 尽可能通过分析统计信息和调整query的写法来达到选择合适索引的目的
结言:欢迎评论区留言 ,喜欢博文可以给个👍呦,也可以关注我【jar壳虫】,后续会有更多分享🎈🎈