目录
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可以让我们明白优化器如何选择执行计划。
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');
-- 没有走索引 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使用,用完之后立即关闭。
-- 查看trace工具状态 show variables like 'optimizer_trace';
-- 会话级别临时开启,只在当前会话生效
set session optimizer_trace="enabled=on",end_markers_in_json=on;
--用完之后要关闭该工具
-- 关闭当前会话的trace
set session optimizer_trace="enabled=off";
-- 使用trace工具 select * from employees where name > 'a'; SELECT * FROM information_schema.OPTIMIZER_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 */
}
"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字段),索引会使用成本小的