bigywater.tech
Flask · 161

慢 SQL 统计

怎么找出最该优化的 SQL?按最慢排还是按总耗时排?应用侧统计与数据库侧统计各看到什么?这篇用同一组请求,把两边的榜单摆在一起对比。

600 次
/report 里一条按 id 查金额的语句被调用的次数(N+1)
66 ms
全表扫描的 count(*) LIKE 在 5 次调用里的总耗时
49.6 对 1.0 ms
那条 N+1 语句在应用侧与数据库侧记到的总耗时

应用侧按总耗时

实测 · 本机执行输出
     次数     总耗时 ms     平均 ms     最大 ms  语句(截断)  | 来自哪个端点
      5       65.9     13.18      14.6  SELECT count(*) AS count_1 FROM "order" WHERE "order".note L | {'search': 5}
    600       49.6      0.08       0.3  SELECT "order".amount FROM "order" WHERE "order".id = %(id_1 | {'report': 600}
     40        8.3      0.21       1.0  SELECT "user".id, "user".name FROM "user" WHERE "user".id =  | {'user': 40}
     20        8.1      0.41       1.7  SELECT "order".id FROM "order" WHERE "order".user_id = %(use | {'report': 20}

上一篇:请求耗时统计;下一篇:Flask Profiler。

第 96 篇讲过 SQLAlchemy 查询日志与慢查询事件,这一篇讲怎么聚合出榜单。

按次数排序

实测 · 本机执行输出
    600 次 总     49.6 ms 平均 0.08 ms  SELECT "order".amount FROM "order" WHERE "order".id = %(id_1)s::INTEGE
     40 次 总      8.3 ms 平均 0.21 ms  SELECT "user".id, "user".name FROM "user" WHERE "user".id = %(pk_1)s::
     20 次 总      8.1 ms 平均 0.41 ms  SELECT "order".id FROM "order" WHERE "order".user_id = %(user_id_1)s::

数据库侧的统计

实测 · 本机执行输出
  calls | total ms | mean ms | max ms | 语句
  5|64.5|12.897|14.2|SELECT count(*) AS count_1 FROM "order" WHERE "order".note LIKE $1::VA
  20|4.6|0.228|1.4|SELECT "order".id FROM "order" WHERE "order".user_id = $1::INTEGER LIM
  600|1.0|0.002|0.0|SELECT "order".amount FROM "order" WHERE "order".id = $1::INTEGER
  40|0.4|0.009|0.0|SELECT "user".id, "user".name FROM "user" WHERE "user".id = $1::INTEGE
  65|0.0|0.000|0.0|BEGIN
  (pg_stat_statements 把参数规整成 $1、$2,看的是数据库内部的执行时间,不含网络与 Python 的耗时;应用侧统计含这两部分)

pg_stat_statements 是自己起的 PostgreSQL 实例里启用的(shared_preload_libraries),不影响你自己的数据库。

分步演示

慢 SQL 统计
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.7 / Flask 3.1.3 / Werkzeug 3.1.9 / Jinja2 3.1.6 / SQLAlchemy 2.1.1 / gunicorn 26.2.0(都装在自己的虚拟环境里);大多数实验用 Flask 的 test_client(直接调用 WSGI 应用,不经过网络),需要真实 HTTP 时另起 gunicorn 或开发服务器(端口 9600 以上,本机 127.0.0.1);计时用 time.perf_counter,数字每次运行会有小幅抖动。Flask-SQLAlchemy 3.1.1、自己起的 PostgreSQL(非默认端口,启用 pg_stat_statements)
    • 没有验证:执行计划(EXPLAIN ANALYZE)的分析、pg_stat_statements 在长期运行下的重置与存储、慢查询日志 log_min_duration_statement、auto_explain、MySQL 的 performance_schema
    京ICP备2026067502号-1京公网安备11010502063487号