十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Navicat分析SQL性能:从执行计划到慢查询排查

Navicat分析SQL性能:从执行计划到慢查询排查 标题看着朴素——用Navicat分析SQL性能。但只要你碰过数据库就一定遇到过类似的画面接口响应突然从200ms飙到3秒报表跑十分钟还没出来数据库监控那边CPU已经顶到80%了可你打开Navicat把SQL贴进去它跑得又快又顺。这就尴尬了。工具没坏SQL也没变问题是这条SQL在真实数据量、真实并发下到底怎么执行的你根本看不到。我一开始排查慢SQL习惯先上命令行EXPLAIN加手工观察耗时后来发现效率太低而且不同数据库的命令板式还不一样。直到我把Navicat认真用起来把解释执行计划、客户端统计、性能监视器这几个功能串成一套流程才真正觉得顺畅。这篇文章想写给所有日常写SQL、维护数据库、或者被后端接口慢查询折磨过的人把Navicat在SQL性能分析上的能力掰开揉碎讲一遍。它是分析入口不是魔法道具关键在于你会不会读它给出来的那些指标。1. 搞清楚Navicat能帮我们做SQL性能分析的哪些事1.1 先分清“慢”是SQL本身的问题还是系统的压力SQL性能问题从来不是单点问题。我习惯把“慢”分四类第一类叫“算得慢”大概率是索引没走到全表扫描一遍第二类叫“取得多”SQL返回几千上万行每行又带一大堆冗余字段传输和序列化全花时间第三类叫“堵得慢”不是SQL跑得慢是行锁、表锁、长事务把它卡住了第四类叫“资源慢”数据库服务器CPU、IO、内存已经满负荷谁来都慢。为了直观对照我平时排查时会按这个表来判断方向问题表现可能原因排查侧重点单条SQL跑了很久怎么改都慢缺索引、扫描范围大、排序临时表EXPLAIN、key、rows、Extra接口偶尔慢不是每次都慢锁等待、并发量波动processlist、InnoDB事务、慢查询时间点返回数据很多网络都卡select *、无limit、N1联表客户端统计的传输字节、结果集行数整个实例响应都差CPU/IO/连接池打满性能监视器、慢日志、连接数曲线这四类问题的解决方式完全不同。很多人一上来就建议“加索引”其实有一半问题加了索引也没用比如返回数据量过大、或者被锁阻塞。先把慢的类别定下来后面才不会用错药。1.2 Navicat里真正有用的五个分析入口折腾了一段时间我总结出Navicat里真正和性能分析强相关的五个入口其他花哨功能基本用不上。第一个是查询编辑器拿来看SQL、改SQL、跑SQL第二个是EXPLAIN/解释看执行计划第三个是客户端统计量化一条SQL到底传输了多少、往返了多少次第四个是性能监视器看整个数据库实例的状态第五个是查询历史重新找回之前跑过的SQL和耗时。入口位置主要解决查询编辑器连接名上点右键新建查询写SQL、格式化、调整语法EXPLAIN/解释查询编辑器工具栏按钮看索引使用和扫描类型客户端统计查询编辑器菜单或CtrlShiftN量化耗时、传输字节、往返次数性能监视器工具或主界面菜单观察实时连接数、命令频率、慢查询查询历史查询编辑器右侧面板找回历史执行过的SQL和耗时不同版本、不同数据库类型的菜单略有差异但核心路径差不多。Navicat本身不是数据库引擎它不负责优化只负责把执行计划和资源状态摊开给你看。最终“改索引”“改SQL”的判断还是得你自己下。1.3 别把EXPLAIN当圣旨理解工具边界很多新手第一次点开“解释”按钮看到一堆type、rows、key感觉高大上但容易被rows预估行数骗了。MySQL的EXPLAIN默认只是优化器模拟出来的执行路径并不真实执行SQL所以它评估的rows和真实运行时的实际行数经常差一个数量级。PostgreSQL的Explain如果勾选Analyze选项则真的会把SQL跑一遍返回实际行数和耗时。决定用哪个功能前先搞清楚当前数据库的EXPLAIN口径是什么。我在实际工作中一直保留这些认知MySQL的EXPLAIN不执行改SQL后要看真实耗时建议再跑一次客户端统计。EXPLAIN ANALYZE会真实执行生产库上慎用最好在只读副本上跑。执行计划是基于当前统计信息的如果表很久没做ANALYZE优化器估算可能失真。同样的SQL在MySQL、PostgreSQL、SQL Server里解释出来的结果长得很不一样别用一套习惯硬套所有数据库。2. 分析前把环境准备好别让“脏数据”骗了你2.1 连接配置为什么会影响性能判断我见过不少同事直接在Navicat默认配置下连一个开发库跑性能分析结果被误导。开发库数据量小不会暴露索引问题生产库又怕慢查询拖慢业务不敢随便跑。我的建议是准备一个和生产数据量规模差不多的测试库或只读副本至少表行数、索引结构要和线上接近否则EXPLAIN看到的rows和真实差距很大。连接设置上还有两个细节容易被忽略一个是连接超时时间如果通过跳板机或跨网段连接网络延迟会混进客户端统计里让你误以为SQL本身很慢另一个是自动提交模式跑UPDATE、DELETE之前一定先开事务核对好影响行数再提交。我不建议直接用管理员账号跑分析最好用一个只有只读权限的账号安全边界清晰。还有一点很现实开发环境往往有定时清理任务表数据量不稳定。如果你在分析前不查一下表的当前行数、索引情况所谓的“性能调优”很容易变成在错误数据规模上做无用功。2.2 把慢SQL原样贴回查询编辑器并保留现场拿到一条慢SQL不要手工去改先原样贴回带格式化的查询编辑器保持缩进清晰。如果是线上反馈某页面慢就把SQL、当时的参数值、表行数、索引情况全部记录下来最好截图存档。我的习惯是建一个“慢SQL台账”表格把日期、SQL、耗时、explain结果、处理人、处理结果都记下来后面再做相同问题能少走弯路。在Navicat里贴完SQL后第一件事不是直接跑而是先检查当前连接的是哪个库、哪个环境。我吃过亏有时候同时开着两个连接一个是测试库一个是生产库在测试库查了半天没问题最后发现慢的那条SQL是在生产库的大数据量下执行的环境都搞错了还分析什么。2.3 用客户端统计拿到量化数据Navicat有个很容易被忽略的功能叫“包含客户端统计”一般快捷键CtrlShiftN。打开后每次执行一条SQL下方会显示执行时间、服务器往返次数、传输字节数、请求时间等。我一般只用它的两个数值总耗时和传输字节数。比如一个定时任务SQL要取100万行做汇总客户端统计里显示传输了50MB数据页面一次展示却只需要20条记录。这时候瓶颈就很明显了——不一定是数据库慢而是数据量大。我曾经把一个报表接口从查询所有字段改成只查必要字段客户端统计的传输字节数从32MB跌到1.2MB接口整体从4秒变成0.6秒索引一行没加。这个功能是个“照妖镜”能帮你区分到底是处理慢还是传输慢。很多人说SQL性能差其实跑完一看执行时间几十毫秒全浪费在结果集太大了。2.4 把慢查询日志打开让问题SQL自动浮出来不要总是自己猜哪条SQL慢数据库自带的慢查询日志是你最可靠的雷达。MySQL里可以这样打开show variables like slow_query_log; set global slow_query_log ON; set global long_query_time 1;这会把执行超过1秒的SQL记录到日志文件如果log_outputTABLE就会写入mysql.slow_log表。你可以在Navicat里新建一个查询来确认配置是否生效也可以直接查看慢日志文件的路径show variables like slow_query_log_file;SQL Server里可以使用扩展事件或DMV查询Oracle有AWR报告需要相应许可不过日常最常用的还是MySQL的慢日志。日志文件可以用Navicat直接打开当文本看或者配合“查询历史”面板确认哪些SQL经常出现。这里必须提醒一句生产环境的慢日志别长期全量开临时开一段时间定位问题后要记得关。慢日志本身也有IO开销总开着会影响业务。3. 聚焦执行计划一次EXPLAIN看懂SQL的“体检报告”3.1 Navicat里打开执行计划的几种姿势Navicat点开查询编辑器后把SQL写好可以直接在工具栏找“解释”或“EXPLAIN”按钮也可以在查询菜单里选“EXPLAIN/解释SELECT”。MySQL 8.0以后还可以手工写EXPLAIN ANALYZE SELECT * FROM orders WHERE customer_id 100;这条会真实执行SQL并返回每一步耗时和实际行数。记住了MySQL的普通EXPLAIN不执行SQL只是优化器估算EXPLAIN ANALYZE才真实执行所以在生产库上慎用。PostgreSQL在Navicat里弹出的“Explain”对话框中勾选“Analyze”选项效果类似。我自己的使用习惯是先用普通EXPLAIN看执行计划的形状确认是否走索引、是否有临时表改完SQL后再用真实执行确认耗时。这样可以避免在生产上反复跑慢SQL。3.2 执行计划列怎么看type、key、rows、Extra拿MySQL的EXPLAIN结果来说简单理解id是执行步骤编号select_type表示子查询还是联合查询table是访问的表type是访问类型possible_keys和key是可能用到的和实际用到的索引rows是优化器估计要扫描的行数filtered是过滤比例Extra是补充信息。type字段是判断SQL健康度的最重要指标。我按自己的经验把type做一个排序表方便新手快速对号入座type值含义判断const按主键或唯一索引等值查询最多返回一行最好eq_ref联表时按主键或唯一索引匹配很好ref普通索引等值匹配可能返回多行正常range索引范围扫描如between、、、in可接受index扫描二级索引也会遍历很多行需要注意ALL全表扫描典型慢查询信号只要看到ALL第一反应应该是“这句SQL是不是没走索引”。但也不要一棒子打死极少数小表比如几百行的字典表全表扫描反而比走索引更快优化器是有判断的。那种几十万上百万行的大表出现ALL基本就是要处理的信号。rows预估行数也只是参考不是实际扫描行数。尤其表统计信息不准时rows可能偏差很大。想确认实际行数可以用EXPLAIN ANALYZE或者真实执行后看客户端统计。3.3 重点关注Extra里的“坏味道”Extra字段里有很多关键词新手容易忽略但恰恰是它最能说明SQL执行过程中的额外操作。我平时会特别留意这几个Using filesort表示需要额外排序。常见于order by字段没有索引或者索引的列顺序和排序要求不匹配。数据量小无所谓数据量大就会拖慢。Using temporary使用了临时表。常见于group by、union、某些复杂的子查询。临时表会占用内存或磁盘性能开销通常不小。Using where在存储引擎层拿到记录后又用where过滤本身不是坏事但如果同时是ALL就需要重点看。Using index覆盖索引表示查询列都在索引里不用回表。这是好信号。举个例子一条SQL查的是select *走普通索引回表Extra可能只有Using where如果改成select order_no, created_at而联合索引正好包含这两列Extra会变成Using index性能会明显好一截。覆盖索引算是性价比很高的优化手段不需要额外加太多索引只需要把常用查询列尽量塞进同一个索引里。3.4 一个分页慢查询的完整调优案例去年我遇到一个订单列表接口用户翻到第100页时接口响应从0.3秒慢慢涨到12秒几乎不可用。原始SQL大概是SELECT o.order_no, o.customer_id, o.amount, o.created_at, c.name FROM orders o LEFT JOIN customers c ON o.customer_id c.id ORDER BY o.created_at DESC LIMIT 20 OFFSET 100000;从逻辑上LIMIT 20 OFFSET 100000会让引擎先扫描并跳过10万行再取20行加上JOIN和回表自然很慢。打开EXPLAIN后能看到orders表扫描了十来万行Extra里还有Using filesort说明排序也走了文件排序。优化方案是“延迟关联”先只查主键和排序字段再回表SELECT o.order_no, o.customer_id, o.amount, o.created_at, c.name FROM ( SELECT id FROM orders ORDER BY created_at DESC LIMIT 20 OFFSET 100000 ) d JOIN orders o ON d.id o.id LEFT JOIN customers c ON o.customer_id c.id ORDER BY o.created_at DESC;再跑EXPLAIN内层子查询用到了二级索引扫描行数从10万降到了索引切片的20条左右外层也是按主键回表。接口响应从12秒降到80毫秒以内。这个案例很典型不是SQL写错了而是翻页场景对数据集的定位方式不对。Navicat里把这个SQL优化前后的EXPLAIN截图一对比谁都看得明白。4. 用监视器盯住数据库的“实时血压”4.1 打开性能监视器先看三个曲线单条SQL的EXPLAIN只能解决“这一条为啥慢”但生产环境经常是“整个库突然变慢”。这种时候我第一个动作是打开Navicat的“性能监视器”一般在工具栏或“工具”菜单里。里面能看到连接数、网络流量、命令执行频率比如SELECT、INSERT、COMMIT、慢查询数等折线图。我一般先看三条连接数是不是持续居高不下慢查询曲线有没有尖峰命令频率是否正常。如果慢查询出现尖峰多半是定时任务在整点跑了复杂SQL如果连接数被占满说明连接泄漏或者连接池配置不合理。监视器适合看整体趋势不适合定位单条SQL但它能帮你快速锁定问题发生的时间段后面排查就更有目的性。4.2 在processlist里“抓现行”如果知道某段时间慢但不知道具体哪条SQL可以直接用命令SHOW FULL PROCESSLIST;Navicat里也有连接/进程列表的入口。重点关注Command列是Query但执行时间很长的会话拿到它的SQL内容和用户判断是谁在跑。必要时可以和业务方确认后用KILL杀掉会话KILL 连接ID;这个操作要非常慎重尤其不能随便kill生产库正在执行的写入会话。还有个办法是查information_schema.processlist把结果按执行时间排序SELECT id, user, host, db, command, time, state, LEFT(info, 200) AS sql_text FROM information_schema.processlist ORDER BY time DESC;这比肉眼刷监控高效很多。不过要注意这个查询看的也是当前时刻的会话快照如果问题是间歇性的可能抓不到需要配合监视器曲线或者定时轮询。4.3 锁等待和长事务SQL本身不慢但被“挡住”了还有一类慢查询你单独把它拿出来在Navicat里跑几十毫秒就完成但线上就是超时。多半是锁等待。MySQL里可以通过SELECT * FROM information_schema.innodb_trx; SELECT * FROM sys.innodb_lock_waits;看到事务的开始时间、等待锁的组合。一个典型的翻车现场业务代码里开了事务里面有查询又插入但忘了提交后续同一行数据的UPDATE全卡在那等锁。这种问题SQL调优没有用得先处理掉那个长事务。排查锁时不要只看当前processlist因为被阻塞的会话还在真正卡住别人的是一个长时间未提交的事务。要顺着“谁在等锁”找到“谁持锁”看持锁事务的执行时间和状态再考虑是等它结束还是联系负责人干预。5. 常见问题速查与我的几条土办法5.1 索引失效的典型翻车场景这是我在帮助同事排查时最常遇见的几类整理成一个速查表场景示例原因怎么改隐式类型转换where phone 13800138000phone是varchar传数字改参数类型保持字符串函数包裹列where DATE(create_time) 2025-01-01列上函数让索引失效改成范围比较 和 下一天的写法前导通配符where name like %张三%通配符在前B树无法定位改全文索引或分词查询联合索引不满足最左前缀联合索引(a,b)但只查b没从最左列开始调整索引列顺序或新增索引OR条件有非索引列where a 1 or b 2OR可能导致全表改写为UNION排序字段不在索引里order by update_time索引只有id文件排序添加联合索引覆盖排序字段这已经是老生常谈但每次线上出问题翻来覆去就是这几类。值得打印出来贴屏幕旁边。遇到一个慢SQL先对照这个表排查一遍大概能解决一半问题。5.2 除去索引外还有这些“看不见”的慢索引不是万能药。有些SQL即使走索引也慢因为返回数据量太大。比如一个接口后台要导出Excel一次性select出全表传输和网络都兜不住。Navicat客户端统计能帮你看到传输字节数。联表过多也常见。我之前见过一条SQL JOIN了七八张表业务上看着合理但一次查询要产生多次回表和匹配。我的建议是拆解成两步先查出主表ID再根据ID集合批量查关联数据对普通报表更友好。还有一个技巧在Navicat里把查询优化为“只查必要列”解决了一半以上的无脑慢。select *是最大的偷懒也是最常见的坑。业务接口可能只需要两三个字段结果把几十列全查出来尤其是在宽表场景下回表成本和传输成本都白涨。5.3 养成三个习惯能省掉一大半排查时间第一建基线。每张核心表记录当前行数、索引清单、近期慢日志数量作为后续判断参考。这样两周后有人喊慢你直接拿基线对比很快就能知道是不是数据量增长导致的。第二用对比法。在Navicat里把优化前后两条SQL的“客户端统计”和EXPLAIN放一起一个看耗时一个看type/rows/Extra就能知道改动效果到底来自哪里。很多同学只说自己“加了索引”但说不清索引到底改变了哪一步这就是对比没做透。第三分层验证。先在开发库小数据量验证逻辑正确性再在生产库只读场景下复核执行计划最后再考虑切换流量。这个习惯能避免很多生产事故。5.4 关于Navicat使用的小结也算我的个人心得用了这么多年Navicat说实话它不是性能分析功能最全的工具MySQL官方Workbench、第三方DBeaver等也有类似能力。但很多人最大的问题不是没有工具而是拿到工具不知道怎么读指标。如果你能养成“任何慢SQL都先在Navicat里看一次执行计划”的习惯并且会读type、key、rows、Extra这四个字段再配合客户端统计看传输量性能分析至少要少踩一半的坑。我自己还有一个很“土”的仪式感每次改完SQL都会顺手把EXPLAIN的结果截图放到需求文档里和改前做对比。这样过几个月项目交接、做回归直接翻文档就能定位不用重新推理一遍。你也不妨试试。
返回列表