暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

火焰图:MySQL 性能分析的可视化利器

308

 

在日常开发和数据库管理中,SQL 性能优化始终是一个绕不开的话题。一条 SQL 查询可能在测试环境中表现良好,但到了生产环境却可能因为数据量的增加而变得异常缓慢。面对这种情况,开发者和 DBA 通常会借助 MySQL 的 EXPLAIN
 工具来分析执行计划,但输出的表格信息往往晦涩难懂,尤其是面对复杂 SQL 时,如何快速定位性能瓶颈成了一个不小的挑战。

火焰图:性能分析的可视化利器

火焰图(Flame Graph)是一种可视化性能分析数据的图表,常用于展示程序在运行过程中各个函数(或方法)调用所消耗的时间和调用关系。它最早由Brendan Gregg提出,广泛应用于CPU性能分析、内存分配分析等领域。

火焰图的特点:

  • • 横轴:表示某个函数被采样到的次数(即消耗的总时间),越宽表示该函数消耗的时间越多。
  • • 纵轴:表示调用栈的深度,从下往上依次是被调用的函数。
  • • 每个矩形块:代表一个函数,块的宽度表示该函数及其所有子函数消耗的总时间。
  • • 颜色:通常没有特殊含义,只是为了区分不同的函数块。

用途:

  • • 快速定位性能瓶颈,找出哪些函数最耗时。
  • • 了解程序的调用关系和热点路径。

受Tanel Poder启发,他用火焰图把oracle的执行计划可视化了Visualizing SQL Plan Execution Time With FlameGraphs[1],那是不是也能用火焰图可视化MySQL的执行计划呢?

火焰图可视化 MySQL 执行计划

在 MySQL 8.0 之前,EXPLAIN
 工具主要输出预估的执行路径和行数,缺乏真实的耗时数据。表格形式的呈现,尤其对于多层嵌套查询,执行顺序也不容易识别。这种情况下,开发者往往只能根据经验对 SQL 进行优化,而无法准确判断每个步骤的性能表现。这样的输出形式自然也是无法用火焰图可视化的。

EXPLAIN SELECT 
    p.category,
    p.name as product_name,
    p.price,
    (
        SELECT COUNT(DISTINCT o.customer_id)
        FROM orders o
        WHERE o.product_id = p.product_id
        AND o.status ='completed'
    ) as unique_buyers,
    EXISTS (
        SELECT1
        FROM orders o2
        WHERE o2.product_id = p.product_id
        AND o2.order_date >= DATE_SUB(NOW(), INTERVAL 30 DAY)
    ) as has_recent_orders
FROM products p
WHERE p.stock <100
AND p.price > (
    SELECT AVG(price) *1.5
    FROM products
    WHERE category = p.category
)
ORDERBY p.category, unique_buyers DESC;
+----+--------------------+----------+------------+------+------------------------+------------+---------+-------------------+------+----------+----------------------------------------------+
| id | select_type        |table     | partitions | type | possible_keys          | key        | key_len |ref                rows | filtered | Extra                                        |
+----+--------------------+----------+------------+------+------------------------+------------+---------+-------------------+------+----------+----------------------------------------------+
|   1PRIMARY            | p        |NULL        |ALL   NULL                   NULL       NULL    NULL              |  100 |    33.33 Using whereUsing temporary; Using filesort |
|   4| DEPENDENT SUBQUERY | products |NULL        |ALL   NULL                   NULL       NULL    NULL              |  100 |    10.00 Using where                                  |
|   3| DEPENDENT SUBQUERY | o2       |NULL        |ref   | product_id             | product_id 5       | test.p.product_id |   37 |    33.33 Using where                                  |
|   2| DEPENDENT SUBQUERY | o        |NULL        |ref   | customer_id,product_id | product_id 5       | test.p.product_id |   37 |    10.00 Using where                                  |
+----+--------------------+----------+------------+------+------------------------+------------+---------+-------------------+------+----------+----------------------------------------------+

