介绍
索引是帮助MySQL高效获取数据的排好序的数据结构
常见的索引数据结构
二叉树
右边元素大于左边元素,查找的时间复杂度是O(logN),但是如果元素是依次递增或者递减的情况,会退化成链表,时间复杂度为O(N),跟不加索引一样
红黑树
二叉平衡树,虽然会自动平衡,因为只有两个叉,所以高度不可控,树的高度每增加一层,就是一次随机磁盘IO,性能差
Hash表
key-value结构
查找等值时效率高,不适合范围查找(> in等),数据量大时会存在hash冲突,冲突多时,会退化成链表结构
B-Tree
每个节点都存数据,树高度太大,磁盘IO次数多
B+Tree
只有叶子节点存放数据,非叶子节点不存放数据(可以在一个磁盘数据页中存放更多索引,减少IO次数) 叶子节点依次递增存放数据,叶子节点之间用指针连接(双向链表),方便范围查询;B+树遍历整棵树,只要遍历叶子节点即可。
一个数据页大概16kb,一个索引8Byte,一个地址6Byte
所以一个数据页大概可以存16k/(8+6)Byte = 1170个索引,
假设叶子节点数据为1kb,那一个数据页可以存16行数据,3层的B+树就是1170*1170*16=2千万,所以一个表的容量最好是在2千万行左右,只需要进行3次磁盘IO就可以定位到数据,
查看数据页大小
聚集索引:用主键索引建立数结构,叶子节点存放的是整行数据,如果没有设置主键,Mysql会自己维护一个列row_id(性能差)
非聚集索引:叶子节点存放的是主键索引,查找数据时需要先找到主键索引,再到聚集索引找数据,这就是回表
索引维护:维护索引的有序性,所以建议主键自增,这样在维护时不需要频繁插入节点,效率高
覆盖索引:要查找的数据在一颗索引树上能全部拿到,对于非主键索引就是不需要再执行回表操作
最左前缀原理:索引按照顺序从左到右匹配,因为在进行索引维护时,数据会依次从左到右按照索引排序,如果查找时缺少左边的索引,就无法定位数据,需要全表扫描。
索引下推:利用覆盖索引上存的数据,判断是否满足查询条件,不符合条件的过滤掉,如果满足再进行回表查询,减少回表次数;(5.6后)
Explain工具
使用EXPLAIN关键字可以模拟优化器执行SQL语句,分析你的查询语句或是结构的性能瓶颈,执行查询会返回执行计划的信息,而不是执行这条SQL
注意:如果 from 中包含子查询,仍会执行该子查询,将结果放入临时表中
列介绍
一、id id列的编号是 select 的序列号,嵌套查询时,id列越大select执行优先级越高,id相同则从上往下执行,id为NULL最后执行
二、select_type
| 字段 | 说明 |
|---|---|
simple | 简单查询。查询不包含子查询和union联合查询 |
primary | 复杂查询中最外层的 select |
subquery | 包含在 select 中的子查询 |
derived | 包含在 from 子句中的子查询。MySQL会将结果存放在一个临时表中,也称为派生表 |
union | 在 union 中的第二个和随后的 select |
三、table
表示当前explain语句正在访问哪个表
| 字段 | 说明 |
|---|---|
<derivenN> | 从派生表查询,依赖explain结果中 id=N 的查询 |
<union1,2> | 1和2表示参与 union 的 select 行id |
四、type
表示关联类型或访问类型,Mysql在定位数据行时大概查找范围,查询效率从上到下递减,优化索引最好到range级别,最好到ref
| 字段 | 说明 |
|---|---|
NULL | mysql能够在优化阶段分解查询语句,在执行阶段用不着再访问表或索引 |
system | 查询的表仅有一行,这是const类型的特列,平时不会出现,这个也可以忽略不计 |
const | 常量查询,一定是用到primary key 或者unique key |
eq_ref | 连接查询中,从表只用主键查询 |
ref | 使用二级索引或覆盖索引的前缀 |
range | 使用索引检索范围 |
index | 扫描全部二级索引(遍历二级索引的叶子节点) |
ALL | 全表扫描,扫描聚簇索引的所有叶子节点 |
两个查询都是常量查询,扫描行数是1,效率高
连接查询时,使用从表的
主键(id)作为连接条件时,从表查询时因为使用主键索引,扫描行数为1行,简单查询中不会有该类型
查询语句使用非主键索引(二级索引)或者覆盖索引的部分前缀时,可能会查询到多行,出现该类型
范围扫描通常出现在 in, between ,> ,<, > 等操作中。使用一个索引来检索给定范围的行(如果指定的范围太大,效率低)。
查询时不加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_id是int类型,占4个字节,所以key_len是4
计算规则
- 字符串,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查询的字段都能从这个索引树中获取(使用了覆盖索引),不需要回表
Using where: 使用了where,并且查询的列未使用到索引
Using index condition: 查询的列未被索引覆盖,where条件中是一个前导列的范围
Using temporary: mysql创建一张临时表处理查询,这种情况一般要进行有优化(建立索引)
建立索引后,因为索引树是有序的,所以建立索引后,扫描索引树就可以达到去重的目的
Using filesort:使用外部排序,数据量小时在内存中,否则需要在磁盘中完成优化(建立索引优化)
这里虽然建立了name的索引,但是没有覆盖所有字段,mysql认为走二级索引后还需要回表查询,效率低,所以不走name的索引,使用了外部排序(全表扫描)
索引使用
# 建立索引
KEY `idx_name_age_position` (`name`,`age`,`position`) USING BTREE
1、全值匹配 覆盖索引上的值全部使用到,效率更高
2、最左前缀法则
如果索引了多列,要遵守最左前缀法则。查询从索引的最左前列开始,不跳过索引中的列,下面查询跳过了
name列,不符合最左前缀原则,无法使用索引
3、在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描
建立索引后,使用函数导致索引失效(因为索引树中只存有原始值,没有转换后的值),从而进行全表扫描
4、存储引擎不能使用索引中范围条件右边的列
key_len是78,表示索引只使用了前两列,因为使用范围查询后,剩下的数据无序,没办法使用索引
5、尽量使用覆盖索引,减少 select * 语句
6、mysql在使用不等于(!=或者<>),not in ,not exists 的时候无法使用索引会导致全表扫描
< 小于、 > 大于、 <=、>= 这些,mysql内部优化器会根据检索比例、表大小、结果集等多个因素整体评估是否使用索引
7、is null,is not null 一般情况下也无法使用索引
在索引树中一般null的数据一般会集中存储,数据无序
8、like通配符开头,会使mysql索引失效,变成全表扫描操作
%在前,导致剩下的数据无序,无法使用索引树;使用覆盖索引可以使用到索引树
9、字符串不加单引号索引失效(隐式类型转换)
10、少用or或in,用它查询时,mysql不一定使用索引,mysql内部优化器会根据检索比例、表大小等多个因素整体评 估是否使用索引
11、范围查询优化
没走索引原因:mysql内部优化器会根据检索比例、表大小等多个因素整体评估是否使用索引。比如这个例子,可能是 由于单次数据量查询过大导致优化器最终选择不走索引
优化方法:可以将大的范围拆分成多个小范围
12、联合索引第一个字段使用范围不会走索引,Mysql认为第一个字段走范围,结果集很大,回表效率不高,不如全表扫描(可以用覆盖索引优化)
优化后走索引
like KK% 一般情况都会走索引(数据量大时不走索引)
总结
like KK%相当于=常量,%KK和%KK% 相当于范围