您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
MySql中执行计划如何来的——Optimizer Trace
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
MySql中执行计划如何来的——Optimizer Trace
自猿其说Tech
2023-06-17
IP归属:北京
210浏览
MySQL
数据库
# 1.前言 当谈到MySQL的执行计划时,会有很多同学想:“我就觉得使用其他的执行方案比EXPLAIN语句输出的方案强,凭什么优化器做的决定与我得不一样?”。这个问题在MySQL 5.6之前或许自己很难解决,但是现在MySQL5.6及更高的版本中引入了Optimizer Trace。 # 2.optimizer_trace开启方式及表结构 当下面这行代码执行的时候会将会使用户能够方便地查看优化器生成执行计划的整个过程。 ```sql SET SESSION optimizer_trace=”enabled=on”; ``` optimizer_trace的开关默认是关闭的,我们可以使用下行代码查看optimizer_trace状态。 ``` SHOW variables LIKE'optimizer_trace'; ``` ![](//img1.jcloudcs.com/developer.jdcloud.com/15dbb51d-6194-4875-a19c-1bd299ff858b20230419141852.png) 其中one_line值是用来控制输出格式的,如果值为on,那所有的信息会在同一行中展示(这样并不便于我们阅读),默认为off。当我们的optimizer_trace的enabled为on时,输入想要查看优化过程的查询语句,在该语句执行完之后,就可以到information_schema数据库下的optimizer_trace表中查看详细的执行计划生成过程,当然也可以直接对想要的查询语句使用EXPLAIN。 optimizer_trace表有四列,每列注释我补充在下方create语句中: ``` CREATE TEMPORARY TABLE `OPTIMIZER_TRACE` ( `QUERY` longtext NOT NULL COMMENT '我们输入的查询语句', `TRACE` longtext NOT NULL COMMENT '优化过程的json文本', `MISSING_BYTES_BEYOND_MAX_MEM_SIZE` int(20) NOT NULL DEFAULT '0' COMMENT '执行计划生成 的过程中产生的超出字数限制的文本数', `INSUFFICIENT_PRIVILEGES` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否有权限查看执行 计划的生成过程,0有权限,1无权限' ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ``` # 3.optimizer_trace实践 我们现在根据一个例子来看看optimizer_trace的实践。 ```sql explain select * from ob_shipment.ob_check_m where outbound_no ='ESL48400163536608' and yn=0 and update_user ='jilei18'; SELECT * FROM information_schema.OPTIMIZER_TRACE; ``` 上述sql的执行计划如下: ![](//img1.jcloudcs.com/developer.jdcloud.com/1d8be76c-ec82-46c5-9658-b16a3fbe8f0320230419142002.png) OPTIMIZER_TRACE表中的信息,这里可以注意到MISSING_BYTES_BEYOND_MAX_MEM_SIZE的值为1023,说明TRACE中并没有显示出全部的优化过程: ![](//img1.jcloudcs.com/developer.jdcloud.com/5bfb0b7a-38fa-47a1-b017-7588826e342d20230419141944.png) Query列中的文本是我们执行的Sql语句: ``` /* ApplicationName=DBeaver 21.1.3 - SQLEditor <Script-2.sql> */ explain select * from ob_shipment.ob_check_m where outbound_no ='ESL48400163536608' and yn=0 and update_user ='jilei18' ``` TRACE列是优化的具体过程,其中分析过程需要注意的点在下面代码框中使用#注释的形式给出: ``` { "steps": [ { "join_preparation": { #prepare阶段 "select#": 1, "steps": [ { "expanded_query": "/* select#1 */ select `ob_shipment`.`ob_check_m`.`m_id` AS `m_id`,`ob_shipment`.`ob_check_m`.`wave_no` AS `wave_no`,`ob_shipment`.`ob_check_m`.`wave_type` AS `wave_type`,`ob_shipment`.`ob_check_m`.`outbound_no` AS `outbound_no`,`ob_shipment`.`ob_check_m`.`outbound_type` AS `outbound_type`,`ob_shipment`.`ob_check_m`.`check_type` AS `check_type`,`ob_shipment`.`ob_check_m`.`production_mode` AS `production_mode`,`ob_shipment`.`ob_check_m`.`sku_qty` AS `sku_qty`,`ob_shipment`.`ob_check_m`.`total_qty` AS `total_qty`,`ob_shipment`.`ob_check_m`.`uncheck_qty` AS `uncheck_qty`,`ob_shipment`.`ob_check_m`.`container_no` AS `container_no`,`ob_shipment`.`ob_check_m`.`production_wave_no` AS `production_wave_no`,`ob_shipment`.`ob_check_m`.`carriage_no` AS `carriage_no`,`ob_shipment`.`ob_check_m`.`realcarriage_no` AS `realcarriage_no`,`ob_shipment`.`ob_check_m`.`case_no` AS `case_no`,`ob_shipment`.`ob_check_m`.`rebinwall_no` AS `rebinwall_no`,`ob_shipment`.`ob_check_m`.`locate_sum_qty` AS `locate_sum_qty`,`ob_shipment`.`ob_check_m`.`check_differ_qty_small` AS `check_differ_qty_small`,`ob_shipment`.`ob_check_m`.`supplier_code` AS `supplier_code`,`ob_shipment`.`ob_check_m`.`supplier_name` AS `supplier_name`,`ob_shipment`.`ob_check_m`.`broke_type` AS `broke_type`,`ob_shipment`.`ob_check_m`.`outbound_level` AS `outbound_level`,`ob_shipment`.`ob_check_m`.`outbound_time` AS `outbound_time`,`ob_shipment`.`ob_check_m`.`sort_entry` AS `sort_entry`,`ob_shipment`.`ob_check_m`.`end_time` AS `end_time`,`ob_shipment`.`ob_check_m`.`end_time_attr` AS `end_time_attr`,`ob_shipment`.`ob_check_m`.`send_address` AS `send_address`,`ob_shipment`.`ob_check_m`.`site_no` AS `site_no`,`ob_shipment`.`ob_check_m`.`site_name` AS `site_name`,`ob_shipment`.`ob_check_m`.`sort_slot_no` AS `sort_slot_no`,`ob_shipment`.`ob_check_m`.`valueadd_flag` AS `valueadd_flag`,`ob_shipment`.`ob_check_m`.`package_qty` AS `package_qty`,`ob_shipment`.`ob_check_m`.`send_type` AS `send_type`,`ob_shipment`.`ob_check_m`.`resource` AS `resource`,`ob_shipment`.`ob_check_m`.`platform_no` AS `platform_no`,`ob_shipment`.`ob_check_m`.`pack_table_no` AS `pack_table_no`,`ob_shipment`.`ob_check_m`.`total_weight` AS `total_weight`,`ob_shipment`.`ob_check_m`.`total_volume` AS `total_volume`,`ob_shipment`.`ob_check_m`.`status` AS `status`,`ob_shipment`.`ob_check_m`.`status_lock` AS `status_lock`,`ob_shipment`.`ob_check_m`.`cancel_order_status` AS `cancel_order_status`,`ob_shipment`.`ob_check_m`.`is_shortage` AS `is_shortage`,`ob_shipment`.`ob_check_m`.`check_num` AS `check_num`,`ob_shipment`.`ob_check_m`.`multiple_check` AS `multiple_check`,`ob_shipment`.`ob_check_m`.`org_no` AS `org_no`,`ob_shipment`.`ob_check_m`.`distribute_no` AS `distribute_no`,`ob_shipment`.`ob_check_m`.`warehouse_no` AS `warehouse_no`,`ob_shipment`.`ob_check_m`.`create_user` AS `create_user`,`ob_shipment`.`ob_check_m`.`create_time` AS `create_time`,`ob_shipment`.`ob_check_m`.`update_user` AS `update_user`,`ob_shipment`.`ob_check_m`.`update_time` AS `update_time`,`ob_shipment`.`ob_check_m`.`yn` AS `yn`,`ob_shipment`.`ob_check_m`.`OWNER_NO` AS `OWNER_NO`,`ob_shipment`.`ob_check_m`.`OWNER_NAME` AS `OWNER_NAME`,`ob_shipment`.`ob_check_m`.`batch_no` AS `batch_no`,`ob_shipment`.`ob_check_m`.`check_business_tag` AS `check_business_tag`,`ob_shipment`.`ob_check_m`.`group_no` AS `group_no`,`ob_shipment`.`ob_check_m`.`TRIAL_PRODUCT_FLAG` AS `TRIAL_PRODUCT_FLAG`,`ob_shipment`.`ob_check_m`.`CHECK_MODE` AS `CHECK_MODE`,`ob_shipment`.`ob_check_m`.`check_differ_qty_total` AS `check_differ_qty_total`,`ob_shipment`.`ob_check_m`.`check_differ_qty_medium` AS `check_differ_qty_medium`,`ob_shipment`.`ob_check_m`.`picking_finished` AS `picking_finished`,`ob_shipment`.`ob_check_m`.`cell_no` AS `cell_no`,`ob_shipment`.`ob_check_m`.`rebin_no` AS `rebin_no`,`ob_shipment`.`ob_check_m`.`status_picking` AS `status_picking`,`ob_shipment`.`ob_check_m`.`status_picking_small` AS `status_picking_small`,`ob_shipment`.`ob_check_m`.`status_picking_medium` AS `status_picking_medium`,`ob_shipment`.`ob_check_m`.`status_small` AS `status_small`,`ob_shipment`.`ob_check_m`.`status_medium` AS `status_medium`,`ob_shipment`.`ob_check_m`.`picking_time` AS `picking_time`,`ob_shipment`.`ob_check_m`.`isv_outstore_no` AS `isv_outstore_no`,`ob_shipment`.`ob_check_m`.`pick_type` AS `pick_type`,`ob_shipment`.`ob_check_m`.`sf_ship_no` AS `sf_ship_no`,`ob_shipment`.`ob_check_m`.`isCollectDeliveryInfo` AS `isCollectDeliveryInfo`,`ob_shipment`.`ob_check_m`.`expect_package_qty` AS `expect_package_qty`,`ob_shipment`.`ob_check_m`.`print_shopping_flag` AS `print_shopping_flag`,`ob_shipment`.`ob_check_m`.`product_mode_flag` AS `product_mode_flag`,`ob_shipment`.`ob_check_m`.`schedulebill_code` AS `schedulebill_code`,`ob_shipment`.`ob_check_m`.`uppershelf_time` AS `uppershelf_time`,`ob_shipment`.`ob_check_m`.`mixedorder_type` AS `mixedorder_type`,`ob_shipment`.`ob_check_m`.`child_order_flag` AS `child_order_flag`,`ob_shipment`.`ob_check_m`.`inbound_no` AS `inbound_no`,`ob_shipment`.`ob_check_m`.`production_order_no` AS `production_order_no`,`ob_shipment`.`ob_check_m`.`check_user` AS `check_user`,`ob_shipment`.`ob_check_m`.`check_finish_time` AS `check_finish_time`,`ob_shipment`.`ob_check_m`.`check_style` AS `check_style` from `ob_shipment`.`ob_check_m` where ((`ob_shipment`.`ob_check_m`.`outbound_no` = 'ESL48400163536608') and (`ob_shipment`.`ob_check_m`.`yn` = 0) and (`ob_shipment`.`ob_check_m`.`update_user` = 'jilei18'))" } ] } }, { "join_optimization": { #optimize阶段 "select#": 1, "steps": [ { "condition_processing": {#处理搜索条件 "condition": "WHERE", "original_condition": "((`ob_shipment`.`ob_check_m`.`outbound_no` = 'ESL48400163536608') and (`ob_shipment`.`ob_check_m`.`yn` = 0) and (`ob_shipment`.`ob_check_m`.`update_user` = 'jilei18'))", "steps": [ { "transformation": "equality_propagation",#处理等值转换 "resulting_condition": "((`ob_shipment`.`ob_check_m`.`outbound_no` = 'ESL48400163536608') and (`ob_shipment`.`ob_check_m`.`update_user` = 'jilei18') and multiple equal(0, `ob_shipment`.`ob_check_m`.`yn`))" }, { "transformation": "constant_propagation",#常量传递转换 "resulting_condition": "((`ob_shipment`.`ob_check_m`.`outbound_no` = 'ESL48400163536608') and (`ob_shipment`.`ob_check_m`.`update_user` = 'jilei18') and multiple equal(0, `ob_shipment`.`ob_check_m`.`yn`))" }, { "transformation": "trivial_condition_removal",#去除没用的条件 "resulting_condition": "((`ob_shipment`.`ob_check_m`.`outbound_no` = 'ESL48400163536608') and (`ob_shipment`.`ob_check_m`.`update_user` = 'jilei18') and multiple equal(0, `ob_shipment`.`ob_check_m`.`yn`))" } ] } }, { "substitute_generated_columns": {#去除虚拟生成的列 } }, { "table_dependencies": [#表的依赖信息 { "table": "`ob_shipment`.`ob_check_m`", "row_may_be_null": false, "map_bit": 0, "depends_on_map_bits": [ ] } ] }, { "ref_optimizer_key_uses": [#列出所有可用的ref类型的索引 { "table": "`ob_shipment`.`ob_check_m`", "field": "outbound_no", "equals": "'ESL48400163536608'", "null_rejecting": false } ] }, { "rows_estimation": [#预估不同单表访问方法的访问成本 { "table": "`ob_shipment`.`ob_check_m`", "range_analysis": { "table_scan": {#全表扫描的行数及成本 "rows": 79745, "cost": 19127 }, "potential_range_indexes": [#分析可能使用的索引,此处就是执行计划中的possiable_keys { "index": "PRIMARY",#主键不可用 "usable": false, "cause": "not_applicable" }, { "index": "UK_batch_production",#UK_batch_production索引不可用 "usable": false, "cause": "not_applicable" }, { "index": "idx_update_time",#idx_update_time索引不可用 "usable": false, "cause": "not_applicable" }, { "index": "IDX_status",#IDX_status索引不可用 "usable": false, "cause": "not_applicable" }, { "index": "idx_case_no",#idx_case_no索引不可用 "usable": false, "cause": "not_applicable" }, { "index": "idx_outbound_time",#idx_outbound_time索引不可用 "usable": false, "cause": "not_applicable" }, { "index": "idx_outboundno",#idx_outboundno索引可用 "usable": true, "key_parts": [ "outbound_no", "m_id" ] }, { "index": "idx_wave_no",#idx_wave_no索引不可用 "usable": false, "cause": "not_applicable" }, { "index": "idx_cancel_order_status",#idx_cancel_order_status索引不可用 "usable": false, "cause": "not_applicable" }, { "index": "idx_production_wave_no",#idx_production_wave_no索引不可用 "usable": false, "cause": "not_applicable" }, { "index": "idx_schedulebillcode_uppershelftime",#idx_schedulebillcode_uppershelftime索引不可用 "usable": false, "cause": "not_applicable" }, { "index": "idx_production_orderno",#idx_production_orderno索引不可用 "usable": false, "cause": "not_applicable" }, { "index": "idx_end_time_attr",#idx_end_time_attr索引不可用 "usable": false, "cause": "not_applicable" } ], "setup_range_conditions": [ ], "group_index_range": { "chosen": false, "cause": "not_group_by_or_distinct" }, "analyzing_range_alternatives": {#分析可能使用的索引的成本 "range_scan_alternatives": [ { "index": "idx_outboundno",#使用idx_outboundno索引的成本 "ranges": [ "ESL48400163536608 <= outbound_no <= ESL48400163536608" ], "index_dives_for_eq_ranges": true,#是否使用index_dives "rowid_ordered": true,#使用该索引获取的记录是否按照主键排序 "using_mrr": false,#是否使用mrr "index_only": false,#是否是覆盖索引 "rows": 1,#使用该索引获取的记录条数 "cost": 2.21,#使用该索引花费的成本 "chosen": true#是否选择该索引 "cause": "cost"#该字段为作者添加,当有索引未被使用时会标记未被使用的原因,cost为成本不合理未被选用 } ], "analyzing_roworder_intersect": {#分析使用索引合并的成本 "usable": false, "cause": "too_few_roworder_scans" } }, "chosen_range_access_summary": {#对于上述单表查询ob_check_m最优的方法 "range_access_plan": { "type": "range_scan", "index": "idx_outboundno", "rows": 1, "ranges": [ "ESL48400163536608 <= outbound_no <= ESL48400163536608" ] }, "rows_for_plan": 1, "cost_for_plan": 2.21, "chosen": true } } } ] }, { "considered_execution_plans": [#分析各种可能的执行计划 { "plan_prefix": [ ], "table": "`ob_shipment`.`ob_check_m`", "best_access_path": { "considered_access_paths": [ { "access_type": "ref", "index": "idx_outboundno", "rows": 1, "cost": 1.2, "chosen": true }, { "access_type": "range", "range_details": { "used_index": "idx_outboundno" }, "chosen": false, "cause": "heuristic_index_cheaper" } ] }, "condition_filtering_pct": 5,#下面的数据来自官网示例,作者示例中超出长度的文本无法获取到 "rows_for_plan": 0.05, "cost_for_plan": 8.55, "chosen": true } ] /* rest_of_plan */ } ] /* considered_execution_plans */ }, { "attaching_conditions_to_tables": {#尝试给查询添加一些其他的查询条件 "original_condition": "((`alias2`.`pk` = `alias1`.`col_int_key`) and (0 <> `alias1`.`pk`))", "attached_conditions_computation": [] /* attached_conditions_computation */, "attached_conditions_summary": [ { "table": "`t1` `alias1`", "attached": "((0 <> `alias1`.`pk`) and (`alias1`.`col_int_key` is not null))" }, { "table": "`t2` `alias2`", "attached": "(`alias2`.`pk` = `alias1`.`col_int_key`)" } ] /* attached_conditions_summary */ } /* attaching_conditions_to_tables */ }, { "optimizing_distinct_group_by_order_by": { "simplifying_order_by": { "original_clause": "`alias1`.`col_int_key`,`alias2`.`pk`", "items": [ { "item": "`alias1`.`col_int_key`" }, { "item": "`alias2`.`pk`", "eq_ref_to_preceding_items": true } ] /* items */, "resulting_clause_is_simple": true, "resulting_clause": "`alias1`.`col_int_key`" } /* simplifying_order_by */, "simplifying_group_by": { "original_clause": "`field2`", "items": [ { "item": "`alias2`.`pk`" } ] /* items */, "resulting_clause_is_simple": false, "resulting_clause": "`field2`" } /* simplifying_group_by */ } /* optimizing_distinct_group_by_order_by */ }, { "finalizing_table_conditions": [ { "table": "`t1` `alias1`", "original_table_condition": "((0 <> `alias1`.`pk`) and (`alias1`.`col_int_key` is not null))", "final_table_condition ": "((0 <> `alias1`.`pk`) and (`alias1`.`col_int_key` is not null))" }, { "table": "`t2` `alias2`", "original_table_condition": "(`alias2`.`pk` = `alias1`.`col_int_key`)", "final_table_condition ": null } ] /* finalizing_table_conditions */ }, { "refine_plan": [#再稍加改进执行计划 { "table": "`t1` `alias1`" }, { "table": "`t2` `alias2`" } ] /* refine_plan */ }, { "considering_tmp_tables": [ { "adding_tmp_table_in_plan_at_position": 2, "write_method": "continuously_update_group_row" }, { "adding_sort_to_table": "" } /* filesort */ ] /* considering_tmp_tables */ } ] /* steps */ } /* join_optimization */ }, { "join_execution": {#execute阶段 "select#": 1, "steps": [ { "temp_table_aggregate": { "select#": 1, "steps": [ { "creating_tmp_table": { "tmp_table_info": { "in_plan_at_position": 2, "columns": 3, "row_length": 18, "key_length": 4, "unique_constraint": false, "makes_grouped_rows": true, "cannot_insert_duplicates": false, "location": "TempTable" } /* tmp_table_info */ } /* creating_tmp_table */ } ] /* steps */ } /* temp_table_aggregate */ }, { "sorting_table": "<temporary>", "filesort_information": [ { "direction": "asc", "expression": "`alias1`.`col_int_key`" } ] /* filesort_information */, "filesort_priority_queue_optimization": { "usable": false, "cause": "not applicable (no LIMIT)" } /* filesort_priority_queue_optimization */, "filesort_execution": [] /* filesort_execution */, "filesort_summary": { "memory_available": 262144, "key_size": 9, "row_size": 26, "max_rows_per_buffer": 7710, "num_rows_estimate": 18446744073709551615, "num_rows_found": 8, "num_initial_chunks_spilled_to_disk": 0, "peak_memory_used": 32840, "sort_algorithm": "std::sort", "unpacked_addon_fields": "skip_heuristic", "sort_mode": "<fixed_sort_key, additional_fields>" } /* filesort_summary */ } ] /* steps */ } /* join_execution */ } ] /* steps */ } ``` # 4.总结 上述内容大致分为三个阶段:prepare阶段、optimize阶段、execute阶段,MySQL中基于成本的优化主要在optimize阶段,在单表查询时会主要关注optimize阶段的rows_estimation过程,这个rows_estimation过程分析了多种执行方案的成本耗费,在多表连接查询的时候,我们更多关注considered_execution_plans过程,不过总而言之查询优化器最终会选择成本最低的方案来作为最终的执行计划,即我们使用EXPLAIN语句时显示出的方案。 ------------ 自猿其说Tech-JDL京东物流技术与数据智能部 **作者:籍磊**
原创文章,需联系作者,授权转载
上一篇:初识机器学习(上)
下一篇:antv-x6使用及总结
相关文章
京东智联云MySQL数据库如何保障数据的可靠性?
一条sql了解MYSQL的架构设计
DBeaver免费开源的数据库客户端工具
自猿其说Tech
文章数
426
阅读量
2161455
作者其他文章
01
深入JDK中的Optional
本文将从Optional所解决的问题开始,逐层解剖,由浅入深,文中会出现Optioanl方法之间的对比,实践,误用情况分析,优缺点等。与大家一起,对这项Java8中的新特性,进行理解和深入。
01
Taro小程序跨端开发入门实战
为了让小程序开发更简单,更高效,我们采用 Taro 作为首选框架,我们将使用 Taro 的实践经验整理了出来,主要内容围绕着什么是 Taro,为什么用 Taro,以及 Taro 如何使用(正确使用的姿势),还有 Taro 背后的一些设计思想来进行展开,让大家能够对 Taro 有个完整的认识。
01
Flutter For Web实践
Flutter For Web 已经发布一年多时间,它的发布意味着我们可以真正地使用一套代码、一套资源部署整个大前端系统(包括:iOS、Android、Web)。渠道研发组经过一段时间的探索,使用Flutter For Web技术开发了移动端可视化编程平台—Flutter乐高,在这里希望和大家分享下使用Flutter For Web实践过程和踩坑实践
01
配运基础数据缓存瘦身实践
在基础数据的常规能力当中,数据的存取是最基础也是最重要的能力,为了整体提高数据的读取能力,缓存技术在基础数据的场景中得到了广泛的使用,下面会重点展示一下配运组近期针对数据缓存做的瘦身实践。
自猿其说Tech
文章数
426
阅读量
2161455
作者其他文章
01
深入JDK中的Optional
01
Taro小程序跨端开发入门实战
01
Flutter For Web实践
01
配运基础数据缓存瘦身实践
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号