EXPLAIN四种输出格式&MySQL监控分析视图——sys schema

EXPLAIN四种输出格式

这里谈谈EXPLAIN的输出格式。EXPLAIN可以输出四种格式:传统格式JSON格式TREE格式以及可视化输出。用户可以根据需要选择适用于自己的格式。

EXPLAIN四种输出格式

传统格式

传统格式简单明了,输出是一个表格形式,概要说明查询计划。

EXPLAIN SELECT s1.key1, s2.key1 FROM s1 LEFT JOIN s2 ON s1.key1 = s2.key1 WHERE
s2.common_field IS NOT NULL;
JSON格式

第1种格式中介绍的EXPLAIN语句输出中缺少了一个衡量执行计划好坏的重要属性——成本。而JSON格式是四种格式里面输出信息最详尽的格式,里面包含了执行的成本信息。

  • JSON格式:在EXPLAIN单词和真正的查询语句中间加上FORMAT=JSON
EXPLAIN FORMAT=JSON SELECT ....
  • EXPLAIN的Column与JSON的对应关系:(来源于MySQL 5.7文档)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wWW3PVdv-1648199800627)(C:/Users/losser/AppData/Roaming/Typora/typora-user-images/image-20220324212250489.png)]

这样我们就可以得到一个json格式的执行计划,里面包含该计划花费的成本,比如这样:

EXPLAIN FORMAT=JSON SELECT * FROM s1 INNER JOIN s2 ON s1.key1 = s2.key2  WHERE s1.common_field = 'a'\G

这种事JSON格式的

{
  "query_block": {
    "select_id": 1,
    # 花费的成本信息
    "cost_info": {
      "query_cost": "2102.20"
    },
    "nested_loop": [
      {
        "table": {
          "table_name": "s1",
          "access_type": "ALL",
          "possible_keys": [
            "idx_key1"
          ],
          "rows_examined_per_scan": 9895,
          "rows_produced_per_join": 989,
          "filtered": "10.00",
          "cost_info": {
            "read_cost": "914.80",
            "eval_cost": "98.95",
            "prefix_cost": "1013.75",
            "data_read_per_join": "1M"
          },
          "used_columns": [
            "id",
            "key1",
            "key2",
            "key3",
            "key_part1",
            "key_part2",
            "key_part3",
            "common_field"
          ],
          "attached_condition": "((`atguigudb1`.`s1`.`common_field` = 'a') and (`atguigudb1`.`s1`.`key1` is not null))"
        }
      },
      {
        "table": {
          "table_name": "s2",
          "access_type": "eq_ref",
          "possible_keys": [
            "idx_key2"
          ],
          "key": "idx_key2",
          "used_key_parts": [
            "key2"
          ],
          "key_length": "5",
          "ref": [
            "atguigudb1.s1.key1"
          ],
          "rows_examined_per_scan": 1,
          "rows_produced_per_join": 989,
          "filtered": "100.00",
          "index_condition": "(cast(`atguigudb1`.`s1`.`key1` as double) = cast(`atguigudb1`.`s2`.`key2` as double))",
          "cost_info": {
        	# IO成本+检测的rows * (1 - filter)条记录的CPU的成本
            "read_cost": "989.50",
            "eval_cost": "98.95",
            "prefix_cost": "2102.20",
            "data_read_per_join": "1M"
          },
          "used_columns": [
            "id",
            "key1",
            "key2",
            "key3",
            "key_part1",
            "key_part2",
            "key_part3",
            "common_field"
          ]
        }
      }
    ]
  }
}

  • read_cost是由下边这两部分组成的:
    • IO成本
    • 检测rows × (1 - filter)条记录的 CPU 成本

小贴士: rows和filter都是我们前边介绍执行计划的输出列,在JSON格式的执行计划中,rows
相当于rows_examined_per_scan,filtered名称不变。

  • eval_cost是这样计算的:

​ 检测rows * filter条记录的成本。

  • prefix_cost就是单独查询s1表的成本,也就是:

read_cost + eval_cost

  • data_read_per -join表示在此次查询中需要读取的数据量。

对于s2表的cost_info部分是这样是:

 "cost_info": {
            "read_cost": "914.80",
            "eval_cost": "98.95",
            "prefix_cost": "1013.75",
            "data_read_per_join": "1M"
          },

由于s2表是被驱动表,所以可能被读取多次,这里的read_costeval_cost是访问多次s2表后累
加起来的值,大家主要关注里边儿的prefix_cost的值代表的是整个连接查询预计的成本,也就是单
次查询s1表和多次查询s2表后的成本的和,也就是:

968.80 + 193.76 + 2034.60 = 3197.16
TREE格式

TREE格式是8.0.16版本之后引入的新格式,主要根据查询的各个部分之间的关系各部分的执行顺序来描述如何查询。