MySQL 8.0 引入了 EXPLAIN ANALYZE
 功能,相比 EXPLAIN
,它不仅展示查询的执行路径,还提供了实际执行过程中的关键性能指标,包括每一步操作的真实耗时(actual time)、读取的行数(rows)、执行次数(loops)等信息。用火焰图可视化 MySQL 执行计划似乎变得可行。

***************************1.row***************************
EXPLAIN: -> Sort: p.category, unique_buyers DESC  (actual time=2.23..2.23rows=4 loops=1)
    -> Stream results  (cost=10.2rows=33.3) (actual time=1.07..1.93rows=4 loops=1)
        ->Filter: ((p.stock <100and (p.price > (select #4)))  (cost=10.2rows=33.3) (actual time=0.832..1.39rows=4 loops=1)
            ->Table scan on p  (cost=10.2rows=100) (actual time=0.0718..0.11rows=100 loops=1)
            ->Select #4 (subquery incondition; dependent)
                -> Aggregate: avg(products.price)  (cost=2.25rows=1) (actual time=0.0823..0.0823rows=1 loops=15)
                    ->Filter: (products.category = p.category)  (cost=1.25rows=10) (actual time=0.0497..0.078rows=27.1 loops=15)
                        ->Table scan on products  (cost=1.25rows=100) (actual time=0.0484..0.0641rows=100 loops=15)
->Select #2 (subquery in projection; dependent)
    -> Aggregate: count(distinct o.customer_id)  (cost=9.75rows=1) (actual time=0.079..0.079rows=1 loops=4)
        ->Filter: (o.`status` ='completed')  (cost=9.39rows=3.61) (actual time=0.0568..0.0744rows=16.8 loops=4)
            -> Index lookup on o using product_id (product_id=p.product_id)  (cost=9.39rows=36.1) (actual time=0.0558..0.0639rows=37 loops=4)
->Select #3 (subquery in projection; dependent)
    -> Limit: 1row(s)  (cost=10.2rows=1) (actual time=0.0499..0.0499rows=0 loops=4)
        ->Filter: (o2.order_date >=<cache>((now() -interval30day)))  (cost=10.2rows=12) (actual time=0.0497..0.0497rows=0 loops=4)
            -> Index lookup on o2 using product_id (product_id=p.product_id)  (cost=10.2rows=36.1) (actual time=0.0367..0.0446rows=37 loops=4)

随后我便试试着用火焰图来可视化它:

 可视化后的执行计划:
  1. 1. 清晰识别执行开销
    火焰图通过宽度直观展示耗时,最宽的部分相对耗时最多的操作,无需逐行比对表格数据。
  2. 2. 清晰展现嵌套结构
    多层 join 或子查询关系通过火焰图的层级结构直观呈现,复杂 SQL 的逻辑关系一目了然。

MySQL Explain Flame Graph

我部署了在线服务:MySQL Explain Flame Graph[2]。开发者和DBA只需将 EXPLAIN ANALYZE
 的输出粘贴到工具中,即可生成火焰图,也可以通过API一键生成。

命令行示例:

mysql -BNEe 'explain analyze sql_statement' | curl --data-binary @- http://sqlfg.dbcopilot.online/api/sqlflamegraph > explain.svg

总结

MySQL 8.0 的 EXPLAIN ANALYZE
 功能为 SQL 性能分析提供了更多执行计划信息,利用火焰图方法让这些信息变得更加直观,开发者和 DBA 可以快速锁定性能瓶颈,让 SQL 调优更高效。


快去试试吧,如果对你用,请帮忙点赞❤️分享 😄

引用链接

[1]
 Visualizing SQL Plan Execution Time With FlameGraphs: https://tanelpoder.com/posts/visualizing-sql-plan-execution-time-with-flamegraphs/
[2]
 MySQL Explain Flame Graph: http://sqlfg.dbcopilot.online/

 

文章转载自爱可生开源社区,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论