MySQL技术內幕

探秘OPTIMIZER_TRACE

2020-06-06  本文已影响0人  秋水畏寒

mysql版本

5.7


参考

本文转自手把手教你认识OPTIMIZER_TRACE,综合博主自身理解,有所修改及重新排版,转载请注明上述链接

mysql官方实例


1. 背景

众所周知,当遇到慢查询sql的时候,需要使用explain关键字来查看该sql的执行计划,从中找出优化点,但是,有时候,光靠explain是不足以得出全面的优化结论的,比如说,执行以下语句:

explain select * from product0 as t1 where t1.name = '产品名称';

结果:


explain结果

不免会有这样的疑问:

这一切,OPTIMIZER_TRACE都将会为我们作出解答。


2. 定义

OPTIMIZER_TRACE,见名知义,优化器跟踪,也就是跟踪语句的解析优化过程,并将跟踪到的信息记录到INFORMATION_SCHEMA.OPTIMIZER_TRACE表中,该功能默认是不开启的,并且默认只记录最后一次结果到INFORMATION_SCHEMA.OPTIMIZER_TRACE表中。

执行以下语句查看相关变量:

show variables like '%optimizer_trace%';

结果:


optimizer_trace相关变量

可以看到相关变量如下:


3. 适用语句


4. Demo

4.1 数据准备

CREATE TABLE t1 (
pk INT, col_int_key INT,
col_varchar_key VARCHAR(1), col_varchar_nokey VARCHAR(1)
);
INSERT INTO t1 VALUES
(10,7,'v','v'),(11,0,'s','s'),(12,9,'l','l'),(13,3,'y','y'),(14,4,'c','c'),
(15,2,'i','i'),(16,5,'h','h'),(17,3,'q','q'),(18,1,'a','a'),(19,3,'v','v'),
(20,6,'u','u'),(21,7,'s','s'),(22,5,'y','y'),(23,1,'z','z'),(24,204,'h','h'),
(25,224,'p','p'),(26,9,'e','e'),(27,5,'i','i'),(28,0,'y','y'),(29,3,'w','w');
CREATE TABLE t2 (
pk INT, col_int_key INT,
col_varchar_key VARCHAR(1), col_varchar_nokey VARCHAR(1),
PRIMARY KEY (pk)
);
INSERT INTO t2 VALUES
(1,4,'b','b'),(2,8,'y','y'),(3,0,'p','p'),(4,0,'f','f'),(5,0,'p','p'),
(6,7,'d','d'),(7,7,'f','f'),(8,5,'j','j'),(9,3,'e','e'),(10,188,'u','u'),
(11,4,'v','v'),(12,9,'u','u'),(13,6,'i','i'),(14,1,'x','x'),(15,5,'l','l'),
(16,6,'q','q'),(17,2,'n','n'),(18,4,'r','r'),(19,231,'c','c'),(20,4,'h','h'),
(21,3,'k','k'),(22,3,'t','t'),(23,7,'t','t'),(24,6,'k','k'),(25,7,'g','g'),
(26,9,'z','z'),(27,4,'n','n'),(28,4,'j','j'),(29,2,'l','l'),(30,1,'d','d'),
(31,2,'t','t'),(32,194,'y','y'),(33,2,'i','i'),(34,3,'j','j'),(35,8,'r','r'),
(36,4,'b','b'),(37,9,'o','o'),(38,4,'k','k'),(39,5,'a','a'),(40,5,'f','f'),
(41,9,'t','t'),(42,3,'c','c'),(43,8,'c','c'),(44,0,'r','r'),(45,98,'k','k'),
(46,3,'l','l'),(47,1,'o','o'),(48,0,'t','t'),(49,189,'v','v'),(50,8,'x','x'),
(51,3,'j','j'),(52,3,'x','x'),(53,9,'k','k'),(54,6,'o','o'),(55,8,'z','z'),
(56,3,'n','n'),(57,9,'c','c'),(58,5,'d','d'),(59,9,'s','s'),(60,2,'j','j'),
(61,2,'w','w'),(62,5,'f','f'),(63,8,'p','p'),(64,6,'o','o'),(65,9,'f','f'),
(66,0,'x','x'),(67,3,'q','q'),(68,6,'g','g'),(69,5,'x','x'),(70,8,'p','p'),
(71,2,'q','q'),(72,120,'q','q'),(73,25,'v','v'),(74,1,'g','g'),(75,3,'l','l'),
(76,1,'w','w'),(77,3,'h','h'),(78,153,'c','c'),(79,5,'o','o'),(80,9,'o','o'),
(81,1,'v','v'),(82,8,'y','y'),(83,7,'d','d'),(84,6,'p','p'),(85,2,'z','z'),
(86,4,'t','t'),(87,7,'b','b'),(88,3,'y','y'),(89,8,'k','k'),(90,4,'c','c'),
(91,6,'z','z'),(92,1,'t','t'),(93,7,'o','o'),(94,1,'u','u'),(95,0,'t','t'),
(96,2,'k','k'),(97,7,'u','u'),(98,2,'b','b'),(99,1,'m','m'),(100,5,'o','o');

