explain索引优化实战


CREATE TABLE `employees` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(24) NOT NULL DEFAULT '' COMMENT '姓名',
`age` int(11) NOT NULL DEFAULT '0' COMMENT '年龄',
`position` varchar(20) NOT NULL DEFAULT '' COMMENT '职位',
`hire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入职时间',
PRIMARY KEY (`id`),
KEY `idx_name_age_position` (`name`,`age`,`position`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='员工记录表';
 
INSERT INTO employees(name,age,position,hire_time) VALUES('LiLei',22,'manager',NOW());
INSERT INTO employees(name,age,position,hire_time) VALUES('HanMeimei', 23,'dev',NOW());
INSERT INTO employees(name,age,position,hire_time) VALUES('Lucy',23,'dev',NOW());
 
-- 插入一些示例数据
drop procedure if exists insert_emp; 
delimiter ;;
create procedure insert_emp()        
begin
  declare i int;                    
  set i=1;                          
  while(i<=100000)do                 
    insert into employees(name,age,position) values(CONCAT('zhuge',i),i,'dev');  
    set i=i+1;                       
  end while;
end;;
delimiter ;
call insert_emp(); 
  我们还是这个表 现在为了做索引查看索引的效率, 我们批量造一波数据
  
select * from employee where name >lihei and age =22 and po='manager'

在这里插入图片描述

这个sql可能走索引 但是没走索引  按道理来说name 会走索引 但是实际上没有走索引,联合索引第一个字段就用范围查找不会走索引,mysql内部可能觉得第一个字段就用范围,结果集应该很大,回表效率不高,还不如就全表扫描

我们可以让 他强制走索引  force index(idx_name_age_position)
虽然使用了强制走索引让联合索引第一个字段范围查找也走索引,扫描的行rows看上去也少了点,但是最终查找效率不一定比全表扫描高,因为回表效率不高
做个小实验
-- 关闭查询缓存 set global query_cache_size=0;   
set global query_cache_type=0; 
-- 执行时间0.333s SELECT * FROM employees WHERE name > 'LiLei'; 
-- 执行时间0.444s SELECT * FROM employees force index(idx_name_age_position) WHERE name > 'LiLei';     
可以使用覆盖索引让他走索引
EXPLAIN SELECT name,age,position FROM employees WHERE name > 'LiLei' AND age = 22 AND position ='manager';      

在这里插入图片描述

4、in和or在表数据量比较大的情况会走索引,在表记录不多的情况下会选择全表扫描

在这里插入图片描述
在这里插入图片描述

看这些都是走的全量索引,
做一个小实验,将employees 表复制一张employees_copy的表,里面保留两三条记录

在这里插入图片描述

EXPLAIN SELECT * FROM employees_copy WHERE name in ('LiLei','HanMeimei','Lucy') AND age = 22 AND position ='manager';       

在这里插入图片描述

EXPLAIN SELECT * FROM employees_copy WHERE (name = 'LiLei' or name = 'HanMeimei') AND age = 22 AND position ='manager';     
      

在这里插入图片描述

like KK% 一般情况都会走索引  不管表中数据大还是小

在这里插入图片描述
在这里插入图片描述

这里给大家补充一个概念,索引下推 (Index Condition Pushdown,ICP), like KK%其实就是用到了索引下推优化
索引下推就是说,上面那个查询在联合索引里匹配到名字是 'LiLei' 开头的索引之后,同时还会在索引里过滤age和position这两个字段,拿着过滤完剩下的索引对应的主键id再回表查整行数据。
 like 一个值 他还会往下继续去推断接下来的字段是否跟我们的条件集相等
mysql 有时候走索引 有时候不走索引,为什么? 或者说 mysql 到底怎么去选择索引的 其实mysql底层有个一个cost成本计算

在这里插入图片描述

=======================================>

在这里插入图片描述

同一个sql 只是条件不一样上一个没有走索引 下一个走了索引 为什么
mysql最终如何选择索引,我们可以用trace工具来一查究竟,开启trace工具会影响mysql性能,所以只能临时分析sql使用,用完之后立即关闭

在这里插入图片描述

mysql> set session optimizer_trace="enabled=on",end_markers_in_json=on;  --开启trace
mysql> select * from employees where name > 'a' order by position;
mysql> SELECT * FROM information_schema.OPTIMIZER_TRACE;
 
查看trace字段:
{
    
    
"steps": [
 {
    
    
   "join_preparation": {
    
        --第一阶段:SQL准备阶段,格式化sql
     "select#": 1,
     "steps": [
       {
    
    
         "expanded_query": "/* select#1 */ select `employees`.`id` AS `id`,`employees`.`name` AS `name`,`employees`.`age` AS `age`,`employees`.`position` AS `position`,`employees`.`hire_time` AS `hire_time` from `employees` where (`employees`.`name` > 'a') order by `employees`.`position`"
       }
     ] /* steps */
   } /* join_preparation */
 },
 {
    
    
   "join_optimization": {
    
        --第二阶段:SQL优化阶段
     "select#": 1,
     "steps": [
       {
    
    
         "condition_processing": {
    
        --条件处理
           "condition": "WHERE",
           "original_condition": "(`employees`.`name` > 'a')",
           "steps": [
             {
    
    
               "transformation": "equality_propagation",
               "resulting_condition": "(`employees`.`name` > 'a')"
             },
             {
    
    
               "transformation": "constant_propagation",
               "resulting_condition": "(`employees`.`name` > 'a')"
             },
             {
    
    
               "transformation": "trivial_condition_removal",
               "resulting_condition": "(`employees`.`name` > 'a')"
             }
           ] /* steps */
         } /* condition_processing */
       },
       {
    
    
         "substitute_generated_columns": {
    
    
         } /* substitute_generated_columns */
       },
       {
    
    
         "table_dependencies": [    --表依赖详情
           {
    
    
             "table": "`employees`",
             "row_may_be_null": false,
             "map_bit": 0,
             "depends_on_map_bits": [
             ] /* depends_on_map_bits */
           }
         ] /* table_dependencies */
       },
       {
    
    
         "ref_optimizer_key_uses": [
         ] /* ref_optimizer_key_uses */
       },
       {
    
    
         "rows_estimation": [    --预估表的访问成本
           {
    
    
             "table": "`employees`",
             "range_analysis": {
    
    
               "table_scan": {
    
         --全表扫描情况
                 "rows": 10123,    --扫描行数
                 "cost": 2054.7    --查询成本
               } /* table_scan */,
               "potential_range_indexes": [    --查询可能使用的索引
                 {
    
    
                   "index": "PRIMARY",    --主键索引
                   "usable": false,
                   "cause": "not_applicable"
                 },
                 {
    
    
                   "index": "idx_name_age_position",    --辅助索引
                   "usable": true,
                   "key_parts": [
                     "name",
                     "age",
                     "position",
                     "id"
                   ] /* key_parts */
                 }
               ] /* potential_range_indexes */,
               "setup_range_conditions": [
               ] /* setup_range_conditions */,
               "group_index_range": {
    
    
                 "chosen": false,
                 "cause": "not_group_by_or_distinct"
               } /* group_index_range */,
               "analyzing_range_alternatives": {
    
        --分析各个索引使用成本
                 "range_scan_alternatives": [
                   {
    
    
                     "index": "idx_name_age_position",
                     "ranges": [
                       "a < name"      --索引使用范围
                     ] /* ranges */,
                     "index_dives_for_eq_ranges": true,
                     "rowid_ordered": false,    --使用该索引获取的记录是否按照主键排序
                     "using_mrr": false,
                     "index_only": false,       --是否使用覆盖索引
                     "rows": 5061,              --索引扫描行数
                     "cost": 6074.2,            --索引使用成本
                     "chosen": false,           --是否选择该索引
                     "cause": "cost"
                   }
                 ] /* range_scan_alternatives */,
                 "analyzing_roworder_intersect": {
    
    
                   "usable": false,
                   "cause": "too_few_roworder_scans"
                 } /* analyzing_roworder_intersect */
               } /* analyzing_range_alternatives */
             } /* range_analysis */
           }
         ] /* rows_estimation */
       },
       {
    
    
         "considered_execution_plans": [
           {
    
    
             "plan_prefix": [
             ] /* plan_prefix */,
             "table": "`employees`",
             "best_access_path": {
    
        --最优访问路径
               "considered_access_paths": [   --最终选择的访问路径
                 {
    
    
                   "rows_to_scan": 10123,
                   "access_type": "scan",     --访问类型:为scan,全表扫描
                   "resulting_rows": 10123,
                   "cost": 2052.6,
                   "chosen": true,            --确定选择
                   "use_tmp_table": true
                 }
               ] /* considered_access_paths */
             } /* best_access_path */,
             "condition_filtering_pct": 100,
             "rows_for_plan": 10123,
             "cost_for_plan": 2052.6,
             "sort_cost": 10123,
             "new_cost_for_plan": 12176,
             "chosen": true
           }
         ] /* considered_execution_plans */
       },
       {
    
    
         "attaching_conditions_to_tables": {
    
    
           "original_condition": "(`employees`.`name` > 'a')",
           "attached_conditions_computation": [
           ] /* attached_conditions_computation */,
           "attached_conditions_summary": [
             {
    
    
               "table": "`employees`",
               "attached": "(`employees`.`name` > 'a')"
             }
           ] /* attached_conditions_summary */
         } /* attaching_conditions_to_tables */
       },
       {
    
    
         "clause_processing": {
    
    
           "clause": "ORDER BY",
           "original_clause": "`employees`.`position`",
           "items": [
             {
    
    
               "item": "`employees`.`position`"
             }
           ] /* items */,
           "resulting_clause_is_simple": true,
           "resulting_clause": "`employees`.`position`"
         } /* clause_processing */
       },
       {
    
    
         "reconsidering_access_paths_for_index_ordering": {
    
    
           "clause": "ORDER BY",
           "steps": [
           ] /* steps */,
           "index_order_summary": {
    
    
             "table": "`employees`",
             "index_provides_order": false,
             "order_direction": "undefined",
             "index": "unknown",
             "plan_changed": false
           } /* index_order_summary */
         } /* reconsidering_access_paths_for_index_ordering */
       },
       {
    
    
         "refine_plan": [
           {
    
    
             "table": "`employees`"
           }
         ] /* refine_plan */
       }
     ] /* steps */
   } /* join_optimization */
 },
 {
    
    
   "join_execution": {
    
        --第三阶段:SQL执行阶段
     "select#": 1,
     "steps": [
     ] /* steps */
   } /* join_execution */
 }
] /* steps */
}
 
结论:全表扫描的成本低于索引扫描,所以mysql最终选择全表扫描
 
mysql> select * from employees where name > 'zzz' order by position;
mysql> SELECT * FROM information_schema.OPTIMIZER_TRACE;
 
查看trace字段可知索引扫描的成本低于全表扫描,所以mysql最终选择索引扫描
 
mysql> set session optimizer_trace="enabled=off";    --关闭trace

猜你喜欢

转载自blog.csdn.net/weixin_43689953/article/details/118658436