MySQL提供了两个用于分析查询计划的强大工具:EXPLAIN和EXPLAIN ANALYZE。EXPLAIN显示优化器选择的执行计划,并在执行之前停止,而EXPLAIN ANALYZE实际执行查询并收集有关每个操作的处理时间和返回行数的统计信息。输出格式可以是表格形式(TRADITIONAL)、树形结构或JSON。前两种格式适用于人类阅读,而JSON格式主要面向机器,同时也易于人类阅读。JSON格式应该是自动化查询分析的理想格式,但自从我们转换为基于迭代器的执行计划后,它未能正确表示计划结构。这使得使用JSON格式进行EXPLAIN ANALYZE变得不可能,因为EXPLAIN ANALYZE直接与计划的迭代器结构相关联。

在MySQL 8.3社区版中,我们引入了一个新的JSON格式用于EXPLAIN和EXPLAIN ANALYZE,同时引入了系统变量"explain_json_format_version={1,2}"来在不同格式之间切换。新的JSON格式反映了迭代器的构建方式,直接匹配TREE格式。JSON格式中的每个对象对应TREE格式中的一行,但JSON格式以机器可读的格式包含更多信息,可以通过例如EXPLAIN INTO或客户端应用程序访问。这在支持新JSON格式的EXPLAIN ANALYZE以及最近添加的EXPLAIN INTO和EXPLAIN FOR SCHEMA功能中尤为有用。因此,EXPLAIN树中的所有值都可以通过JSON格式访问并进行程序化分析。

如何使用新的JSON格式
谈论新功能时不能没有示例,所以让我们看一个简单的SELECT查询:
mysql> EXPLAIN FORMAT=TREE SELECT name, quantity FROM orders JOIN items i ON item_id = i.id WHERE quantity > 1000;
-> Nested loop inner join (cost=49828 rows=76470)
-> Filter: ((orders.quantity > 1000) and (orders.item_id is not null)) (cost=23063 rows=76470)
-> Table scan on orders (cost=23063 rows=229432)
-> Single-row index lookup on i using PRIMARY (id=orders.item_id) (cost=0.25 rows=1)
TREE格式虽然对人类读者来说紧凑且易于阅读和理解,但需要手动解析才能在代码中进行分析。使用EXPLAIN FORMAT=JSON,您可以获得一个JSON对象,可以在您喜欢的编程语言中的客户端端处理,或者在MySQL中使用EXPLAIN INTO进行处理。这种JSON格式是在MySQL 5.6中引入的,反映了当时的计划结构。随着转换为基于迭代器的计划,这种旧的JSON EXPLAIN格式不再代表内部计划结构。
# Old JSON EXPLAIN format, default
mysql> SET explain_json_format_version=1;
mysql> EXPLAIN FORMAT=JSON SELECT name, quantity FROM orders JOIN items i ON item_id = i.id WHERE quantity > 1000\G
{
  "query_block": {
    "select_id": 1,
    "cost_info": {
      "query_cost": "49827.84"
    },
    "nested_loop": [
      {
        "table": {
          "table_name": "orders",
          "access_type": "ALL",
          "possible_keys": [
            "fk_item_id"
          ],
          "rows_examined_per_scan": 229432,
          "rows_produced_per_join": 76469,
          "filtered": "33.33",
          "cost_info": {
            "read_cost": "15416.48",
            "eval_cost": "7646.97",
            "prefix_cost": "23063.45",
            "data_read_per_join": "1M"
          },
          "used_columns": [
            "item_id",
            "quantity"
          ],
          "attached_condition": "((`customer`.`orders`.`quantity` > 1000) and (`customer`.`orders`.`item_id` is not null))"
        }
      },
      {
        "table": {
          "table_name": "i",
          "access_type": "eq_ref",
          "possible_keys": [
            "PRIMARY"
          ],
          "key": "PRIMARY",
          "used_key_parts": [
            "id"
          ],
          "key_length": "4",
          "ref": [
            "customer.orders.item_id"
          ],
          "rows_examined_per_scan": 1,
          "rows_produced_per_join": 76469,
          "filtered": "100.00",
          "cost_info": {
            "read_cost": "19117.42",
            "eval_cost": "7646.97",
            "prefix_cost": "49827.84",
            "data_read_per_join": "19M"
          },
          "used_columns": [
            "id",
            "name"
          ]
        }
      }
    ]
  }
}

正如我们从这个例子中看到的,实际执行计划中的迭代器结构在TREE格式中显示,但在旧的JSON格式中并没有反映出来。在对订单表进行表扫描时,过滤器迭代器更像是一个脚注,而关于行和连接成本的所有信息都在两个表对象下。虽然这些值是正确的,但结构是错误的,这使得使用这些信息来优化你的查询变得更加困难。

