# Day07 EXPLAIN 执行计划

# 本节关键词

  • explain

# 命令EXPLAIN介绍

语句的执行计划 针对索引应用和优化器算法应用部分信息

  • SQL层估算值,没有实际执行
# 或者 desc
explain format=json select * from test.t100w where k1 = 'zz';
explain format=tree select * from test.t100w where k1 = 'zz';

mysql> explain select * from test.t100w where k1 = 'zz';
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
|  1 | SIMPLE      | t100w | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 997335 |    10.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.46 sec)

- id 从上之下,从大到小,越大优先级越高
- type 应用索引的类型
    ALL     : 全表扫描,没走索引树
    index   : 扫描全部索引树,如果 后面的key是PRI(聚簇索引) 效率就是全表扫描,效率低
    
    range   : 范围扫描索引树,> < >= <= in or,可
    ref             : 辅助索引 等值查询
    eq_ref          : a join b on a.id=b.id ==> 非驱动表的关联列式,主键或唯一键
    const(system)   : 主键等值查询
    NULL            : id=1000000,统计信息直接判断
- possible_keys     : 可能有多个
- key       实际所用索引
- key_len   评估联合索引 idx(a, b, c) 所用的长度,可结合 format=json中的 used_key_parts
            长度?
                参考字符集类型 utf8 utf8mb4
- rows      预估扫描行
- filtered  索引层过滤的百分比,如果不是100%的话,说明需要在engine层过滤,也就是需要加载到内存中过滤
- extra     重点关注 排序时是否额外开销,或者是否有临时表,或者优化器算法
            
            1. Using filesort  大的排序会导致CPU过高
            explain select * from world.city where countrycode='CHN' order by population;

            单列不起作用,建联合索引,去掉 Using filesort 选项
            alter table world.city add index idx_countrycode_population(countrycode, population);

            2. Using tmp  join或子查询会导致这种情况  -> 子查询转换为join
            3. 优化器算法选择

# 是否走索引示例

  • 走全表扫描情况,应避免,需优化
-- 新增索引
alter table world.city add index idx_name(name);

# 没走索引的情况
-- name 未创建索引
desc select * from world.city where name = 'peking';
-- % 开头
desc select * from world.city where name like '%pek%';
-- 否定
desc select * from world.city where name != 'peking';
-- not in
desc select * from world.city where name not in('peking');

-- 统计信息失效、过旧
-- 查询结果集 25% 以上,优化器有可能走全表扫描
-- 隐式转换 库表中 num char(3)
    where num = '10'  --> ref
    where num = 10    --> index 扫描全部索引树
  • 联合索引规范
alter table t1 add index idx_a_b_c(a, b, c);

-- 走索引情况
select * from t1 where a=1 and b=1 and c='xxx'
select * from t1 where c='xxx' and b=1 and a=1  -- 新版本也可以,顺序无所谓

-- b数值,!= 转换为 range 查询
select * from t1 where a=1 and b!=1 and c='xxx'
select * from t1 where a=1 and b>1 and c='xxx'

-- 缺少 a 不走索引
select * from t1 where b=1 and c='xxx'


联合索引最左原则
1. 建索引时,重复值少的放到最左列
2. 查询条件中必须包含最左列条件

# 索引应用场景-数据库慢

  • 数据库慢
1. 应急性的慢
show full processlist; --> 对外连接 --> 慢SQL --> explain SQL --> 优化索引、改写语句
2. 间隙性的慢
slowlog --> 慢SQL --> explain SQL --> 优化索引、改写语句

# 索引应用规范-运维开发规范

  • 建立索引的原则(DBA运维规范)
为了使索引的使用效率更高,在创建索引时,必须考虑在哪些字段上创建索引和创建什么类型的索引
- 必须要有主键,最好数字自增列
- 经常做为where条件列、order bygroup byjoin ondistinct的条件(业务:产品功能+用户行为)
- 联合索引最左原则
- 列值长度较长的索引列,我们建议使用前缀索引
    idx(a,b,c) a ab abc
- 降低索引条目,一方面不要创建没用索引,不常使用的索引清理
    * sys.schema_unused_indexes
    * sys.schema_redundant_indexes
- 索引维护要避开业务繁忙期,建议用pt-osc\gh-ost
- 大量数据导入的时候,先把索引禁用
  • 不走索引的情况(开发规范)
- 没有查询条件,或者查询条件没有建立索引
- 查询的结果集,超过了总数行数25%,优化器觉得就没有必要走索引了
- 索引本身失效,统计信息不真实(过旧) analyze table t1;
- 查询条件使用函数在索引列上,或者对索引列进行运算,运算包括(+-*/!)
    where id - 1 = 2;
- 隐式转换导致索引失效,这一点应当引起重视,也是开发中经常会犯的错误
    telnum char(11) 
    select * from t1 where telnum = '110';select * from t1 where telnum = 110; X
- <>not in 不走索引(辅助索引)
- 单独的><in 有可能走,也有可能不走,和结果集大小有关,尽量结合业务添加limit
- like "%_" 百分号在最前面不走

# MySQL 自优化能力

  • AHI 自适应HASH索引
自动评估"热"的内存索引page,生成HASH索引表
帮助InnoDB快速读取索引页,加快索引读取的效果
相当与索引的索引
  • Change Buffer
Change Buffer功能是临时缓冲辅助索引需要的数据更新,当我们需要查询新insert的数据,会在内存中进行merge(合并)操作,此时辅助索引就是最新的

比如insertupdatedelete数据
1. 对于聚簇索引会立即更新,新增时只是追加数据,索引树影响不大
2. 对于辅助索引,不是实时更新的

根据读写比例,一般2/83/7,如果写比列特别小,可以适当关闭Change Buffer功能

# 优化器算法

  • index hits
select * from t1 use index(col1_index, col2_index)
where col1=1 and col2=2 and col3=3;
  • index_condition_pushdown (ICP)
上次更新: 10/31/2022, 5:19:53 PM