Optimizer trace的基本使用,以及优化过程中的含义
[TOC]
对于 MySQL 5.6 以及之前的版本来说,查询优化器就像是一个黑盒子一样,你只能通过 EXPLAIN 语句查看到最后优化器决定使用的执行计划,却无法知道它为什么做这个决策。
在 MySQL 5.6 以及之后的版本中,MySQL 提出了一个 optimizer trace 的功能,这个功能可以让我们方便的查看优化器生成执行计划的整个过程。
Optimizer trace 并不是自动就会默认开启的,开启 trace 多多少少都会有一些额外的工作要做,因此并不建议一直开着。但 trace 属于轻量级的工具,开启和关闭都非常简便,对系统的影响也微乎其微。而且支持在 session 中开启,不影响其它 session,对系统的影响降到了最低。
optimizer trace
状态> show variables like '%optimizer_trace%';
+----------------------------+--------------------------------------------------------------------------+
|Variable_name |Value |
+----------------------------+--------------------------------------------------------------------------+
|optimizer_trace |enabled=off,one_line=off |
|optimizer_trace_features |greedy_search=on,range_optimizer=on,dynamic_range=on,repeated_subselect=on|
|optimizer_trace_limit |1 |
|optimizer_trace_max_mem_size|16384 |
|optimizer_trace_offset |-1 |
+----------------------------+--------------------------------------------------------------------------+
optimizer trace
功能SET optimizer_trace="enabled=on";
SET optimizer_trace_limit=10;
SET optimizer_trace_offset=10;
SET optimizer_trace_max_mem_size = 32768;
注意:在这里设置了optimizer_trace_limit为10主要是因为在使用DataGrip
时会自动插入多条数据影响查看
SELECT * FROM information_schema.OPTIMIZER_TRACE;
SET optimizer_trace="enabled=off";
SET optimizer_trace_limit=1;
SET optimizer_trace_offset=-1;
SET optimizer_trace_max_mem_size = 16384;
TRACE文本分析:
{
"steps": [
{
"join_preparation": { # prepare阶段
"select#": 1,
"steps": [
{
"expanded_query": "/* select#1 */ select sql_no_cache `item_sale_summary`.`ent_id` AS `ent_id`,`item_sale_summary`.`region_code` AS `region_code`,ceiling((count(distinct `item_sale_summary`.`item_code`,`item_sale_summary`.`barcode`,date_format(`item_sale_summary`.`trans_date`,'%Y-%m-%d')) / ((to_days('2021-12-05') - to_days('2021-11-05')) + 1))) AS `sku_item_sale` from `item_sale_summary` where ((`item_sale_summary`.`trans_date` between '2021-11-01' and '2021-11-15') and (`item_sale_summary`.`ent_id` = 1747964630024192400)) group by `item_sale_summary`.`ent_id`,`item_sale_summary`.`region_code`"
}
]
}
},
{
"join_optimization": { # optimize阶段
"select#": 1,
"steps": [
{
"condition_processing": { # 处理搜索条件
"condition": "WHERE",
# 原始搜索条件
"original_condition": "((`item_sale_summary`.`trans_date` between '2021-11-01' and '2021-11-15') and (`item_sale_summary`.`ent_id` = 1747964630024192400))",
"steps": [
{
"transformation": "equality_propagation", # 等值传递转换
"resulting_condition": "((`item_sale_summary`.`trans_date` between '2021-11-01' and '2021-11-15') and multiple equal(1747964630024192400, `item_sale_summary`.`ent_id`))"
},
{
"transformation": "constant_propagation", # 常量传递转换
"resulting_condition": "((`item_sale_summary`.`trans_date` between '2021-11-01' and '2021-11-15') and multiple equal(1747964630024192400, `item_sale_summary`.`ent_id`))"
},
{
"transformation": "trivial_condition_removal", # 去除没用的条件
"resulting_condition": "((`item_sale_summary`.`trans_date` between '2021-11-01' and '2021-11-15') and multiple equal(1747964630024192400, `item_sale_summary`.`ent_id`))"
}
]
}
},
{
"substitute_generated_columns": { # 替换虚拟生成列
}
},
{
"table_dependencies": [ # 表的依赖信息
{
"table": "`item_sale_summary`",
"row_may_be_null": false,
"map_bit": 0,
"depends_on_map_bits": [
]
}
]
},
{
"ref_optimizer_key_uses": [
{
"table": "`item_sale_summary`",
"field": "ent_id",
"equals": "1747964630024192400",
"null_rejecting": false
},
{
"table": "`item_sale_summary`",
"field": "ent_id",
"equals": "1747964630024192400",
"null_rejecting": false
}
]
},
{
"rows_estimation": [ # 预估不同单表访问方法的访问成本
{
"table": "`item_sale_summary`",
"range_analysis": {
"table_scan": { # 全表扫描的行数以及成本
"rows": 4245934,
"cost": 944293
},
"potential_range_indexes": [ # 分析可能使用的索引
{
"index": "unique_index",
"usable": true, # 可能被使用
"key_parts": [
"trans_date",
"ent_id",
"region_code",
"channel_keyword",
"item_code",
"barcode"
]
},
{
"index": "idx_ent_date_region",
"usable": true,
"key_parts": [
"ent_id",
"trans_date",
"region_code"
]
},
{
"index": "idx_saled_item",
"usable": true,
"key_parts": [
"ent_id",
"region_code",
"item_code",
"barcode",
"trans_date",
"channel_keyword"
]
}
],
"best_covering_index_scan": {
"index": "unique_index",
"cost": 1.11e6,
"chosen": false,
"cause": "cost"
},
"setup_range_conditions": [
],
"group_index_range": {
"chosen": false,
"cause": "not_applicable_aggregate_function"
},
"analyzing_range_alternatives": { # 分析各种可能使用的索引的成本
"range_scan_alternatives": [
{
# 使用unique_index的成本分析
"index": "unique_index",
"ranges": [
"0x61cb0f <= trans_date <= 0x6fcb0f"
],
"index_dives_for_eq_ranges": true, # 是否使用index dive
"rowid_ordered": false, # 使用该索引获取的记录是否按照主键排序
"using_mrr": false, # 是否使用mrr
"index_only": true, # 是否是索引覆盖访问
"rows": 1, # 使用该索引获取的记录条数
"cost": 1.21, # 使用该索引的成本
"chosen": true # 是否选择该索引
},
{
"index": "idx_ent_date_region",
"ranges": [
"1747964630024192400 <= ent_id <= 1747964630024192400 AND 0x61cb0f <= trans_date <= 0x6fcb0f"
],
"index_dives_for_eq_ranges": true,
"rowid_ordered": false,
"using_mrr": false,
"index_only": false,
"rows": 1,
"cost": 2.21,
"chosen": false,
"cause": "cost" # 因为成本太大所以不选择该索引
},
{
"index": "idx_saled_item",
"ranges": [
"1747964630024192400 <= ent_id <= 1747964630024192400"
],
"index_dives_for_eq_ranges": true,
"rowid_ordered": false,
"using_mrr": false,
"index_only": true,
"rows": 753872,
"cost": 197892,
"chosen": false,
"cause": "cost"
}
],
"analyzing_roworder_intersect": {
# 分析使用索引合并的成本
"usable": false,
"cause": "too_few_roworder_scans"
}
},
"chosen_range_access_summary": {
# 对于上述单表查询最优的访问方法
"range_access_plan": {
"type": "range_scan",
"index": "unique_index",
"rows": 1,
"ranges": [
"0x61cb0f <= trans_date <= 0x6fcb0f"
]
},
"rows_for_plan": 1,
"cost_for_plan": 1.21,
"chosen": true
}
}
}
]
},
{
# 分析各种可能的执行计划
"considered_execution_plans": [
{
"plan_prefix": [
],
"table": "`item_sale_summary`",
"best_access_path": {
"considered_access_paths": [
{
"access_type": "ref",
"index": "idx_ent_date_region",
"rows": 710.55,
"cost": 852.66,
"chosen": true
},
{
"access_type": "ref",
"index": "idx_saled_item",
"rows": 753872,
"cost": 197892,
"chosen": false
},
{
"rows_to_scan": 1,
"access_type": "range",
"range_details": {
"used_index": "unique_index"
},
"resulting_rows": 0.1776,
"cost": 1.41,
"chosen": true,
"use_tmp_table": true
}
]
},
"condition_filtering_pct": 100,
"rows_for_plan": 0.1776,
"cost_for_plan": 1.41,
"sort_cost": 0.1776,
"new_cost_for_plan": 1.5876,
"chosen": true
}
]
},
{
# 尝试给查询添加一些其他的查询条件
"attaching_conditions_to_tables": {
"original_condition": "((`item_sale_summary`.`ent_id` = 1747964630024192400) and (`item_sale_summary`.`trans_date` between '2021-11-01' and '2021-11-15'))",
"attached_conditions_computation": [
],
"attached_conditions_summary": [
{
"table": "`item_sale_summary`",
"attached": "((`item_sale_summary`.`ent_id` = 1747964630024192400) and (`item_sale_summary`.`trans_date` between '2021-11-01' and '2021-11-15'))"
}
]
}
},
{
"clause_processing": {
"clause": "GROUP BY",
"original_clause": "`item_sale_summary`.`ent_id`,`item_sale_summary`.`region_code`",
"items": [
{
"item": "`item_sale_summary`.`ent_id`",
"equals_constant_in_where": true
},
{
"item": "`item_sale_summary`.`region_code`"
}
],
"resulting_clause_is_simple": true,
"resulting_clause": "`item_sale_summary`.`region_code`"
}
},
{
"reconsidering_access_paths_for_index_ordering": {
"clause": "GROUP BY",
"steps": [
],
"index_order_summary": {
"table": "`item_sale_summary`",
"index_provides_order": false,
"order_direction": "undefined",
"index": "unique_index",
"plan_changed": false
}
}
},
{
# 再稍稍的改进一下执行计划
"refine_plan": [
{
"table": "`item_sale_summary`"
}
]
},
{
"creating_tmp_table": {
"tmp_table_info": {
"table": "intermediate_tmp_table",
"row_length": 344,
"key_length": 349,
"unique_constraint": false,
"location": "memory (heap)",
"row_limit_estimate": 48770
}
}
}
]
}
},
{
# execute阶段
"join_execution": {
"select#": 1,
"steps": [
{
"filesort_information": [
{
"direction": "asc",
"table": "`item_sale_summary`",
"field": "region_code"
}
],
"filesort_priority_queue_optimization": {
"usable": false,
"cause": "not applicable (no LIMIT)"
},
"filesort_execution": [
],
"filesort_summary": {
"rows": 0,
"examined_rows": 0,
"number_of_tmp_files": 0,
"sort_buffer_size": 261632,
"sort_mode": "<sort_key, packed_additional_fields>"
}
}
]
}
}
]
}
想要更加具体的了解其中的含义可参考Chapter 8 Tracing the Optimizer
以上为optimizer trace
的简单使用,使用好该功能可以有效帮助我们了解MySQL的优化过程。
整体优化过程虽然看起来杂乱,但主要分成了以下三个部分
的基于成本的优化主要集中在 optimize 阶段,对于单表查询来说,我们主要关注 optimize 阶段的 “rows_estimation” 这个过程,这个过程深入分析了对单表查询的各种执行方案的成本;对于多表连接查询来说,我们更多需要关注 “considered_execution_plans” 这个过程,这个过程里会写明各种不同的连接方式所对应的成本。反正优化器最终会选择成本最低的那种方案来作为最终的执行计划,也就是我们使用 EXPLAIN 语句所展现出的那种方案。