随着HeatWave MySQL中新MySQL超图优化器的开发,引入了一种新的JSON EXPLAIN格式,因为旧格式与遗留的规划过程或由优化器创建的最终计划没有关联。使用超图优化器并执行EXPLAIN FORMAT=JSON将始终产生这种新的JSON格式。如果您使用的是旧优化器并且运行的是MySQL 8.3或更新版本,可以通过将系统变量"explain_json_format_version"设置为2来访问新的JSON格式。

# New JSON EXPLAIN format
mysql> SET explain_json_format_version=2;
mysql> EXPLAIN FORMAT=JSON SELECT name, quantity FROM orders JOIN items i ON item_id = i.id WHERE quantity > 1000;
{
  "query": "/* select#1 */ select `i`.`name` AS `name`,`customer`.`orders`.`quantity` AS `quantity` from `customer`.`orders` join `customer`.`items` `i` where ((`i`.`id` = `customer`.`orders`.`item_id`) and (`customer`.`orders`.`quantity` > 1000))",
  "inputs": [
    {
      "inputs": [
        {
          "operation": "Table scan on orders",
          "table_name": "orders",
          "access_type": "table",
          "schema_name": "customer",
          "used_columns": [
            "item_id",
            "quantity"
          ],
          "estimated_rows": 229432,
          "estimated_total_cost": 23063.45
        }
      ],
      "condition": "((orders.quantity > 1000) and (orders.item_id is not null))",
      "operation": "Filter: ((orders.quantity > 1000) and (orders.item_id is not null))",
      "access_type": "filter",
      "estimated_rows": 76469.68433094025,
      "estimated_total_cost": 23063.45
    },
    {
      "alias": "i",
      "covering": false,
      "operation": "Single-row index lookup on i using PRIMARY (id = orders.item_id)",
      "index_name": "PRIMARY",
      "table_name": "items",
      "access_type": "index",
      "schema_name": "customer",
      "used_columns": [
        "id",
        "name"
      ],
      "estimated_rows": 1,
      "lookup_condition": "id = orders.item_id",
      "index_access_type": "index_lookup",
      "estimated_total_cost": 0.25000130770776513
    }
  ],
  "join_type": "inner join",
  "operation": "Nested loop inner join",
  "access_type": "join",
  "estimated_rows": 76469.68433094025,
  "join_algorithm": "nested_loop",
  "estimated_total_cost": 49827.83951582908
}

我们不会详细介绍新JSON格式中的每个字段,因为它们中的大多数与旧格式相同或只是重命名的版本,或者是相当直观的,但我将提及其中的几个。

如前所述,新的JSON格式与TREE格式直接对应。TREE格式中的顶级迭代器是JSON格式中的顶级对象,而子迭代器可以在JSON对象的“inputs”字段中找到。要将每个迭代器映射到相关的JSON对象,您可以查看JSON中的“operation”字段,这就是在TREE格式中打印的内容。

在旧的JSON格式中存在但现在行为略有变化的字段是“table_name”。在旧格式中,“table_name”实际上是表的别名,而不是底层表的名称。这在新格式中已经改变,其中“table_name”现在是基础表名,并在表有别名时向表访问迭代器添加了新的“alias”字段。别名仍然是在表访问的父迭代器中引用的内容。

使用新的JSON格式与EXPLAIN ANALYZE

新JSON格式的另一个实用特性是,虽然旧的JSON格式不适合显示EXPLAIN ANALYZE的输出,但由于新格式基于与TREE格式相同的迭代器结构,我们现在可以在JSON格式中获得EXPLAIN ANALYZE的输出。这允许对执行时间进行程序化分析,因为EXPLAIN ANALYZE FORMAT=JSON包含多个字段,这些字段包含了以前需要从TREE格式手动解析的信息。
让我们看看我们之前查询的EXPLAIN ANALYZE:
mysql> EXPLAIN ANALYZE FORMAT=TREE SELECT name, quantity FROM orders JOIN items i ON item_id = i.id WHERE quantity > 1000\G
-> Nested loop inner join (cost=49828 rows=76470) (actual time=1.95..5182 rows=227103 loops=1)
-> Filter: ((orders.quantity > 1000) and (orders.item_id is not null)) (cost=23063 rows=76470) (actual time=1.82..1271 rows=227103 loops=1)
-> Table scan on orders (cost=23063 rows=229432) (actual time=1.76..1135 rows=229376 loops=1)
-> Single-row index lookup on i using PRIMARY (id=orders.item_id) (cost=0.25 rows=1) (actual time=0.0164..0.0164 rows=1 loops=227103)

