optimizer trace 查看Mysql如何选择执行计划的过程

前面几篇讲了Mysql基于成本的优化,也看了执行计划,今天我们来看看Mysql提供的更详细的执行计划生成过程。

optimizer trace由来:

1、我们平时查看SQL的执行计划,但是执行计划是相对粗略的,只告诉你使用了什么索引,采用了什么方式。但是没有告诉你它为什么选择这种查询方式。
2、所以,在MySQL 5.6之后,提供了个optimizer trace功能,可以查看Mysql详细的查询计划生成过程。

optimizer trace的使用方式:

  1. 打开optimizer trace功能 (默认情况下它是关闭的):
    SET optimizer_trace=“enabled=on”;
  2. 这里输入你自己的查询语句
    SELECT …;
  3. 从OPTIMIZER_TRACE表中查看上一个查询的优化过程
    SELECT * FROM information_schema.OPTIMIZER_TRACE;
  4. 可能你还要观察其他语句执行的优化过程,重复上边的第2、3步
  5. 当你停止查看语句的优化过程时,把optimizer trace功能关闭
    SET optimizer_trace=“enabled=off”;

optimizer trace实践:

我们可以把optimizer_trace开关打开:

SET optimizer_trace="enabled=on";

查看修改情况:

show variables like '%optimizer_trace%';

在这里插入图片描述

查询一条sql之后,如:select * from test_index where key1 > 'key120' and key1 < 'key129';,发现SELECT * FROM information_schema.OPTIMIZER_TRACE;
在这里插入图片描述

是IDEA的一条SQL,SET SQL_SELECT_LIMIT=501,因为optimizer_trace默认只显示最近一条SQL的执行计划。
正因为我们使用的是IDEA的数据库插件,它会在我们查询前后增添一些SQL,影响了我们查看optimizer_trace,我们观察到optimizer_trace_limitoptimizer_trace_offset两个变量,默认值分别为1和-1,表示只保留一条执行计划,同时指向最近的一条记录,我将optimizer_trace_limitoptimizer_trace_offset都修改为5,表示保留最近5条记录,显示前5条。

再次查询成功:

select * from test_index where key1 > 'key120' and key1 < 'key129';
SELECT * FROM information_schema.OPTIMIZER_TRACE;

在这里插入图片描述

我们来看一下执行计划生成过程:

{
  "steps": [
    {
      "join_preparation": {
        "select#": 1,
        "steps": [
          {
			// 将select * 转换为 select 每个列
            "expanded_query": "/* select#1 */ select `test_index`.`id` AS `id`,`test_index`.`key1` AS `key1`,`test_index`.`key2` AS `key2`,`test_index`.`key3` AS `key3`,`test_index`.`key_part1` AS `key_part1`,`test_index`.`key_part2` AS `key_part2`,`test_index`.`key_part3` AS `key_part3`,`test_index`.`common_field` AS `common_field` from `test_index` where ((`test_index`.`key1` > 'key120') and (`test_index`.`key1` < 'key129'))"
          }
        ]
      }
    },
    {
      "join_optimization": {
        "select#": 1,
        "steps": [
          {
			// 对查询条件进行处理
            "condition_processing": {
				// 原始的查询条件
              "condition": "WHERE",
              "original_condition": "((`test_index`.`key1` > 'key120') and (`test_index`.`key1` < 'key129'))",
              "steps": [
                {
   					// 等值转换
                  "transformation": "equality_propagation",
                  "resulting_condition": "((`test_index`.`key1` > 'key120') and (`test_index`.`key1` < 'key129'))"
                },
                {
   					// 常量传递转换
                  "transformation": "constant_propagation",
                  "resulting_condition": "((`test_index`.`key1` > 'key120') and (`test_index`.`key1` < 'key129'))"
                },
                {
					// 琐碎的条件 (去除没用的条件)
                  "transformation": "trivial_condition_removal",
                  "resulting_condition": "((`test_index`.`key1` > 'key120') and (`test_index`.`key1` < 'key129'))"
                }
              ]
            }
          },
          {
			-- 替换虚拟生成列
            "substitute_generated_columns": {
            }
          },
          {
            "table_dependencies": [
              {
                "table": "`test_index`",
                "row_may_be_null": false,
                "map_bit": 0,
                "depends_on_map_bits": [
                ]
              }
            ]
          },
          {
            "ref_optimizer_key_uses": [
            ]
          },
          {
				// 预估不同单表访问方法的访问成本
            "rows_estimation": [
              {
                "table": "`test_index`",
                "range_analysis": {
					// 全表扫描的行数以及成本
                  "table_scan": {
                    "rows": 99316,
                    "cost": 119181
                  },
                  "potential_range_indexes": [
                    {
						// 不能使用主键索引
                      "index": "PRIMARY",
                      "usable": false,
                      "cause": "not_applicable"
                    },
                    {
					// 不能使用idx_key2索引
                      "index": "idx_key2",
                      "usable": false,
                      "cause": "not_applicable"
                    },
                    {
						// 不能使用idx_key3索引
                      "index": "idx_key3",
                      "usable": false,
                      "cause": "not_applicable"
                    },
                    {
						// 不能使用idx_key_part索引
                      "index": "idx_key_part",
                      "usable": false,
                      "cause": "not_applicable"
                    },
                    {
						// 可以使用idx_key1索引
                      "index": "idx_key1",
                      "usable": true,
						// 二级索引由 二级索引列+主键索引构成
                      "key_parts": [
                        "key1",
                        "id"
                      ]
                    }
                  ],
                  "setup_range_conditions": [
                  ],
               // 没有group_by或distinct,不选择通过归集索引的方式
                  "group_index_range": {
                    "chosen": false,
                    "cause": "not_group_by_or_distinct"
                  },
					// 分析各种可能使用的索引的成本
                  "analyzing_range_alternatives": {
					// 通过索引range扫描的方案
                    "range_scan_alternatives": [
                      {
                        "index": "idx_key1",
                        "ranges": [
                          "key120 < key1 < key129"
                        ],
                        "index_dives_for_eq_ranges": true,  # 是否使用index dive
                        "rowid_ordered": false,  # 使用该索引获取的记录是否按照主键排序
                        "using_mrr": false,  # 是否使用mrr
                        "index_only": false,  # 是否是索引覆盖访问
                        "rows": 18254,  # 使用该索引获取的记录条数
                        "cost": 21906,  # 使用该索引的成本
                        "chosen": true  # 是否选择该索引
                      }
                    ],
 					# 分析使用索引合并的成本
                    "analyzing_roworder_intersect": {
                      "usable": false,
                      "cause": "too_few_roworder_scans"
                    }
                  },
 					# 对于上述单表查询最优的访问方法
                  "chosen_range_access_summary": {
                    "range_access_plan": {
                      "type": "range_scan",
                      "index": "idx_key1",
                      "rows": 18254,
                      "ranges": [
                        "key120 < key1 < key129"
                      ]
                    },
                    "rows_for_plan": 18254,
                    "cost_for_plan": 21906,
                    "chosen": true
                  }
                }
              }
            ]
          },
          {
			# 分析各种可能的执行计划
			 #(对多表查询这可能有很多种不同的方案,单表查询的方案上边已经分析过了,直接选取就好)
            "considered_execution_plans": [
              {
                "plan_prefix": [
                ],
                "table": "`test_index`",
                "best_access_path": {
                  "considered_access_paths": [
                    {
                      "rows_to_scan": 18254,
                      "access_type": "range",
                      "range_details": {
                        "used_index": "idx_key1"
                      },
                      "resulting_rows": 18254,
                      "cost": 25557,
                      "chosen": true
                    }
                  ]
                },
                "condition_filtering_pct": 100,
                "rows_for_plan": 18254,
                "cost_for_plan": 25557,
                "chosen": true
              }
            ]
          },
          {
				# 尝试给查询添加一些其他的查询条件
            "attaching_conditions_to_tables": {
              "original_condition": "((`test_index`.`key1` > 'key120') and (`test_index`.`key1` < 'key129'))",
              "attached_conditions_computation": [
                {
                  "table": "`test_index`",
					# 重新确认索引的使用,原因是low_limit,查询很少的数据
                  "rechecking_index_usage": {
                    "recheck_reason": "low_limit",
                    "limit": 501,
                    "row_estimate": 18254
                  }
                }
              ],
              "attached_conditions_summary": [
                {
                  "table": "`test_index`",
                  "attached": "((`test_index`.`key1` > 'key120') and (`test_index`.`key1` < 'key129'))"
                }
              ]
            }
          },
          {
			# 再稍稍的改进一下执行计划
            "refine_plan": [
              {
                "table": "`test_index`",
                "pushed_index_condition": "((`test_index`.`key1` > 'key120') and (`test_index`.`key1` < 'key129'))",
                "table_condition_attached": null
              }
            ]
          }
        ]
      }
    },
    {
      "join_execution": {
        "select#": 1,
        "steps": [
        ]
      }
    }
  ]
}

小结

查看optimizer trace可以使我们查看更详细的执行计划生成过程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值