张治峰的博客

MySQL Optimizer 分析(trace 工具)

2021-09-21

trace工具

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

⚠️开启trace工具会影响mysql性能,所以只能临时分析sql使用,用 完之后立即关闭

使用方式

开启 trace
set session optimizer_trace=”enabled=on”,end_markers_in_json=on;

使用

select * from ${table};
SELECT * FROM information_schema.OPTIMIZER_TRACE;

案例分析

示例表参见:索引分析实操: 索引分析实操

我们先来看下面两条语句的结果。
EXPLAIN select * from employees where name > ‘a’;

EXPLAIN select * from employees where name > ‘zzz’;

这两条语句除了参数一模一样 但是第一个走了全表扫描而第二个走的索引搜索,这是为什么呢,接下来让我们使用 trace工具 一探究竟。

  1. 开启trace

    set session optimizer_trace=”enabled=on”,end_markers_in_json=on;

  2. 执行语句

    select * from employees where name > ‘a’ order by position;
    SELECT * FROM information_schema.OPTIMIZER_TRACE;

  3. 将结果字段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": 100036, /*扫描行数*/
    "cost": 20362 /*查询成本*/
    } /* 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": { /*当使用了GROUP BY或DISTINCT时,是否有合适的索引可用。当未使用GROUP BY或DISTINCT时,会显示chosen=false, cause=not_group_by_or_distinct;如使用了GROUP BY或 DISTINCT,但是多表查询时,会显示chosen=false,cause =not_single_table。其他情况下会尝试 分析可用的索引(potential_group_range_indexes)并计算对应的扫描行数及其所需代价*/
    "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": 50018, /*索引扫描行数*/
    "cost": 60023, /*索引适用成本*/
    "chosen": false, /*适用选择索引*/
    "cause": "cost" /*不选择原因 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": 100036,
    "access_type": "scan", /*访问类型scan 为全表扫描 ref 为索引 具体可参考explain中的type字段*/
    "resulting_rows": 100036,
    "cost": 20360,
    "chosen": true, /*确定选择*/
    "use_tmp_table": true
    }
    ] /* considered_access_paths */
    } /* best_access_path */,
    "condition_filtering_pct": 100,
    "rows_for_plan": 100036,
    "cost_for_plan": 20360,
    "sort_cost": 100036,
    "new_cost_for_plan": 120396,
    "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": {
    "select#": 1,
    "steps": [
    {
    "filesort_information": [
    {
    "direction": "asc",
    "table": "`employees`",
    "field": "position"
    }
    ] /* filesort_information */,
    "filesort_priority_queue_optimization": {
    "usable": false,
    "cause": "not applicable (no LIMIT)"
    } /* filesort_priority_queue_optimization */,
    "filesort_execution": [
    ] /* filesort_execution */,
    "filesort_summary": {
    "rows": 100003,
    "examined_rows": 100003,
    "number_of_tmp_files": 30,
    "sort_buffer_size": 262056,
    "sort_mode": "<sort_key, packed_additional_fields>"
    } /* filesort_summary */
    }
    ] /* steps */
    } /* join_execution */
    }
    ] /* steps */
    }

    结论

分析trace结果 适用 全表扫描 cost=20362 适用索引 cost = 60023 所以该条语句选择全表扫描。 简单的一次trace工具使用及mysql对查询的最优路径选择就到此结束了。

Tags: mysql
使用支付宝打赏
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

扫描二维码,分享此文章