这里是同样的计划,使用FORMAT=JSON:
mysql> SET explain_json_format_version=2;
mysql> EXPLAIN ANALYZE FORMAT=JSON SELECT name, quantity FROM orders JOIN items i ON item_id = i.id WHERE quantity > 1000\G
{
"query": "/* select#1 */ select `i`.`name` AS `name`,`customer`.`orders`.`quantity` AS `quantity` from `customer`.`orders` join `customer`.`items` `i` where ((`i`.`id` = `customer`.`orders`.`item_id`) and (`customer`.`orders`.`quantity` > 1000))",
"inputs": [
{
"inputs": [
{
"operation": "Table scan on orders",
"table_name": "orders",
"access_type": "table",
"actual_rows": 229376.0,
"schema_name": "customer",
"actual_loops": 1,
"used_columns": [
"item_id",
"quantity"
],
"estimated_rows": 229432.0,
"actual_last_row_ms": 1123.953248,
"actual_first_row_ms": 1.868662,
"estimated_total_cost": 23063.45
}
],
"condition": "((orders.quantity > 1000) and (orders.item_id is not null))",
"operation": "Filter: ((orders.quantity > 1000) and (orders.item_id is not null))",
"access_type": "filter",
"actual_rows": 227103.0,
"actual_loops": 1,
"estimated_rows": 76469.68433094025,
"actual_last_row_ms": 1252.748685,
"actual_first_row_ms": 1.92276,
"estimated_total_cost": 23063.45
},
{
"alias": "i",
"covering": false,
"operation": "Single-row index lookup on i using PRIMARY (id = orders.item_id)",
"index_name": "PRIMARY",
"table_name": "items",
"access_type": "index",
"actual_rows": 1.0,
"schema_name": "customer",
"actual_loops": 227103,
"used_columns": [
"id",
"name"
],
"estimated_rows": 1.0,
"lookup_condition": "id = orders.item_id",
"index_access_type": "index_lookup",
"actual_last_row_ms": 0.016049919261304342,
"actual_first_row_ms": 0.015992197227689638,
"estimated_total_cost": 0.25000130770776513
}
],
"join_type": "inner join",
"operation": "Nested loop inner join",
"access_type": "join",
"actual_rows": 227103.0,
"actual_loops": 1,
"estimated_rows": 76469.68433094025,
"join_algorithm": "nested_loop",
"actual_last_row_ms": 5071.693038,
"actual_first_row_ms": 2.071419,
"estimated_total_cost": 49827.83951582908
}
使用EXPLAIN ANALYZE,我们得到了一些以"actual_"开头的额外字段,这些字段包含了关于执行的信息。在TREE格式中,这些信息与迭代器中的执行信息相同,比如"(actual time=<actual_first_row_ms>..<actual_last_row_ms> rows=<actual_rows> loops=<actual_loops>)"。

总结
EXPLAIN和EXPLAIN ANALYZE的新JSON格式首次在MySQL 8.3社区版中引入,现在也可用于所有平台上的MySQL 8.4 LTS和9.x创新版本,包括OCI、AWS和Azure上的HeatWave MySQL。这种新格式允许基于实际执行计划进行详细的查询分析,无论是在本地还是使用EXPLAIN INTO在服务器上。在TREE格式中找到的所有信息都可以使用JSON函数轻松访问,因此您不必手动解析TREE输出。这在EXPLAIN ANALYZE中特别有用,因为在早期版本中它没有JSON格式。新的JSON格式已经被HeatWave Autopilot用于云中的索引建议,也应该有助于您在自己的应用程序中收集额外的洞察。记得将"explain_json_format_version"设置为2,或者在HeatWave MySQL 9.x中使用新的MySQL超图优化器,以享受EXPLAIN和EXPLAIN ANALYZE新JSON格式的好处。

感谢您使用MySQL!

翻译转载:https://blogs.oracle.com/mysql/post/new-json-format-for-explain