4.2 执行

set optimizer_trace = 'enabled=on';
SELECT SUM(alias2.col_varchar_nokey) , alias2.pk AS field2 FROM t1 AS alias1
STRAIGHT_JOIN t2 AS alias2 ON alias2.pk = alias1.col_int_key WHERE alias1.pk
GROUP BY field2 ORDER BY alias1.col_int_key,alias2.pk ;
select * from information_schema.OPTIMIZER_TRACE;
set optimizer_trace = 'enabled=off';

执行结果:


执行结果

5. 解析

各个字段解析如下:

其中,TRACE详细内容如下:

{
    "steps" : [
        {
            "join_preparation" : {
                "select#" : 1,
                "steps" : [
                    {
                        "expanded_query" : "/* select#1 */ select sum(`t2`.`col_varchar_nokey`) AS `SUM(t2.col_varchar_nokey)`,`t2`.`pk` AS `field2` from (`t2` straight_join `t1` on((`t2`.`pk` = `t1`.`col_int_key`))) where (`t2`.`pk` between 10 and 100) group by `field2` order by `t1`.`col_int_key`,`t2`.`pk` limit 0,1000"
                    },
                    {
                        "transformations_to_nested_joins" : {
                            "expanded_query" : "/* select#1 */ select sum(`t2`.`col_varchar_nokey`) AS `SUM(t2.col_varchar_nokey)`,`t2`.`pk` AS `field2` from `t2` straight_join `t1` where ((`t2`.`pk` between 10 and 100) and (`t2`.`pk` = `t1`.`col_int_key`)) group by `field2` order by `t1`.`col_int_key`,`t2`.`pk` limit 0,1000",
                            "transformations" : [
                                "JOIN_condition_to_WHERE",
                                "parenthesis_removal"
                            ]
                        }
                    },
                    {
                        "functional_dependencies_of_GROUP_columns" : {
                            "all_columns_of_table_map_bits" : [
                                0
                            ],
                            "columns" : [
                                "training.t2.pk",
                                "training.t1.col_int_key"
                            ]
                        }
                    }
                ]
            }
        },
        {
            "join_optimization" : {
                "select#" : 1,
                "steps" : [
                    {
                        "condition_processing" : {
                            "condition" : "WHERE",
                            "original_condition" : "((`t2`.`pk` between 10 and 100) and (`t2`.`pk` = `t1`.`col_int_key`))",
                            "steps" : [
                                {
                                    "resulting_condition" : "((`t2`.`pk` between 10 and 100) and multiple equal(`t2`.`pk`, `t1`.`col_int_key`))",
                                    "transformation" : "equality_propagation"
                                },
                                {
                                    "resulting_condition" : "((`t2`.`pk` between 10 and 100) and multiple equal(`t2`.`pk`, `t1`.`col_int_key`))",
                                    "transformation" : "constant_propagation"
                                },
                                {
                                    "resulting_condition" : "((`t2`.`pk` between 10 and 100) and multiple equal(`t2`.`pk`, `t1`.`col_int_key`))",
                                    "transformation" : "trivial_condition_removal"
                                }
                            ]
                        }
                    },
                    {
                        "substitute_generated_columns" : {                      }
                    },
                    {
                        "table_dependencies" : [
                            {
                                "depends_on_map_bits" : [                               ],
                                "map_bit" : 0,
                                "row_may_be_null" : false,
                                "table" : "`t2`"
                            },
                            {
                                "depends_on_map_bits" : [
                                    0
                                ],
                                "map_bit" : 1,
                                "row_may_be_null" : false,
                                "table" : "`t1`"
                            }
                        ]
                    },
                    {
                        "ref_optimizer_key_uses" : [
                            {
                                "equals" : "`t1`.`col_int_key`",
                                "field" : "pk",
                                "null_rejecting" : true,
                                "table" : "`t2`"
                            }
                        ]
                    },
                    {
                        "rows_estimation" : [
                            {
                                "range_analysis" : {
                                    "analyzing_range_alternatives" : {
                                        "analyzing_roworder_intersect" : {
                                            "cause" : "too_few_roworder_scans",
                                            "usable" : false
                                        },
                                        "range_scan_alternatives" : [
                                            {
                                                "chosen" : true,
                                                "cost" : 19.291000,
                                                "index" : "PRIMARY",
                                                "index_dives_for_eq_ranges" : true,
                                                "index_only" : false,
                                                "ranges" : [
                                                    "10 <= pk <= 100"
                                                ],
                                                "rowid_ordered" : true,
                                                "rows" : 91,
                                                "using_mrr" : false
                                            }
                                        ]
                                    },
                                    "chosen_range_access_summary" : {
                                        "chosen" : true,
                                        "cost_for_plan" : 19.291000,
                                        "range_access_plan" : {
                                            "index" : "PRIMARY",
                                            "ranges" : [
                                                "10 <= pk <= 100"
                                            ],
                                            "rows" : 91,
                                            "type" : "range_scan"
                                        },
                                        "rows_for_plan" : 91
                                    },
                                    "group_index_range" : {
                                        "cause" : "not_single_table",
                                        "chosen" : false
                                    },
                                    "potential_range_indexes" : [
                                        {
                                            "index" : "PRIMARY",
                                            "key_parts" : [
                                                "pk"
                                            ],
                                            "usable" : true
                                        }
                                    ],
                                    "setup_range_conditions" : [                                    ],
                                    "table_scan" : {
                                        "cost" : 23.100000,
                                        "rows" : 100
                                    }
                                },
                                "table" : "`t2`"
                            },
                            {
                                "table" : "`t1`",
                                "table_scan" : {
                                    "cost" : 1,
                                    "rows" : 20
                                }
                            }
                        ]
                    },
                    {
                        "considered_execution_plans" : [
                            {
                                "best_access_path" : {
                                    "considered_access_paths" : [
                                        {
                                            "access_type" : "ref",
                                            "chosen" : false,
                                            "index" : "PRIMARY",
                                            "usable" : false
                                        },
                                        {
                                            "access_type" : "range",
                                            "chosen" : true,
                                            "cost" : 37.491000,
                                            "range_details" : {
                                                "used_index" : "PRIMARY"
                                            },
                                            "resulting_rows" : 91,
                                            "rows_to_scan" : 91
                                        }
                                    ]
                                },
                                "condition_filtering_pct" : 100,
                                "cost_for_plan" : 37.491000,
                                "plan_prefix" : [                               ],
                                "rest_of_plan" : [
                                    {
                                        "best_access_path" : {
                                            "considered_access_paths" : [
                                                {
                                                    "access_type" : "scan",
                                                    "buffers_needed" : 1,
                                                    "chosen" : true,
                                                    "cost" : 365,
                                                    "resulting_rows" : 20,
                                                    "rows_to_scan" : 20,
                                                    "using_join_cache" : true
                                                }
                                            ]
                                        },
                                        "chosen" : true,
                                        "condition_filtering_pct" : 10,
                                        "cost_for_plan" : 402.490000,
                                        "plan_prefix" : [
                                            "`t2`"
                                        ],
                                        "rows_for_plan" : 182,
                                        "table" : "`t1`"
                                    }
                                ],
                                "rows_for_plan" : 91,
                                "table" : "`t2`"
                            }
                        ]
                    },
                    {
                        "attaching_conditions_to_tables" : {
                            "attached_conditions_computation" : [                           ],
                            "attached_conditions_summary" : [
                                {
                                    "attached" : "(`t2`.`pk` between 10 and 100)",
                                    "table" : "`t2`"
                                },
                                {
                                    "attached" : "(`t1`.`col_int_key` = `t2`.`pk`)",
                                    "table" : "`t1`"
                                }
                            ],
                            "original_condition" : "((`t1`.`col_int_key` = `t2`.`pk`) and (`t2`.`pk` between 10 and 100))"
                        }
                    },
                    {
                        "clause_processing" : {
                            "clause" : "ORDER BY",
                            "items" : [
                                {
                                    "item" : "`t1`.`col_int_key`"
                                },
                                {
                                    "item" : "`t2`.`pk`"
                                }
                            ],
                            "original_clause" : "`t1`.`col_int_key`,`t2`.`pk`",
                            "resulting_clause" : "`t1`.`col_int_key`,`t2`.`pk`",
                            "resulting_clause_is_simple" : false
                        }
                    },
                    {
                        "clause_processing" : {
                            "clause" : "GROUP BY",
                            "items" : [
                                {
                                    "item" : "`t2`.`pk`"
                                }
                            ],
                            "original_clause" : "`field2`",
                            "resulting_clause" : "`field2`",
                            "resulting_clause_is_simple" : true
                        }
                    },
                    {
                        "refine_plan" : [
                            {
                                "table" : "`t2`"
                            },
                            {
                                "table" : "`t1`"
                            }
                        ]
                    }
                ]
            }
        },
        {
            "join_execution" : {
                "select#" : 1,
                "steps" : [
                    {
                        "creating_tmp_table" : {
                            "tmp_table_info" : {
                                "key_length" : 4,
                                "location" : "memory (heap)",
                                "row_length" : 18,
                                "row_limit_estimate" : 932067,
                                "table" : "intermediate_tmp_table",
                                "unique_constraint" : false
                            }
                        }
                    },
                    {
                        "filesort_execution" : [                        ],
                        "filesort_information" : [
                            {
                                "direction" : "asc",
                                "field" : "col_int_key",
                                "table" : "intermediate_tmp_table"
                            },
                            {
                                "direction" : "asc",
                                "field" : "pk",
                                "table" : "intermediate_tmp_table"
                            }
                        ],
                        "filesort_priority_queue_optimization" : {
                            "cause" : "quicksort_is_cheaper",
                            "chosen" : false,
                            "limit" : 1000,
                            "memory_available" : 262144,
                            "row_size" : 17,
                            "rows_estimate" : 10
                        },
                        "filesort_summary" : {
                            "examined_rows" : 0,
                            "number_of_tmp_files" : 0,
                            "rows" : 0,
                            "sort_buffer_size" : 376,
                            "sort_mode" : "<sort_key, rowid>"
                        }
                    }
                ]
            }
        }
    ]
}

