MySQL如何选择合适的索引-trace工具

目录

1、演示SQL

2、MySQL索引的选择

3、trace工具用法

2.1 查看trace开关状态,默认是关闭的 

2.2 开启trace

 2.3 trace用法

2.4 trace 详解 

2.5 trace 索引使用着重字段


trace主要作用:

        MySQL5.6版本后提供的SQL跟踪工具,通过使用trace可以让我们明白优化器如何选择执行计划。

1、演示SQL

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,
  KEY `idx_age` (`age`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 COMMENT='员工记录表';

INSERT INTO `qxb`.`employees`(`id`, `name`, `age`, `position`, `hire_time`) VALUES (1, 'LiLei', 22, 'manager', '2022-09-19 15:19:37');
INSERT INTO `qxb`.`employees`(`id`, `name`, `age`, `position`, `hire_time`) VALUES (2, 'HanMeimei', 23, 'dev', '2022-09-19 15:19:37');
INSERT INTO `qxb`.`employees`(`id`, `name`, `age`, `position`, `hire_time`) VALUES (3, 'Lucy', 23, 'dev', '2022-09-19 15:19:37');

2、MySQL索引的选择

-- 没有走索引
explain select * from employees where name > 'a';
-- 走了索引,并且使用了覆盖索引
explain select id,name,age,position from employees where name > 'a';
-- 走了索引,与条件 name > 'a' 执行的过程不一样
explain select * from employees where name > 'zzz';

 


        对于上面这两种 name>'a' 和 name>'zzz' 的执行结果,mysql最终是否选择走索引或者一张表涉及多个索引,mysql最终如何选择索引,我们可以用trace工具来一查究竟,开启trace工具会影响mysql性能,所以只能临时分析sql使用,用完之后立即关闭。

3、trace工具用法

2.1 查看trace开关状态,默认是关闭的 

-- 查看trace工具状态
show variables like 'optimizer_trace';

 

2.2 开启trace

-- 会话级别临时开启,只在当前会话生效
set session optimizer_trace="enabled=on",end_markers_in_json=on;

--用完之后要关闭该工具

-- 关闭当前会话的trace
set session optimizer_trace="enabled=off";

 2.3 trace用法

-- 使用trace工具
select * from employees where name > 'a';
SELECT * FROM information_schema.OPTIMIZER_TRACE;

MySQL如何选择合适的索引-trace工具_第1张图片

 

2.4 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')"
          }
        ] /* 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": 3, -- 扫描行数
                    "cost": 3.7 -- 查询成本(该数越小,则会被使用)
                  } /* table_scan */,
                  "potential_range_indexes": [ -- 查询可能使用的索引(这里只是可能使用到的索引,并不是最后要使用的索引)
                    {
                      "index": "PRIMARY", -- 主键索引
                      "usable": false, -- 未使用
                      "cause": "not_applicable" -- 原因:不适合
                    },
                    {
                      "index": "idx_name_age_position", -- name,age,position 组合索引
                      "usable": true, -- 使用
                      "key_parts": [ -- 索引字段
                        "name",
                        "age",
                        "position",
                        "id"
                      ] /* key_parts */
                    },
                    {
                      "index": "idx_age", -- age索引
                      "usable": false, -- 未使用
                      "cause": "not_applicable" -- 原因:不适合
                    }
                  ] /* potential_range_indexes */,
				  
                  "setup_range_conditions": [
                  ] /* setup_range_conditions */,
                  "group_index_range": { -- group 用到的索引
                    "chosen": false, -- 未使用
                    "cause": "not_group_by_or_distinct" -- 原因:未使用group by 或者 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": 3, -- 索引扫描行数
                        "cost": 4.61, -- 索引使用成本
                        "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": 3,
                      "access_type": "scan", -- 访问类型:为scan,全表扫描
                      "resulting_rows": 3,
                      "cost": 1.6,
                      "chosen": true -- 确定选择
                    }
                  ] /* considered_access_paths */
                } /* best_access_path */,
                "condition_filtering_pct": 100,
                "rows_for_plan": 3,
                "cost_for_plan": 1.6,
                "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 */
          },
          {
            "refine_plan": [
              {
                "table": "`employees`"
              }
            ] /* refine_plan */
          }
        ] /* steps */
      } /* join_optimization */
    },
    {
      "join_execution": {  -- 第三阶段:SQL执行阶段
        "select#": 1,
        "steps": [
        ] /* steps */
      } /* join_execution */
    }
  ] /* steps */
}

2.5 trace 索引使用着重字段

"rows_estimation": [ -- 预估表的访问成本
              {
                "table": "`employees`",
                "range_analysis": {
                  "table_scan": { -- 全表扫描情况
                    "rows": 3, -- 扫描行数
                    "cost": 3.7 -- 查询成本(该数越小,则会被使用)
                  } /* table_scan */,
                  
                  "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": 3, -- 索引扫描行数
                        "cost": 4.61, -- 索引使用成本
                        "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 */

主要对比各个分析成本(cost字段),索引会使用成本小的

你可能感兴趣的:(MySQL,mysql,数据库)