【昌哥IT课堂】MySQL8.3 EXPLAIN中的新JSON格式(译)的更多相关文章

  1. jmeter随笔(1)-在csv中数据为json格式的数据不完整

    昨天同事在使用jmeter遇到问题,在csv中数据为json格式的数据,在jmeter中无法完整的取值,小怪我看了下,给出解决办法,其实很简单,我们一起看看,看完了记得分享给你的朋友. 问题现象: 1 ...

  2. Ajax中XML和JSON格式的优劣比较

    刚做完一个小的使用Ajax的项目.整个小项目使用JavaScript做客户端,使用PHP做服务器端.利用xmlHttpRequest组件作为交互工具,利用XML作为数据传输的格式.做完后基本做一个简单 ...

  3. MVC4中视图获取控制器中返回的json格式数据

    再开发MVC项目时,有时只需要从控制器中返回一个处理的结果,这时返回Json格式的数据非常的方便,在Controller中,提供了几种返回类型和方法,如: Content() 返回文本类型的Conte ...

  4. java中post发送json格式数据

    /** * 发送post请求 * @param URL 数据发送地址 * @param json json格式数据内容 * @param headParams 请求头内容 * @return 请求结果 ...

  5. iOS开发之功能模块--Apns推送中的的json格式介绍

    在开发向苹果Apns推送消息服务功能,我们需要根据Apns接受的数据格式进行推送.下面接受我在进行apns推送时候总结的一点apns服务接受的Json数据格式 示例 1: 以下负载包含哦一个简单的 a ...

  6. Apns推送中的的json格式介绍

    在开发向苹果Apns推送消息服务功能,我们需要根据Apns接受的数据格式进行推送.下面接受我在进行apns推送时候总结的一点apns服务接受的Json数据格式 示例 1: 以下负载包含哦一个简单的 a ...

  7. ajax中url赋json格式的值时发生中文乱码的相关问题

    具体流程:转入到jsp界面时会加载ajax,ajax转到url时传带hide在jsp界面的值titleString,其来源见下面的代码. String title=new String("\ ...

  8. java中,用json格式转换遇到问题

    将list转为JSONObject类,报 org/apache/commons/lang/exception/NestableRuntimeException是什么原因? 还需要导入这些包common ...

  9. SpringCloud中接收application/json格式的post请求参数并转化为实体类

    @CrossOrigin(allowCredentials="true", allowedHeaders="*", methods={RequestMethod ...

  10. json中把非json格式的字符串转换成json对象再转换成json字符串

    JSON.toJson(str).toString()假如key和value都是整数的时候,先转换成jsonObject对象,再转换成json字符串

随机推荐

  1. 【Mac】之安装VM虚拟机并安装centos7系统

    参考文章:<Mac 安装VMware Fusion虚拟机> 一.安装VMware Fusion 首先下载Mac版VMware虚拟机: 链接:https://pan.baidu.com/s/ ...

  2. 开发Android应用程序,在Android10的系统上提示网络出错?

    今天维护以前开发的一个Android客户端程序,发版后,有用户说自己手机安装,无法登录,首屏打开后(有网络通过接口加载服务器数据并显示的行为),提示网络出错. 但是我在我自己手上的PDA设备(Andr ...

  3. 一步一步将PlantUML类图导出为自定义格式的XMI文件

    一步一步将PlantUML类图导出为自定义格式的XMI文件 说明: 首次发表日期:2024-09-08 PlantUML官网: https://plantuml.com/zh/ PlantUML命令行 ...

  4. linux磁盘分区之后,lsblk没有显示

    可以看出  fdisk 创建一个 sda4 的分区  并保存退出, 但是 不管是使用 fdisk -l ,还是 lsblk 都无法显示出来, 那么导致问题的原因,主要是因为新创建了分区之后,系统没有重 ...

  5. Angular 18+ 高级教程 – Component 组件 の Dynamic Component 动态组件

    前言 Angular 是 MVVM 框架. MVVM 的宗旨是 "不要直接操作 DOM". 为了这个 "不要直接操作 DOM",我们已经讲了 2 篇文章了. 第 ...

  6. TypeScript – tsconfig

    前言 上一篇 TypeScript – Get Started 使用了命令 tsc index.ts --module es2015 很少人会在命令时给写 config, 更正规的做法是创建一个 ts ...

  7. Soring —— 容器总结

    获取bean    容器层次结构图      *BeanFactory 是IoC容器的顶层接口,初始化BeanFactory对象时,加载的bean延迟加载 bean相关    依赖注入相关   

  8. namenode rpc 请求队列调优

    当 datanode 上新写完一个块,默认会立即汇报给 namenode.在一个大规模 Hadoop 集群上,每时每刻都在写数据,datanode 上随时都会有写完数据块然后汇报给 namenode ...

  9. 神经网络之卷积篇:详解为什么使用卷积?(Why convolutions?)

    详解为什么使用卷积? 来分析一下卷积在神经网络中如此受用的原因,然后对如何整合这些卷积,如何通过一个标注过的训练集训练卷积神经网络做个简单概括.和只用全连接层相比,卷积层的两个主要优势在于参数共享和稀 ...

  10. 第44天:WEB攻防-PHP应用&SQL盲注&布尔回显&延时判断&报错处理&增删改查方式

    #PHP-MYSQL-SQL操作-增删改查 1.功能:数据查询 查询:SELECT * FROM news where id=$id 2.功能:新增用户,添加新闻等 增加:INSERT INTO ne ...