对上述json串进行格式化,可以明显看到以下三个子节点:

主结构

5.1 join_preparation

准备阶段
展开join_preparation,可以看到主结构主要有俩个:

union下join_preparation主结构

5.1.1 expanded_query

{
    "expanded_query":"/* select#1 */ select sum(`t2`.`col_varchar_nokey`) AS `SUM(t2.col_varchar_nokey)`,`t2`.`pk` AS `field2` from (`t2` straight_join `t1` on((`t2`.`pk` = `t1`.`col_int_key`))) where (`t2`.`pk` between 10 and 100) group by `field2` order by `t1`.`col_int_key`,`t2`.`pk` limit 0,1000"
}

可以看到,steps中的expanded_query内容是原本的select *语句转化为具体库名、字段名、列名等后的结果

limit语句应该是数据库可视化软件默认加上的,这里不去追究


5.1.2 transformations_to_nested_joins

{
    "transformations_to_nested_joins":{
        "expanded_query":"/* select#1 */ select sum(`t2`.`col_varchar_nokey`) AS `SUM(t2.col_varchar_nokey)`,`t2`.`pk` AS `field2` from `t2` straight_join `t1` where ((`t2`.`pk` between 10 and 100) and (`t2`.`pk` = `t1`.`col_int_key`)) group by `field2` order by `t1`.`col_int_key`,`t2`.`pk` limit 0,1000",
        "transformations":[
            "JOIN_condition_to_WHERE",
            "parenthesis_removal"
        ]
    }
}

