news 2026/6/17 15:34:18

31-慢查询排查全流程(上)-Django-Debug-Toolbar与EXPLAIN入门

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
31-慢查询排查全流程(上)-Django-Debug-Toolbar与EXPLAIN入门

文章目录

  • 你的接口为什么慢?(上)——Django Debug Toolbar + EXPLAIN:从看到慢查询到读懂它
    • 导入语
    • 1 ~> Django Debug Toolbar——把你写的每个 View 的 SQL 全部摊在桌面上
      • 1.1 安装与配置
      • 1.2 打开页面——看见"SQL"面板
      • 1.3 Debug Toolbar 的四个核心面板
      • 1.4 从 SQL 面板定位到代码
    • 2 ~> EXPLAIN——读懂一条 SQL 的执行计划
      • 2.1 基础用法
      • 2.2 `type` 字段——最重要的一列
      • 2.3 `key` 字段——实际用到的索引
      • 2.4 `rows` 字段——预估扫描行数
      • 2.5 `Extra` 字段——优化器的补充注释
    • 3 ~> 真实案例——一个报表页面的慢查询定位
      • 3.1 背景
      • 3.2 用 EXPLAIN 定位
      • 3.3 修复
    • 4 ~> 如何在 Django 中直接用代码输出 EXPLAIN
      • 4.1 Java 开发者的类比
    • 思考 && 总结
    • 结尾

你的接口为什么慢?(上)——Django Debug Toolbar + EXPLAIN:从看到慢查询到读懂它

📖文章简介:接口突然从 50ms 变成 800ms,你第一反应是什么?“加缓存”“改索引”“换 SSD”——但你没看到实际的慢查询长什么样。上篇聚焦性能排查的第一步:用 Django Debug Toolbar 把所有 SQL 查询摊在桌面上,再通过 MySQL 的 EXPLAIN 命令读懂每条查询的执行计划。从安装配置 Debug Toolbar 开始,到定位出具体是哪个 View 的哪条 ORM 触发了慢查询,再到 EXPLAIN 输出中的 type / key / rows / Extra 四个核心字段的解读。配有两个真实案例——一个 N+1 问题的可视化暴露,一个因为缺少索引导致的全表扫描。


🎬 个人主页:源码骑士

专栏传送门:《Android开发基础》《python基础课程》

⭐️热衷从源码视角拆解技术底层原理,将复杂架构讲得通俗易懂


🎬 源码骑士的简介:
5年Android Framework系统开发经验,曾主导多项系统级性能优化专项
技术栈覆盖Android系统全链路(Binder/Handler/AMS/WMS/启动流程)及Java后端全家桶(Spring + MyBatis + Redis + Oracle)
累计产出原创技术文章100+篇,文章以源码拆解为特色,被读者评价为"看一篇胜过啃一周文档"


导入语

2022 年的一天,产品经理在群里发了一张截图——用户详情页加载了 3 秒才出来。DBA 说数据库负载正常,运维说服务器 CPU 才 15%。我打开浏览器访问了一下——确实慢,但不知道慢在哪。

这时候最蠢的做法是"猜"——“可能是索引没建”“可能是 ORM 写得不好”“可能是网络问题”。正确的做法是:先看清楚到底执行了哪些 SQL,每条 SQL 花了多少时间。眼睛看得到,手才改得对。上篇讲清两步——用 Debug Toolbar 把慢查询"抓出来",再用 EXPLAIN 把慢查询"读明白"。


1 ~> Django Debug Toolbar——把你写的每个 View 的 SQL 全部摊在桌面上

1.1 安装与配置

pipinstalldjango-debug-toolbar
# settings.pyINSTALLED_APPS+=["debug_toolbar"]MIDDLEWARE=["debug_toolbar.middleware.DebugToolbarMiddleware",]+MIDDLEWARE# 放最前面——尽早介入请求INTERNAL_IPS=["127.0.0.1"]# 只对本地请求显示
# urls.pyfromdjango.urlsimportinclude,path urlpatterns+=[path("__debug__/",include("debug_toolbar.urls")),]

1.2 打开页面——看见"SQL"面板

部署完后,打开任意页面,右侧会出现一个可折叠的 Debug Toolbar 面板。点开 “SQL” ——你会看到类似这样的东西:

共 47 条查询,总耗时 123.45 ms 1. SELECT "auth_user"."id" FROM "auth_user" WHERE ... 0.23 ms 2. SELECT "books"."id", "books"."title" FROM "books" 0.15 ms 3. SELECT "books"."id" FROM "books" WHERE ... 0.12 ms (重复 99 次...) 47. SELECT "books"."id" FROM "books" WHERE ... 0.11 ms

