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

📅 2026/6/17 15:34:25
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 中直接用代码输出 EXPLAIN4.1 Java 开发者的类比思考 总结结尾你的接口为什么慢上——Django Debug Toolbar EXPLAIN从看到慢查询到读懂它文章简介接口突然从 50ms 变成 800ms你第一反应是什么“加缓存”“改索引”“换 SSD”——但你没看到实际的慢查询长什么样。上篇聚焦性能排查的第一步用 Django Debug Toolbar 把所有 SQL 查询摊在桌面上再通过 MySQL 的 EXPLAIN 命令读懂每条查询的执行计划。从安装配置 Debug Toolbar 开始到定位出具体是哪个 View 的哪条 ORM 触发了慢查询再到 EXPLAIN 输出中的 type / key / rows / Extra 四个核心字段的解读。配有两个真实案例——一个 N1 问题的可视化暴露一个因为缺少索引导致的全表扫描。 个人主页源码骑士❄专栏传送门《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 条是重复的这就是 N1 的可视化铁证——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_idauthor.id)这就是你真正的排查起始点——不是靠经验蒙是靠工具指出来的。2 ~ EXPLAIN——读懂一条 SQL 的执行计划找到了哪条 SQL 慢下一步是理解 MySQL 具体是怎么执行它的。EXPLAIN 是数据库的执行方案 PDF——它告诉你 MySQL 准备怎么执行这条 SQL。2.1 基础用法EXPLAINSELECT*FROMbookWHEREauthor_id42;你会得到一张这样的表idselect_typetabletypepossible_keyskeyrowsExtra1SIMPLEbookALLNULLNULL50000Using 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_agotimezone.now()-timedelta(days30)recordsBorrowRecord.objects.filter(borrowed_at__gtethirty_days_ago)booksBook.objects.filter(borrow_records__inrecords)returnrender(request,report.html,{books:books})Debug Toolbar 抓到 SQL 面板266 条查询总耗时 1.8 秒。3.2 用 EXPLAIN 定位EXPLAINSELECT*FROMborrowrecordWHEREborrowed_at2026-06-17;输出typekeyrowsExtraALLNULL487230Using where全表扫描48 万行。因为borrowed_at列上没有索引——MySQL 被迫扫整个表去找最近 30 天的记录。3.3 修复# models.pyclassBorrowRecord(models.Model):borrowed_atmodels.DateTimeField(db_indexTrue)# ← 加索引# 或者用迁移# ALTER TABLE borrowrecord ADD INDEX idx_borrowed_at (borrowed_at);加了索引后 EXPLAIN 变成typekeyrowsExtrarangeidx_borrowed_at1523Using index condition扫描行数从 48 万降到 1500查询耗时从 1.8 秒降到 48ms。4 ~ 如何在 Django 中直接用代码输出 EXPLAINfromdjango.dbimportconnection qsBook.objects.filter(author_id42)# 获取 Django 生成的 SQLsql,paramsqs.query.sql_with_params()# 用原生查询执行 EXPLAINwithconnection.cursor()ascursor:cursor.execute(fEXPLAIN{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 把它集成到了浏览器面板中体验更直观。思考 总结慢查询排查的第一步不是加索引——而是用 Debug Toolbar 把 SQL 全部抓出来——看清楚到底有哪些查询、哪些是重复的。用 EXPLAIN 看懂执行计划——typeALL和rows500000是红旗keyNULL是没用到索引。确认问题点后再加索引——不要猜要靠工具指出具体是哪条查询、哪张表。结尾上篇到这里。下篇进入索引实战——什么列该建索引、联合索引的最左前缀原则、以及 EXPLAIN 进阶优化技巧。源码骑士 — 源码级拆解从底层看透技术关注跟博主一起从源码视角深耕底层原理❤️点赞让优质内容被更多人看见⭐收藏核心知识点存好随用随查评论分享你的经验或疑问一起交流一键四连别忘了给博主一键四连️寄语工具先告诉你问题在哪你再动手改——才是正确的排查姿势。结语上篇的光是 Debug Toolbar EXPLAIN——让你的慢查询不再是盲猜。下篇进入索引实战优化。一键四连