从名字和expanded_query内容来看,是将on子句转化为where子句


5.2 join_optimization

优化阶段
优化内容,相对内容多且复杂,主要分为以下几个部分:


5.2.1 condition_processing

{
    "condition_processing":{
        "condition":"WHERE",
        "original_condition":"((`t2`.`pk` between 10 and 100) and (`t2`.`pk` = `t1`.`col_int_key`))",
        "steps":[
            {
                "resulting_condition":"((`t2`.`pk` between 10 and 100) and multiple equal(`t2`.`pk`, `t1`.`col_int_key`))",
                "transformation":"equality_propagation"
            },
            {
                "resulting_condition":"((`t2`.`pk` between 10 and 100) and multiple equal(`t2`.`pk`, `t1`.`col_int_key`))",
                "transformation":"constant_propagation"
            },
            {
                "resulting_condition":"((`t2`.`pk` between 10 and 100) and multiple equal(`t2`.`pk`, `t1`.`col_int_key`))",
                "transformation":"trivial_condition_removal"
            }
        ]
    }
}

对条件语句wherehaving的处理:

demo中的where字句只命中了equality_propagation一种情况(虽然也有constant_propagationtrivial_condition_removal的部分,但是可以看到resulting_condition并没有发生变化,也就是相当于只命中了equality_propagation一种情况),可以通过以下语句命中全部三种情况:

select * from t1 join t2 on t1.pk=t2.pk+1 where t2.pk = 5 and 1 =1 ;
{
    "condition_processing":{
        "condition":"WHERE",
        "original_condition":"((`t2`.`pk` = 5) and (1 = 1) and (`t1`.`pk` = (`t2`.`pk` + 1)))",
        "steps":[
            {
                "resulting_condition":"((1 = 1) and (`t1`.`pk` = (5 + 1)) and multiple equal(5, `t2`.`pk`))",
                "transformation":"equality_propagation"
            },
            {
                "resulting_condition":"((1 = 1) and (`t1`.`pk` = 6) and multiple equal(5, `t2`.`pk`))",
                "transformation":"constant_propagation"
            },
            {
                "resulting_condition":"((`t1`.`pk` = 6) and multiple equal(5, `t2`.`pk`))",
                "transformation":"trivial_condition_removal"
            }
        ]
    }
}

分析如下:


5.2.2 table_dependencies

{
    "table_dependencies":[
        {
            "depends_on_map_bits":[

            ],
            "map_bit":0,
            "row_may_be_null":false,
            "table":"`t2`"
        },
        {
            "depends_on_map_bits":[
                0
            ],
            "map_bit":1,
            "row_may_be_null":false,
            "table":"`t1`"
        }
    ]
}

在这个结点中我们可以看到表之间的依赖关系:


5.2.3 ref_optimizer_key_uses

{
    "ref_optimizer_key_uses":[
        {
            "equals":"`t1`.`col_int_key`",
            "field":"pk",
            "null_rejecting":true,
            "table":"`t2`"
        }
    ]
}

该节点列出了所有可用的ref类型的索引,如果使用了组合索引的多个部分,则会列出多个结构体。单个结构体会列出单表ref使用的索引及其对应值。


5.2.4 rows_estimation