第 3~47 条是重复的!这就是 N+1 的可视化铁证——Debug Toolbar 让你不用猜,一眼就能看到重复查询。

1.3 Debug Toolbar 的四个核心面板

面板告诉你什么
SQL执行了哪些 SQL、用了多长时间、从哪里发出的(追溯代码位置)
Profiling哪个 Python 函数耗时最长
Cache缓存命中了多少次、miss 了多少次
Request VarsView 拿到了哪些 GET/POST 参数和 Session 信息

1.4 从 SQL 面板定位到代码

Debug Toolbar 的每条 SQL 旁边都有一个"追溯"链接——点击后能看到这条查询是在哪个文件的哪一行调用的:

D:\myproject\books\views.py:23 in book_detail books = Book.objects.filter(author_id=author.id)

这就是你真正的排查起始点——不是靠经验蒙,是靠工具指出来的。


2 ~> EXPLAIN——读懂一条 SQL 的执行计划

找到了哪条 SQL 慢,下一步是理解 MySQL 具体是怎么执行它的。EXPLAIN 是数据库的"执行方案 PDF"——它告诉你 MySQL 准备怎么执行这条 SQL。

2.1 基础用法

EXPLAINSELECT*FROMbookWHEREauthor_id=42;

你会得到一张这样的表:

idselect_typetabletypepossible_keyskeyrowsExtra
1SIMPLEbookALLNULLNULL50000Using where

核心字段解释:

2.2type字段——最重要的一列

type代表 MySQL 的访问方式——也就是"怎么找到数据的"。从最好到最差排列:

NULL → 不需要查表(如 SELECT 1) system → 表里只有一行 const → 主键/唯一索引查一行 eq_ref → JOIN 中通过主键关联 ref → 非唯一索引等值匹配 range → 索引范围扫描(用了 > < BETWEEN 等) index → 全索引扫描(扫了整个索引) ALL → 全表扫描 ← 罪魁祸首

目标是把查询中的 ALL 和 index 降到至少 range 或 ref。看到type = ALL就是信号——这行查询扫描了整个表。

2.3key字段——实际用到的索引

如果key = NULL——没有用到索引。如果possible_keys不为空但key是 NULL——有索引但优化器选择不用它。这通常意味着索引的选择性太差或者统计信息过期。

2.4rows字段——预估扫描行数

优化器估算的"需要检查的行数"。rows不是实际返回的行数,而是扫描行数。rows = 500000即使最终只返回 10 行——速度仍然很慢。

2.5Extra字段——优化器的补充注释

Extra 内容含义严重程度
Using index覆盖索引——只扫描了索引,没碰数据行✅ 完美
Using where在 server 层使用了 WHERE 过滤⚠️ 普通
Using filesort额外排序操作——没用到排序索引🔴 需要优化
Using temporary使用了临时表(常用于 GROUP BY)🔴 需要优化
Using index condition使用了索引条件下推(ICP)✅ 还不错

3 ~> 真实案例——一个报表页面的慢查询定位

3.1 背景

一个日报报表——显示"最近 30 天借出过的图书列表"。Django View 代码:

defdaily_report(request):thirty_days_ago=timezone.now()-timedelta(days=30)records=BorrowRecord.objects.filter(borrowed_at__gte=thirty_days_ago)books=Book.objects.filter(borrow_records__in=records)returnrender(request,"report.html",{"books":books})

Debug Toolbar 抓到 SQL 面板:266 条查询,总耗时 1.8 秒。

3.2 用 EXPLAIN 定位

EXPLAINSELECT*FROMborrowrecordWHEREborrowed_at>="2026-06-17";

输出:

typekeyrowsExtra
ALLNULL487230Using where

全表扫描,48 万行。因为borrowed_at列上没有索引——MySQL 被迫扫整个表去找最近 30 天的记录。

3.3 修复

# models.pyclassBorrowRecord(models.Model):borrowed_at=models.DateTimeField(db_index=True)# ← 加索引# 或者用迁移:# ALTER TABLE borrowrecord ADD INDEX idx_borrowed_at (borrowed_at);

加了索引后 EXPLAIN 变成:

typekeyrowsExtra
rangeidx_borrowed_at1523Using index condition

扫描行数从 48 万降到 1500,查询耗时从 1.8 秒降到 48ms。


