MySQL 8.1 和 8.2 中 EXPLAIN 的新玩法

从 MySQL 8.1 开始 EXPLAIN 引入了 INTO 子句,它允许将 EXPLAIN 查询的输出存储在用户变量中,而不是将其返回给客户端。目前仅支持 JSON 格式,但这对于大多数用例来说应该足够了。

作者:Magnus Brevik

原文地址:https://dev.mysql.com/blog-archive/explain-into-and-explain-for-schema-in-mysql-81-and-82/

EXPLAIN INTO

从 MySQL 8.1 开始 EXPLAIN 引入了 INTO 子句,它允许将 EXPLAIN 查询的输出存储在用户变量中,而不是将其返回给客户端。目前仅支持 JSON 格式,但这对于大多数用例来说应该足够了。

EXPLAIN 的输出存储在用户变量之后,就可以将它当成 JSON 对象操作。这使您能够以编程方式直接从 EXPLAIN中提取、操作和存储数据到数据库中。

举个例子:

mysql> SELECT name, quantity FROM orders JOIN items ON orders.item_id = items.id;
+-------------+----------+
| name        | quantity |
+-------------+----------+
| Screwdriver |       23 |
| Screwdriver |        1 |
| Locket      |       17 |
| Armoire     |       42 |
| Armoire     |       16 |
+-------------+----------+

如果只对此次查询的开销成本感兴趣,可以先将查询定义为为 @explain_output

mysql> EXPLAIN FORMAT=JSON INTO @explain_output SELECT name, quantity FROM orders JOIN items ON orders.item_id = items.id;
Query OK, 0 rows affected (0,00 sec)

现在,从这个 EXPLAIN 中得到的唯一输出是“Query OK”。如果我们想查看完整的 EXPLAIN 输出,我们可以选择 @explain_output,但是完整的 JSON 输出会占用很大的空间,而且我只对开销(cost )感兴趣。为了从 JSON 对象中提取,只需使用 MySQL 的 JSON 函数对指定的 JSON 对象处理即可。

mysql> SELECT JSON_EXTRACT(@explain_output, "$.query_block.cost_info.query_cost") AS query_cost;
+------------+
| query_cost |
+------------+
| "1.60"     |
+------------+

EXPLAIN SCHEMA

从 MySQL 8.2 开始 EXPLAIN 引入了 FOR SCHEMA 子句。它允许在当前 SCHEMA 之外的其他 SCHEMA 中执行 EXPLAIN 查询。这使得创建一个存储过程 PROCEDURE 来解释每个 SCHEMA 中最常运行的查询并收集有关它们的统计信息成为可能。

如果当前的 SCHEMA 与我们要查询的表所在的 SCHEMA 不同,我们可以

mysql> USE statistics;
mysql> EXPLAIN FORMAT=TREE FOR SCHEMA customer1 SELECT name, quantity FROM orders JOIN items ON orders.item_id = items.id;
-> Nested loop inner join  (cost=2.3 rows=5)
    -> Table scan on items  (cost=0.55 rows=3)
    -> Index lookup on orders using fk_item_id (item_id=items.id)  (cost=0.472 rows=1.67)

这对于大多数 EXPLAIN 使用的情况可能不太有用,但是如果我们有多个具有相同表结构的 SCHEMA,并且想要一次性收集所有 SCHEMA 的统计信息,我们可以创建一个存储过程 PROCEDURE 来为我们做到这一点:

DELIMITER |
CREATE PROCEDURE explain_query_for_schema(IN schema_name VARCHAR(64), IN query VARCHAR(1000))
BEGIN
    SET @explain_stmt = CONCAT("EXPLAIN FORMAT=JSON INTO @explain_output FOR SCHEMA ", schema_name, " ", query);
    PREPARE stmt FROM @explain_stmt;
    EXECUTE stmt;
    INSERT INTO explain_outputs (schema_name, query, explain_output) VALUES (schema_name, query, @explain_output);
END |

CREATE PROCEDURE explain_query_for_all_schemas(IN query VARCHAR(1000))
BEGIN
    DECLARE done BOOLEAN DEFAULT FALSE;
    DECLARE schema_name VARCHAR(64);
    DECLARE cur_schema_names CURSOR FOR SELECT schema_name_table.schema_name FROM schema_name_table;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
    OPEN cur_schema_names;

    explain_loop: LOOP
        FETCH cur_schema_names INTO schema_name;
        IF done THEN
            LEAVE explain_loop;
        END IF;
        CALL explain_query_for_schema(schema_name, query);
    END LOOP;
    CLOSE cur_schema_names;
END |
DELIMITER ;

SET @query = "SELECT name, quantity FROM orders JOIN items ON orders.item_id = items.id";
CALL explain_query_for_all_schemas(@query);

SELECT schema_name, query, JSON_EXTRACT(explain_output, "$.query_block.cost_info.query_cost") AS query_cost, created_at FROM explain_outputs;
+-------------+---------------------------------------------------------------------------+------------+---------------------+
| schema_name | query                                                                     | query_cost | created_at          |
+-------------+---------------------------------------------------------------------------+------------+---------------------+
| customer1   | SELECT name, quantity FROM orders JOIN items ON orders.item_id = items.id | "2.30"     | 2023-11-14 20:56:47 |
| customer2   | SELECT name, quantity FROM orders JOIN items ON orders.item_id = items.id | "0.70"     | 2023-11-14 20:56:47 |
| customer3   | SELECT name, quantity FROM orders JOIN items ON orders.item_id = items.id | "9.10"     | 2023-11-14 20:56:47 |
+-------------+---------------------------------------------------------------------------+------------+---------------------+