{
    "rows_estimation":[
        {
            "range_analysis":{
                "analyzing_range_alternatives":{
                    "analyzing_roworder_intersect":{
                        "cause":"too_few_roworder_scans",
                        "usable":false
                    },
                    "range_scan_alternatives":[
                        {
                            "chosen":true,
                            "cost":19.291,
                            "index":"PRIMARY",
                            "index_dives_for_eq_ranges":true,
                            "index_only":false,
                            "ranges":[
                                "10 <= pk <= 100"
                            ],
                            "rowid_ordered":true,
                            "rows":91,
                            "using_mrr":false
                        }
                    ]
                },
                "chosen_range_access_summary":{
                    "chosen":true,
                    "cost_for_plan":19.291,
                    "range_access_plan":{
                        "index":"PRIMARY",
                        "ranges":[
                            "10 <= pk <= 100"
                        ],
                        "rows":91,
                        "type":"range_scan"
                    },
                    "rows_for_plan":91
                },
                "group_index_range":{
                    "cause":"not_single_table",
                    "chosen":false
                },
                "potential_range_indexes":[
                    {
                        "index":"PRIMARY",
                        "key_parts":[
                            "pk"
                        ],
                        "usable":true
                    }
                ],
                "setup_range_conditions":[

                ],
                "table_scan":{
                    "cost":23.1,
                    "rows":100
                }
            },
            "table":"`t2`"
        },
        {
            "table":"`t1`",
            "table_scan":{
                "cost":1,
                "rows":20
            }
        }
    ]
}

rows_estimation展示估算的表扫描行数及其代价,从结构体中可以看到,是以表为单位展示对应结果的

首先看一下t1t1表由于没有索引,所以走的是全表扫描,结构体也比较简单,只有俩个结点:

{
    "table":"`t1`",
    "table_scan":{
        "cost":1,
        "rows":20
    }
}

接下来看一下t2,t2相对复杂很多,主要看一下range_analysis,分为以下几个部分:

5.2.4.1 analyzing_range_alternatives

{
    "analyzing_roworder_intersect":{
        "cause":"too_few_roworder_scans",
        "usable":false
    },
    "range_scan_alternatives":[
        {
            "chosen":true,
            "cost":19.291,
            "index":"PRIMARY",
            "index_dives_for_eq_ranges":true,
            "index_only":false,
            "ranges":[
                "10 <= pk <= 100"
            ],
            "rowid_ordered":true,
            "rows":91,
            "using_mrr":false
        }
    ]
}

分析可选方案的代价,包括

俩个阶段,分别针对不同的情况进行执行代价的分析,从中选择出更优的执行计划。

5.2.4.1.1 analyzing_roworder_intersect

由于示例没有使用index merge,所以在这一段仅仅给出了不使用index merge的原因。如果是语句可以使用index_merge的情况,在该阶段会分析使用index_merge过程中消耗的代价(index_scan_costdisk_sweep_cost等),并汇总merge的代价确认是否选择使用index_merge以及对应使用的索引。

5.2.4.1.2 range_scan_alternatives

range扫描分析针对所有可用于range扫描的索引进行了代价分析,并根据分析结果确认选择使用的索引,包含如下字段:

5.2.4.2 chosen_range_access_summary

{
    "chosen":true,
    "cost_for_plan":19.291,
    "range_access_plan":{
        "index":"PRIMARY",
        "ranges":[
            "10 <= pk <= 100"
        ],
        "rows":91,
        "type":"range_scan"
    },
    "rows_for_plan":91
}

在前一个步骤中分析了各类索引使用的方法及代价,得出了一定的中间结果之后,在summary阶段汇总前一阶段的中间结果确认最后的方案

5.2.4.3 group_index_range

{
    "cause":"not_single_table",
    "chosen":false
}

评估在使用了GROUP BY或者是DISTINCT的时候是否有适合的索引可用:

5.2.4.4 potential_range_indexes

[
    {
        "index":"PRIMARY",
        "key_parts":[
            "pk"
        ],
        "usable":true
    }
]

列出表中所有的索引,并分析其是否可用,结点释义如下:

5.2.4.5 table_scan

同前面t1的释义,全表扫描的行数及代价


5.2.5 considered_execution_plans