mysql> EXPLAIN FORMAT=TREE SELECT * FROM s1 INNER JOIN s2 ON s1.key1 = s2.key2  WHERE s1.common_field = 'a'\G
*************************** 1. row ***************************
EXPLAIN: -> Nested loop inner join  (cost=2102.20 rows=990)
    -> Filter: ((s1.common_field = 'a') and (s1.key1 is not null))  (cost=1013.75 rows=990)
        -> Table scan on s1  (cost=1013.75 rows=9895)
    -> Single-row index lookup on s2 using idx_key2 (key2=s1.key1), with index condition: (cast(s1.key1 as double) = cast(s2.key2 as double))  (cost=1.00 rows=1)

1 row in set, 1 warning (0.00 sec)
可视化输出

可视化输出,可以通过MySQL Workbench可视化查看MysQL的执行计划。通过点击Workbench的放大镜图标,即可生成可视化的查询计划。

你提供的 SQL 语句: ```sql SELECT table_schema AS schemaName, table_name AS tableName, redundant_index_name AS objectName FROM sys.schema_redundant_indexes WHERE table_schema NOT IN ('information_schema', 'sys', 'mysql', 'performance_schema'); ``` 这是一条非常有用的 **MySQL 系统视图查询语句**,用于检测当前实例中所有用户数据库的**冗余索引(redundant indexes)**。 --- ### ✅ **这条语句的作用解释** #### 📌 `sys.schema_redundant_indexes` - 这是 MySQL 提供的一个**系统视图(在 sys 库中)**,专门用来识别表中可以被其他索引替代的“冗余索引”。 - 冗余索引指的是:某个索引的所有查询功能都可以由另一个更优或等价的索引完成,因此它是多余的,浪费空间和写性能。 #### 📌 字段说明: | 字段 | 含义 | |------|------| | `table_schema` | 数据库名(schema 名) | | `table_name` | 表名 | | `redundant_index_name` | **可以被删除的冗余索引名称**(即这个索引是多余的) | | `redundant_index_columns` | 冗余索引包含的列 | | `alternative_index_name` | 可以替代它的那个**更优或等效的索引名称** | > ⚠️ 注意:这里的 `redundant_index_name` 是建议你可以考虑删除的索引。 #### 📌 WHERE 条件过滤了系统库 ```sql WHERE table_schema NOT IN ('information_schema','sys','mysql','performance_schema') ``` 避免显示 MySQL 自带系统数据库中的索引信息,只关注**用户自定义数据库**。 --- ### ✅ 示例输出 假设执行结果如下: | schemaName | tableName | objectName | |------------|-------------------------|-----------------| | finance_db | trn_regu_overpay_refund | idx_contr_no | | payment_db | ul_captl_paymt_instructn_detl | idx_temp_contr | 这意味着: - 在 `finance_db.trn_regu_overpay_refund` 表上,`idx_contr_no` 是一个**冗余索引**,应该检查是否可以删除。 - 它可能被另一个复合索引(如 `(contr_no, create_time)`)所覆盖。 --- ### ✅ 如何处理这些冗余索引? 1. **确认该索引是否还在被应用使用** - 查看慢查询日志、执行计划 (`EXPLAIN`) 是否用到该索引。 - 使用 `performance_schema` 或 `sys.statements_with_full_table_scans` 分析。 2. **评估删除影响** - 在测试环境先删除,观察查询性能变化。 - 使用 `pt-index-usage` 工具分析索引实际使用情况。 3. **安全删除冗余索引** ```sql ALTER TABLE trn_regu_overpay_refund DROP INDEX idx_contr_no; ``` 4. **定期巡检** 将此 SQL 加入数据库健康检查脚本中,定期运行。 --- ### ✅ 建议补充字段以便更好分析 增强版查询(推荐): ```sql SELECT table_schema AS schemaName, table_name AS tableName, redundant_index_name AS redundantIndexName, redundant_index_columns AS redundantColumns, alternative_index_name AS alternativeIndexName, sql_drop -- 直接给出删除该冗余索引的 SQL FROM sys.schema_redundant_indexes WHERE table_schema NOT IN ('information_schema', 'sys', 'mysql', 'performance_schema'); ``` > 🔥 特别注意:`sql_drop` 字段会直接生成类似: > ```sql > ALTER TABLE `trn_regu_overpay_refund` DROP INDEX `idx_contr_no`; > ``` > 非常适合自动化运维! --- ### ✅ 总结 你的这条 SQL 是**识别 MySQL 中冗余索引的标准方法之一**,适用于: - 数据库优化 - 减少存储开销 - 提高 DML 性能(减少索引维护) - 清理历史遗留索引 但要注意:**不要盲目删除 `redundant_index_name` 所示的索引**,必须结合实际执行计划和业务逻辑判断。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值