4 ~> 如何在 Django 中直接用代码输出 EXPLAIN

fromdjango.dbimportconnection qs=Book.objects.filter(author_id=42)# 获取 Django 生成的 SQLsql,params=qs.query.sql_with_params()# 用原生查询执行 EXPLAINwithconnection.cursor()ascursor:cursor.execute(f"EXPLAIN{sql}",params)forrowincursor.fetchall():print(row)

4.1 Java 开发者的类比

如果你来自 Java 背景——Hibernate 的show_sql相当于 Django 的connection.queries,但远没有 Debug Toolbar 好用。MySQL 的 EXPLAIN 在 Java 的 MyBatis 中也同样使用——原理一模一样。只是 Django Debug Toolbar 把它集成到了浏览器面板中,体验更直观。


思考 && 总结

慢查询排查的第一步不是"加索引"——而是:

  1. 用 Debug Toolbar 把 SQL 全部抓出来——看清楚到底有哪些查询、哪些是重复的。
  2. 用 EXPLAIN 看懂执行计划——type=ALLrows=500000是红旗,key=NULL是没用到索引。
  3. 确认问题点后再加索引——不要猜,要靠工具指出具体是哪条查询、哪张表。

结尾

上篇到这里。下篇进入索引实战——什么列该建索引、联合索引的最左前缀原则、以及 EXPLAIN 进阶优化技巧。

源码骑士 — 源码级拆解,从底层看透技术

👀关注:跟博主一起从源码视角深耕底层原理

❤️点赞:让优质内容被更多人看见

收藏:核心知识点存好,随用随查

💬评论:分享你的经验或疑问,一起交流

🔄一键四连:别忘了给博主一键四连!

🗡️寄语:工具先告诉你问题在哪,你再动手改——才是正确的排查姿势。

结语:上篇的光是 Debug Toolbar + EXPLAIN——让你的慢查询不再是盲猜。下篇进入索引实战优化。一键四连!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/17 15:30:20

Kali Linux渗透测试Android 9.0实战:从信息搜集到权限维持

1. 项目概述与核心目标最近在整理自己的渗透测试笔记&#xff0c;翻到了一个挺有意思的老项目&#xff1a;用Kali Linux对一台Android 9.0的手机进行安全测试。这个项目听起来有点“黑客范儿”&#xff0c;但本质上是一次完全可控、用于学习和验证移动设备安全性的内部演练。很…

作者头像 李华
网站建设 2026/6/17 15:25:00

SPI通信协议深度解析:从寄存器操作到中断与错误处理实战

1. SPI数据传输机制与错误处理详解&#xff1a;从寄存器操作到中断控制搞嵌入式开发&#xff0c;SPI&#xff08;Serial Peripheral Interface&#xff09;几乎是绕不开的通信协议。从简单的EEPROM读写到复杂的传感器数据采集&#xff0c;SPI以其简单、高速、全双工的特性&…

作者头像 李华
网站建设 2026/6/17 15:20:59

终极指南:如何用ComfyUI-LTXVideo解决你的AI视频生成难题?

终极指南&#xff1a;如何用ComfyUI-LTXVideo解决你的AI视频生成难题&#xff1f; 【免费下载链接】ComfyUI-LTXVideo LTX-Video Support for ComfyUI 项目地址: https://gitcode.com/GitHub_Trending/co/ComfyUI-LTXVideo 你是不是也遇到过这些问题&#xff1f;&#x…

作者头像 李华
网站建设 2026/6/17 15:20:30

基于NXP S12 MCU的小型发动机ECU参考设计实战解析

1. 项目概述&#xff1a;从零开始构建小型发动机ECU如果你是一位嵌入式工程师&#xff0c;或者对汽车电子、小型动力设备控制感兴趣&#xff0c;那么“发动机电子控制单元”对你来说一定不陌生。它就像是发动机的大脑&#xff0c;负责接收曲轴位置、进气压力、水温等各路传感器…

作者头像 李华
网站建设 2026/6/17 15:18:32

从KITTI Raw Data到LIO-SAM适配包:定制化数据集制作全流程解析

1. KITTI数据集与LIO-SAM适配的核心挑战 当你第一次拿到KITTI Raw Data时&#xff0c;可能会觉得这就像是一箱未经分类的乐高积木——所有零件都在&#xff0c;但直接用来拼装特定模型&#xff08;比如LIO-SAM&#xff09;却总差那么几个关键连接件。我去年在部署自动驾驶小车时…

作者头像 李华