[
    {
        "best_access_path":{
            "considered_access_paths":[
                {
                    "access_type":"ref",
                    "chosen":false,
                    "index":"PRIMARY",
                    "usable":false
                },
                {
                    "access_type":"range",
                    "chosen":true,
                    "cost":37.491,
                    "range_details":{
                        "used_index":"PRIMARY"
                    },
                    "resulting_rows":91,
                    "rows_to_scan":91
                }
            ]
        },
        "condition_filtering_pct":100,
        "cost_for_plan":37.491,
        "plan_prefix":[

        ],
        "rest_of_plan":[
            {
                "best_access_path":{
                    "considered_access_paths":[
                        {
                            "access_type":"scan",
                            "buffers_needed":1,
                            "chosen":true,
                            "cost":365,
                            "resulting_rows":20,
                            "rows_to_scan":20,
                            "using_join_cache":true
                        }
                    ]
                },
                "chosen":true,
                "condition_filtering_pct":10,
                "cost_for_plan":402.49,
                "plan_prefix":[
                    "`t2`"
                ],
                "rows_for_plan":182,
                "table":"`t1`"
            }
        ],
        "rows_for_plan":91,
        "table":"`t2`"
    }
]

负责对比各可行计划的代价,选择相对最优的执行计划,由于我们使用了straight join,强制决定了连接顺序,由t2表驱动t1表,所以considered_execution_plans下面只有一个元素,也就代表只有一个可行的执行计划,若是使用join,那么此时就会有俩个元素,由优化器帮我们决定使用哪一个执行计划:

{
    "considered_execution_plans":[
        {
            "best_access_path":{
                "considered_access_paths":[
                    {
                        "access_type":"scan",
                        "chosen":true,
                        "cost":5,
                        "resulting_rows":20,
                        "rows_to_scan":20
                    }
                ]
            },
            "condition_filtering_pct":100,
            "cost_for_plan":5,
            "plan_prefix":[

            ],
            "rest_of_plan":[
                {
                    "best_access_path":{
                        "considered_access_paths":[
                            {
                                "access_type":"eq_ref",
                                "cause":"clustered_pk_chosen_by_heuristics",
                                "chosen":true,
                                "cost":24,
                                "index":"PRIMARY",
                                "rows":1
                            },
                            {
                                "access_type":"range",
                                "cause":"heuristic_index_cheaper",
                                "chosen":false,
                                "range_details":{
                                    "used_index":"PRIMARY"
                                }
                            }
                        ]
                    },
                    "chosen":true,
                    "condition_filtering_pct":100,
                    "cost_for_plan":29,
                    "plan_prefix":[
                        "`t1`"
                    ],
                    "rows_for_plan":20,
                    "table":"`t2`"
                }
            ],
            "rows_for_plan":20,
            "table":"`t1`"
        },
        {
            "best_access_path":{
                "considered_access_paths":[
                    {
                        "access_type":"ref",
                        "chosen":false,
                        "index":"PRIMARY",
                        "usable":false
                    },
                    {
                        "access_type":"range",
                        "chosen":true,
                        "cost":37.491,
                        "range_details":{
                            "used_index":"PRIMARY"
                        },
                        "resulting_rows":91,
                        "rows_to_scan":91
                    }
                ]
            },
            "condition_filtering_pct":100,
            "cost_for_plan":37.491,
            "plan_prefix":[

            ],
            "pruned_by_cost":true,
            "rows_for_plan":91,
            "table":"`t2`"
        }
    ]
}

字段释义:

5.2.5.1 access_type

[
    {
        "access_type":"ref",
        "chosen":false,
        "index":"PRIMARY",
        "usable":false
    },
    {
        "access_type":"range",
        "chosen":true,
        "cost":37.491,
        "range_details":{
            "used_index":"PRIMARY"
        },
        "resulting_rows":91,
        "rows_to_scan":91
    }
]