就是这样。EXPLAIN 的两个相对简单的扩展乍一看可能并不重要,但使存储过程能够以编程方式处理 EXPLAIN 输出。我们自己将其用于 《使用 MySQL Autopilot Indexing 删除索引猜测值》 这个案例中,其中 EXPLAIN INTOEXPLAIN FOR SCHEMA 对于收集数据以建议更好的索引至关重要。我们希望它对您和我们一样有用。

如果您想了解更多信息,这些都记录在 MySQL EXPLAIN 文档 中。

更多技术文章,请访问:https://opensource.actionsky.com/

关于 SQLE

SQLE 是一款全方位的 SQL 质量管理平台,覆盖开发至生产环境的 SQL 审核和管理。支持主流的开源、商业、国产数据库,为开发和运维提供流程自动化能力,提升上线效率,提高数据质量。

SQLE 获取

类型地址
版本库https://github.com/actiontech/sqle
文档https://actiontech.github.io/sqle-docs/
发布信息https://github.com/actiontech/sqle/releases
数据审核插件开发文档https://actiontech.github.io/sqle-docs/docs/dev-manual/plugins/howtouse
<think>好的,用户想了解MySQLPostgreSQL中执行EXPLAIN命令时的输出差异。我需要结合之前提供的引用内容,特别是引用3中的比较部分,来组织答案。 首先,我需要回顾引用3提到的差异,比如查询优化器的不同,连接类型支持,性能工具等。这可能直接影响EXPLAIN输出的结构内容。 接下来,应该分点比较两者的EXPLAIN输出,可能包括输出格式、优化器细节、执行计划指标、连接方式、扩展功能等方面。例如,PostgreSQL的查询优化器更强大,支持复杂查询,所以EXPLAIN可能显示更多优化细节,比如不同的连接方法。而MySQL可能只显示嵌套循环连接。 另外,引用3提到PostgreSQL有性能视图统计信息,这可能反映在EXPLAIN的输出中,比如缓冲区命中率等额外指标。而MySQL在这方面较为简单,可能需要插件或额外工具来获取类似信息。 还需要注意用户提到的性能差异,比如简单查询在MySQL可能更快,而复杂查询在PostgreSQL更优,这可能通过EXPLAIN的执行计划成本估算来体现。 最后,总结使用场景,帮助用户根据需求选择合适的数据库。同时,生成相关问题,比如优化器差异、如何分析执行计划等,以扩展用户的知识点。</think>### MySQL vs PostgreSQL EXPLAIN 输出差异分析 #### 1. **输出格式与可读性** - **MySQL** `EXPLAIN` 默认返回表格形式的执行计划,包含 `id`、`select_type`、`table`、`type`、`possible_keys` 等字段。例如: ```sql EXPLAIN SELECT * FROM users WHERE age > 30; ``` 输出会显示索引使用情况(如 `Using index` 或 `Using where`)扫描类型(如 `ALL` 全表扫描或 `range` 范围扫描)。 - **PostgreSQL** 输出为文本树状结构,更详细地展示执行步骤层级。例如: ```sql EXPLAIN ANALYZE SELECT * FROM users WHERE age > 30; ``` 会包含实际执行时间(如 `actual time=0.016..0.021`)资源消耗(如 `Buffers: shared hit=3`),支持 `ANALYZE` 参数获取实际运行数据[^3]。 --- #### 2. **优化器信息差异** - **查询优化器能力** PostgreSQL 的优化器支持复杂查询优化(如子查询去嵌套、通用表表达式优化),其 `EXPLAIN` 会展示更多高级操作(如 `Hash Join`、`Merge Join`)。而 MySQL 优化器较简单,主要依赖嵌套循环连接(`Nested Loop Join`)[^3]。 - **连接类型显示** - **PostgreSQL** 明确标注连接类型(如 `Hash Join`、`Merge Join`)。 - **MySQL** 仅显示 `type` 字段(如 `ref` 或 `eq_ref`),需结合 `Extra` 字段推断连接方式。 --- #### 3. **执行计划指标** - **PostgreSQL** 通过 `EXPLAIN ANALYZE` 可获取: - 实际执行时间(毫秒级) - 内存/缓冲区使用情况(如 `Buffers: shared hit=5`) - 行过滤统计(如 `Rows Removed by Filter: 10`) - **MySQL** 默认输出仅包含估算值(如 `rows` 字段),需启用 `FORMAT=JSON` 或使用 `SHOW PROFILE` 获取详细性能数据。 --- #### 4. **扩展功能支持** - **PostgreSQL** 支持多种扩展参数: - `EXPLAIN (BUFFERS, VERBOSE)` 显示内存字段级信息 - `EXPLAIN (COSTS OFF)` 隐藏成本估算 - **MySQL** 功能较基础,需依赖第三方工具(如 `EXPLAIN FORMAT=JSON` 或 `Performance Schema`)进行深度分析。 --- #### 5. **典型场景对比 假设执行多表关联查询: - **PostgreSQL** 输出: ``` Hash Join (cost=...) Hash Cond: (a.id = b.id) -> Seq Scan on a -> Hash -> Seq Scan on b ``` 明确展示哈希连接过程。 - **MySQL** 输出: ``` +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | rows | Extra| +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | a | ALL | NULL | NULL | NULL | 1000 | Using where | | 1 | SIMPLE | b | ALL | NULL | NULL | NULL | 1000 | Using join buffer| +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ ``` 仅显示全表扫描连接缓冲区使用。 --- ### 总结建议 - **简单查询/高并发读取**:优先使用 MySQL,其 `EXPLAIN` 结果更易解读,适合快速优化[^2]。 - **复杂分析/数据完整性要求高**:选择 PostgreSQL,其 `EXPLAIN` 提供更详尽的优化器决策信息[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值