目录

MySQL 比 EXPLAIN 更详细的信息获取:Optimizer Trace

平时我们对语句调优一般用 EXPLAIN 就可以,但是对于一些复杂语句和疑难杂症这个所能给出信息不够详细,从 MySQL 5.6 开始加入了 Optimizer Trace 功能查看优化器生成执行计划的整个过程,使用示例如下:

SQL 语句

1
2
3
4
SET SESSION OPTIMIZER_TRACE="enabled=on";
SELECT *,`updated_at` < DATE_SUB(NOW(), INTERVAL 1 MINUTE) AS expired FROM `sessions` WHERE `id` = 1 LIMIT 1;
SELECT * FROM information_schema.OPTIMIZER_TRACE;
SET SESSION OPTIMIZER_TRACE="enabled=off";

执行结果

它所返回的信息为一个 JSON,里面会有各种语句运行的详细信息,以上面的为例会列出真实运行的语句

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
{
  "steps": [
    {
      "join_preparation": {
        "select#": 1,
        "steps": [
          {
            "expanded_query": "/* select#1 */ select `sessions`.`id` AS `id`,`sessions`.`user_id` AS `user_id`,`sessions`.`ip_address` AS `ip_address`,`sessions`.`payload` AS `payload`,`sessions`.`device_id` AS `device_id`,`sessions`.`created_at` AS `created_at`,`sessions`.`updated_at` AS `updated_at`,(`sessions`.`updated_at` < (now() - interval 1 minute)) AS `expired` from `sessions` where (`sessions`.`id` = '1111111111111111111111111111111111111111') limit 1"
          }
        ]
      }
    },
    {
      "join_optimization": {
        "select#": 1,
        "steps": [
          {
            "condition_processing": {
              "condition": "WHERE",
              "original_condition": "(`sessions`.`id` = '1111111111111111111111111111111111111111')",
              "steps": [
                {
                  "transformation": "equality_propagation",
                  "resulting_condition": "(`sessions`.`id` = '1111111111111111111111111111111111111111')"
                },
                {
                  "transformation": "constant_propagation",
                  "resulting_condition": "(`sessions`.`id` = '1111111111111111111111111111111111111111')"
                },
                {
                  "transformation": "trivial_condition_removal",
                  "resulting_condition": "(`sessions`.`id` = '1111111111111111111111111111111111111111')"
                }
              ]
            }
          },
          {
            "substitute_generated_columns": {
            }
          },
          {
            "table_dependencies": [
              {
                "table": "`sessions`",
                "row_may_be_null": false,
                "map_bit": 0,
                "depends_on_map_bits": [
                ]
              }
            ]
          },
          {
            "ref_optimizer_key_uses": [
              {
                "table": "`sessions`",
                "field": "id",
                "equals": "'1111111111111111111111111111111111111111'",
                "null_rejecting": true
              }
            ]
          },
          {
            "rows_estimation": [
              {
                "table": "`sessions`",
                "rows": 1,
                "cost": 1,
                "table_type": "const",
                "empty": true
              }
            ]
          }
        ],
        "empty_result": {
          "cause": "no matching row in const table"
        }
      }
    },
    {
      "join_execution": {
        "select#": 1,
        "steps": [
        ]
      }
    }
  ]
}

可以看出虽然我们写语句时写的是 * 查询,但这只是给用户使用的简便写法。在真实运行时语句还是会转变为真实表字段的列表,而不是用 * 去运行。

优化器

1. 优化器的工作流程

  1. 语法分析器将 SQL 语句转换为抽象语法树(AST)。
  2. 优化器将 AST 转换为逻辑执行计划。
  3. 优化器将逻辑执行计划转换为物理执行计划。
  4. 执行器执行物理执行计划。
  5. 执行器返回结果。

2. 优化器的工作原理

  1. 优化器会根据 SQL 语句的特点,选择合适的优化策略。
  2. 优化器会根据表的统计信息,选择合适的索引。
  3. 优化器会根据表的统计信息,选择合适的连接顺序。
  4. 优化器会根据表的统计信息,选择合适的连接类型。
  5. 优化器会根据表的统计信息,选择合适的连接算法。

参考

更多信息参考 Optimizer Trace