可以看到,这时候有多条数据,第一条数据使用ref类型的索引,但是该索引不可用("useable":false),所以不被选择("chosen":false);第二条数据使用了range类型的索引,使用了名称为PRIMARY的索引,并且展示了扫描的行数,扫描结果行数以及代价,最后的是在选择比较的结果中是否选择了该方式(chosen

{
    "access_type":"scan",
    "chosen":true,
    "cost":5,
    "resulting_rows":20,
    "rows_to_scan":20
}

该结构体下列出了该表的扫描行数(rows_to_scan),由于这里没有列出index所以能分析出这里的access_type中的scan在这里指的是全表扫描。


5.2.6 attaching_conditions_to_tables

{
    "attaching_conditions_to_tables":{
        "attached_conditions_computation":[

        ],
        "attached_conditions_summary":[
            {
                "attached":"(`t2`.`pk` between 10 and 100)",
                "table":"`t2`"
            },
            {
                "attached":"(`t1`.`col_int_key` = `t2`.`pk`)",
                "table":"`t1`"
            }
        ],
        "original_condition":"((`t1`.`col_int_key` = `t2`.`pk`) and (`t2`.`pk` between 10 and 100))"
    }
}

基于considered_execution_plans中已选执行计划改造原有的where条件句并针对表的增加适当地附加条件便于单表数据的筛选。这部分条件的增改主要是为了便于ICP,但是ICP是否开启并不影响该部分的构造


5.2.7 clause_processing

{
    "clause_processing":{
        "clause":"ORDER BY",
        "items":[
            {
                "item":"`t1`.`col_int_key`"
            },
            {
                "item":"`t2`.`pk`"
            }
        ],
        "original_clause":"`t1`.`col_int_key`,`t2`.`pk`",
        "resulting_clause":"`t1`.`col_int_key`,`t2`.`pk`",
        "resulting_clause_is_simple":false
    }
}

主要是对DISTINCTGROUP BYORDER BY等语句进行优化

{
    "clause_processing":{
        "clause":"ORDER BY",
        "items":[
            {
                "item":"`t1`.`col_int_key`"
            },
            {
                "eq_ref_to_preceding_items":true,
                "item":"`t2`.`pk`"
            }
        ],
        "original_clause":"`t1`.`col_int_key`,`t2`.`pk`",
        "resulting_clause":"`t1`.`col_int_key`",
        "resulting_clause_is_simple":true
    }
}

那么显然,这一块我们需要重点关注的就是original_clauseresulting_clause,通过对比结果,再去查看细节,探究mysql在对应从句上是做了哪些优化


5.2.8 refine_plan

{
    "refine_plan":[
        {
            "table":"`t2`"
        },
        {
            "table":"`t1`"
        }
    ]
}

该阶段展示的是改善之后的执行计划,如图所示只展示了对应的表对象没有其他的字段的话,说明在之前已经确定的执行计划中没有需要再优化的地方,可直接应用


5.3 join_execution

执行阶段
join_execution阶段,如果分析的语句是explain的话,该阶段的代码是空结构体,只有在真正执行语句之后,该阶段的代码才会有具体的步骤过程


5.3.1 creating_tmp_table

{
    "creating_tmp_table":{
        "tmp_table_info":{
            "key_length":4,
            "location":"memory (heap)",
            "row_length":18,
            "row_limit_estimate":932067,
            "table":"intermediate_tmp_table",
            "unique_constraint":false
        }
    }
}

5.3.2 filesort

{
    "filesort_execution":[

    ],
    "filesort_information":[
        {
            "direction":"asc",
            "field":"col_int_key",
            "table":"intermediate_tmp_table"
        },
        {
            "direction":"asc",
            "field":"pk",
            "table":"intermediate_tmp_table"
        }
    ],
    "filesort_priority_queue_optimization":{
        "cause":"quicksort_is_cheaper",
        "chosen":false,
        "limit":1000,
        "memory_available":262144,
        "row_size":17,
        "rows_estimate":10
    },
    "filesort_summary":{
        "examined_rows":0,
        "number_of_tmp_files":0,
        "rows":0,
        "sort_buffer_size":376,
        "sort_mode":"<sort_key, rowid>"
    }
}

在创建临时表之后如果临时表不需要转换为磁盘表的话,即开始对文件排序进行处理


6. 总结

在整个optimizer_trace中我们重点其实就是在跟踪记录TRACE的JSON树,我们通过这棵树中的内容可以具体去分析优化器究竟做了什么事情,进行了哪些选择,是基于什么原因做的选择,选择的结果及依据。这一系列都可以辅助验证我们的一些观点及优化,更好的帮助我们对我们的数据库的实例进行调整。

上一篇下一篇

猜你喜欢

热点阅读