想象一个周末的傍晚,你所在的城市突然大堵车,交警大队的电话被瞬间打爆——“这条路为什么走不动了?”其实,每一条 SQL 查询就像城市里的一辆车,数据库就是这座城市的庞大路网。当某条 SQL 执行得异常缓慢,它就是一台“堵在路中央的抛锚车”,不仅自己过不去,还拖垮身后排成长队的请求。MySQL 慢查询,正是数据库世界里最需要被揪出来的“堵车元凶”。 治理堵车,交警的第一步是调出路口的监控探头——这对应着数据库的慢查询日志;第二步,把一天的监控录像送到指挥中心做大数据分析——这对应着 pt-query-digest 这样的专业分析工具;第三步,派最有经验的交警去现场勘探——这对应着 EXPLAIN 执行计划;第四步,给关键路口修一条快速专用道——这对应着索引优化;第五步,为每辆车重新规划最优路线——这对应着SQL 改写。这套从发现到解决的完整方法论,就是后端工程师和 DBA 每天都在使用的“城市交通治理术”。
掌握这套系统化的慢查询定位与优化方法,是后端工程师从“能用数据库”走向“会调数据库”的分水岭。它让你在面对线上慢 SQL 时不再靠直觉猜,而是像老刑警一样按证据链排查——这是数据库性能调优最实用的通用工具箱。
目录
可视化演示
慢查询是数据库性能的头号杀手,定位和优化它是后端和DBA的日常必修课。本文带你搞懂慢查询日志开启、pt-que... 切换模式查看不同维度。
本节配有交互动效(进入视口后自动加载,约 3KB)。
慢查询日志的:核心原理 动态展示关键流程与数据流动 交互控件调节参数实时渲染 f(x) = transform(x) 核心概念的可视化呈现
日志开启配置:给路口装上监控探头
想要治理堵车,第一件事不是修路,而是在每个路口装上监控探头。没有探头数据,你连哪里堵、堵多久、堵多严重都无从知晓。MySQL 的慢查询日志,就是数据库世界的“路口监控探头”——它把所有执行时间超过预设阈值的 SQL 语句,原原本本地记录到日志文件中。
开启它非常简单。在 MySQL 配置文件(通常是 my.cnf 或 my.ini)中设置 slow_query_log = ON 打开开关,用 slow_query_log_file = /var/log/mysql-slow.log 指定日志落盘路径,再用 long_query_time = 1 告诉数据库“超过 1 秒就算慢”。注意这个参数的单位是秒且支持小数,0.5 就代表 500 毫秒。生产环境建议从 1 秒起步,观察日志量后再逐步收紧。
日志里的每条记录包含关键四要素:Query_time 是总执行时间,Lock_time 是锁等待耗时,Rows_examined 是扫描行数,Rows_sent 是实际返回行数。当 Rows_examined 远大于 Rows_sent 时,说明这条 SQL 翻遍全城只为送一份外卖——典型的无效扫描。
装上探头只是第一步,日志文件会像录像带一样越堆越多。下一步,我们需要一套能自动分析海量录像带的“指挥中心系统”。
- T_query(查询执行时间)这条 SQL 从开始执行到结束所花费的秒数,相当于外卖骑手从接单到送达的总时长。
- long_query_time(慢查询阈值)预先设定的“堵多久算慢”的标准。MySQL 默认值是 10 秒,对大多数业务过于宽松,生产环境通常调整为 1 秒或 0.5 秒。
- ≥(比较判断)将实际耗时与阈值比较,大于等于则条件成立。
- recorded(记录动作)条件满足后,MySQL 把该 SQL 连同执行信息写入慢查询日志文件,形成案发现场的完整存档。
日志分析工具:指挥中心的拥堵分析大屏
当全城的监控录像带堆积如山,你不可能一卷一卷人工回放。交警大队的指挥中心会把海量数据汇总成一块“大屏”——哪条路最堵、堵了多少次、集中在哪个时段,一目了然。在 MySQL 世界里,pt-query-digest 就是这块“大屏”。
它是 Percona Toolkit 工具包中最著名的慢查询分析工具。读取慢查询日志后,pt-query-digest 会把所有 SQL 按“指纹”聚合——所谓指纹,就是把具体值替换成占位符后的归一化模板,比如 SELECT * FROM users WHERE id = 1 和 WHERE id = 2 会归为同一类。然后统计每一类 SQL 的出现次数、总耗时、平均耗时、最大耗时,并生成图文报告。
报告中最重要的部分是 Query Review 表,它把 SQL 模板按总耗时从高到低排序。实际使用中,重点盯三个指标:总耗时(Sum)反映这类 SQL 对系统的总影响,与出现次数成正比;平均耗时(Avg)反映单次执行的性能;出现次数(Count)反映是否属于高频访问路径。总耗时和平均耗时双高,就是全村的“堵车王”。
大屏锁定了“堵车王”后,要找出它具体堵在哪个路口,还需要经验最丰富的交警到现场画一张勘探图——这就是 EXPLAIN 的舞台。
- N_slow(慢查询条数)从慢查询日志中统计出的记录总数,相当于指挥中心接到的堵车投诉电话数。
- N_total(总查询条数)数据库自启动以来处理的查询总数,可用
SHOW GLOBAL STATUS LIKE 'Questions'获取。 - R(慢查询占比)两者相除再乘以 100%,得到慢查询在全部查询中的占比,是宏观健康度指标。
EXPLAIN定位:交警现场的勘探报告
指挥中心锁定嫌疑路段后,交警立即出警勘探现场——是红绿灯配时不合理?前方修路?还是路口没有方向标识?在数据库世界,EXPLAIN 就是这张“现场勘探报告”。你只需在 SELECT 语句前加上 EXPLAIN 关键字,MySQL 就会告诉你这条 SQL 将如何执行,而且不会真正运行它。
执行 EXPLAIN SELECT ... 后,MySQL 返回一张结果表,每一行描述一个表访问步骤。最值得看的四列是:id 执行顺序、type 访问类型、key 实际使用的索引、rows 估算扫描行数。type 是勘探报告的核心,从好到差依次为:system > const > eq_ref > ref > range > index > ALL。ALL 表示全表扫描,是报告里画满红叉的部分。
rows 列不是精确值,而是优化器基于统计信息估算的扫描行数,rows 越小说明访问路径越精准。type=ref 表示通过普通二级索引定位,是最常见的优化目标;type=range 表示索引范围扫描,多来自 BETWEEN 或 IN;type=ALL 则是必须处理的红色警报。结合 filtered 可以估算最终命中的行数比例。
勘探完毕,确认是“路口设计不合理”之后就可以动手了——在关键路口加装快速专用车道,这正是索引的用武之地。
- R_scan(扫描行数)EXPLAIN 输出中的 rows 列,表示优化器估计需要扫描的行数,相当于交警预估这个路口要通过的车辆总数。
- R_match(实际命中行数)经过 WHERE 条件层层过滤后真正有用的行数,是真正需要送达的目的地车辆。
- percent(过滤百分比)R_match 除以 R_scan,衡量扫描效率。值越低,浪费的扫描越多,SQL 越需要优化。
索引优化策略:修建快速专用车道
堵车的路口经勘探发现,问题在于所有左转车辆和直行车辆混在一起抢道。解决办法是修一条“左转快速专用道”,让车辆进入后直接到达目的地。数据库里的索引正是这个机制:为某一列或某几列建立一份有序的目录,查询时像查字典一样直接翻到目标页,而不是逐页翻阅整本书。
MySQL 最常用的是 B+Tree 索引。当我们对多列建立复合索引(比如 INDEX(a,b,c))时,就引出了最左前缀法则:查询必须先用到索引最左列 a,索引才会被启用。只要 a 在查询条件中,无论 b、c 是否出现,a 都能大幅减少扫描量。另外,覆盖索引指索引中已包含查询所需的全部列,可以免去回表(回到主键索引取完整行)的额外开销,相当于“高速终点直达”。
但加索引并非万能。索引失效的典型场景有:对索引列使用函数(如 WHERE YEAR(create_time)=2024)、隐式类型转换(字符串列与数值比较)、LIKE 以通配符开头(LIKE '%abc')。建索引前还要评估列的选择性——如果某列只有“男/女”两种值,区分度太低,索引形同虚设。
车道修好后,如果司机的驾驶路线本身就很绕,依然快不起来。所以下一项工作,是为每一辆“慢车”重新规划最优行驶路线。
- COUNT(DISTINCT col)(去重值个数)这一列有多少种不同的取值,相当于一个城市划分成多少个区县。
- COUNT(col)(总行数)整张表有多少行数据,相当于整个城市的总人口。
- S(选择性指标)两者相除得到选择性。S 越高,索引越能精准筛选,建索引的价值越大。
SQL改写技巧:重新规划行车路线
同一段路程,新手司机可能左拐右绕频繁变道,老司机却走一条大道直达。SQL 的写法也如此——同一个业务目标,不同写法的执行效率可能相差几十倍。SQL 改写,就是给数据库这辆车重新规划一条更高效的行驶路线。
最常见的改写有三类。第一,把 SELECT * 改成 SELECT 需要的列,减少数据传输量,毕竟没人会开车绕城一圈就为了买瓶水。第二,深分页优化——LIMIT 1000000, 10 会让数据库先扫描 100 万行再全部丢弃,等价于开车到终点才发现导航导错了。更优的写法是延迟关联:先只查主键,再回表取出需要的数据。第三,多表关联尽量用 JOIN 而非大量 IN 子查询,并遵循小表驱动大表原则。
LIMIT offset, size 的成本模型可以这样理解:总代价约等于扫描 offset + size 行的成本。当 offset 非常大时,主要开销来自前面 offset 行的无效扫描。改写策略是改用“上一次位置”(keyset pagination):WHERE id > last_id ORDER BY id LIMIT 10,让扫描量恒定不变。
路线规划得再好,还得看导航软件够不够聪明——MySQL 优化器就是车里的那位导航员。理解它的思考方式,才能写出它真正听得懂的 SQL。
- C_scan(单行扫描成本)数据库扫描一行数据所需的平均开销,相当于车辆行驶一公里的油耗。
- O(偏移量 offset)要跳过的前多少行,对应
LIMIT后的第一个参数。 - P(页大小)本次返回的记录数,对应
LIMIT后的第二个参数。 - C(总成本)扫描总量乘以单行成本。当 O 远大于 P 时,O 主导总成本,所以深分页代价极高。
优化器原理:聪明导航员的算路逻辑
同样输入“去机场”,高德地图会综合实时路况、红绿灯数量、限行规则,给出多条路线并标注推荐。MySQL 优化器就是这样的“导航员”——执行 SQL 前,它先基于统计信息估算所有可选执行计划的代价,然后选择代价最小的一套方案。
代价估算的核心数据来自两张统计表:mysql.innodb_table_stats 存表行数等基础信息,mysql.innodb_index_stats 存索引基数(索引去重值的数量)和数据分布。MySQL 为顺序读、随机读、比较操作、临时表等操作定义了不同的“权重单价”。优化器用这套模型为每种执行路径打分,分越低越优先。
查询重写是优化器的隐藏技能。例如 WHERE id IN (1,2,3) 可能被重写为多个等值条件的组合;EXISTS 子查询在特定场景下被改写成半连接(semi-join)。理解优化器后你会发现,与其研究各种偏门技巧,不如做好两件基础事:定期 ANALYZE TABLE 保持统计信息新鲜;避免在 WHERE 子句中使用函数,否则索引很可能被“弃用”。
有了一次成功的路段改造,交警队不会就此收工——真正有经验的城市交通管理者,会把经验固化为制度,让今天的堵点明天不再复发。这正是慢查询治理实践体系的精髓。
- W_seq(顺序读权重)按物理顺序扫描一页数据的代价单价,相当于普通公路每公里油耗。
- N_seq(顺序扫描页量)全表扫描预计要读多少页数据。
- W_idx(索引读权重)通过索引随机读一页的代价单价,通常高于顺序读,相当于高速每公里收费。
- N_idx(索引扫描页量)走索引预计访问的页数,因为索引树很矮,这个值往往远小于全表扫描量。
- Cost(总代价)两部分之和,优化器选择 Cost 最小的执行计划。
治理实践体系:从救火到防火
治理堵车最有经验的城市,靠的不是每次堵车后派交警去现场疏通,而是一套长效机制:实时路况预警、新开路段前的车流模拟、季度性的交通规划复盘。慢查询治理同样需要这样的“成熟制度”。
治理体系的第一层是监控告警。把慢查询日志接入 Prometheus 等监控系统,一旦慢查询数量或某类 SQL 耗时超过阈值就触发告警,让“火势”还在苗头时就被扑灭。第二层是压测验证。改动索引或上线重构 SQL 之前,先在测试环境用 sysbench 模拟线上流量,对比优化前后的性能指标,而不是上线后等用户投诉。
第三层是迭代优化:每轮优化后都要收集新数据,回到“日志 → 分析 → 定位 → 优化”的循环起点。团队还应把常用优化沉淀为 SQL 规范,例如“禁止 SELECT *”“分页必须带排序字段”“上线前必须走 EXPLAIN 评审”——把个人经验变成组织制度。整个体系是一个不停旋转的闭环:发现问题、验证方案、实施优化、复盘沉淀。
到此,你已经从“堵车时手忙脚乱的交警”成长为“统筹全局的交通局长”。把方法论变成习惯,慢查询就不再是深夜事故的元凶,而是可控可量化的日常管理对象。
- T_before(优化前耗时)改动前该 SQL 的平均执行时间,是堵车现场的历史数据。
- T_after(优化后耗时)改动后该 SQL 的平均执行时间。
- Speedup(加速比)两者相除。大于 1 说明优化有效,数值越大越好。汇报工作时,这个数字比“我优化了一下”有力得多。
AI 应用场景
场景一:大促前的 AI 智能巡检
阶段:数据准备与异常检测
解决的问题:传统大促前,DBA 手动分析慢查询日志耗时数小时且容易遗漏隐藏在深处的“慢 N+1”问题。AI 巡检系统自动抓取慢查询日志,用聚类算法把相似 SQL 归并为模式,再由大模型自动给出索引建议和改写建议,把数小时的人工排查压缩到分钟级。
具体案例:某电商平台在 618 大促前的例行巡检中,AI 助手自动发现“订单表按 user_id 分组统计”这条 SQL 的全表扫描量高达 2000 万行。AI 结合表统计信息推荐建立 (user_id, status) 复合索引,并改写掉一次多余子查询。优化后该 SQL 从 2.8 秒降到 0.03 秒,加速比接近 100 倍。
场景二:AI 数据库自治助手
阶段:模型推理与智能决策
解决的问题:普通工程师写 SQL 时无法实时评估性能风险。AI 助手在开发环境的数据库网关中接入执行计划分析,每次 EXPLAIN 由 AI 模型解读 rows、type 等指标,标记可疑的全表扫描并生成优化说明,相当于给每个开发者配了一位 24 小时在线的 DBA 副驾。
具体案例:某公司的内部 AI 编码助手中加入 SQL 检查插件,在代码评审阶段自动拦截 SELECT * 和无 WHERE 条件的 UPDATE。一位新入职工程师提交的报表查询被 AI 提示“存在深分页风险”,系统直接给出改写后的延迟关联示例,代码一次通过评审。
Python 演示
#!/usr/bin/env python3# -*- coding: utf-8 -*-"""MySQL slow query log analyzer: Top N slow SQL"""import refrom collections import defaultdict class SlowLogParser: def __init__(self, log_path): self.log_path = log_path self.queries = [] self.sql_stats = defaultdict( lambda: {"count": 0, "total_time": 0.0} ) def parse(self): with open(self.log_path, "r", encoding="utf-8") as f: exec_time = 0.0 sql_buf = "" for line in f: m = re.search(r"Query_time:\s+([\d.]+)", line) if m: exec_time = float(m.group(1)) continue if not line.startswith("#") and exec_time > 0: sql_buf += line.strip() + " " if line.rstrip().endswith(";"): sql = sql_buf.strip() self._record(sql, exec_time) exec_time = 0.0 sql_buf = "" def _record(self, sql, exec_time): sql_norm = re.sub(r"\d+", "?", sql) sql_norm = re.sub(r"'.*?'", "?", sql_norm) self.sql_stats[sql_norm]["count"] += 1 self.sql_stats[sql_norm]["total_time"] += exec_time self.queries.append((sql_norm, exec_time)) def top_n(self, n=10): items = sorted( self.sql_stats.items(), key=lambda kv: kv[1]["total_time"], reverse=True )[:n] return [(sql, v["count"], v["total_time"]) for sql, v in items] def report(self, n=10): print(f"parsed {len(self.queries)} slow queries") header = f"{'rank':<6}{'count':<8}{'total(s)':<12}sql template" print(header) print("-" * 70) for i, (sql, cnt, total) in enumerate(self.top_n(n), 1): preview = sql[:50] + ("..." if len(sql) > 50 else "") print(f"{i:<6}{cnt:<8}{total:<12.2f}{preview}") def main(): parser = SlowLogParser("mysql-slow.log") parser.parse() parser.report(10) if __name__ == "__main__": main()
m = re.search(r"Query_time:\s+([\d.]+)", line)从日志行中提取执行时间,这是判断“堵不堵”的直接证据。sql_norm = re.sub(r"\d+", "?", sql)把 SQL 中的数字替换成占位符,完成“指纹归一化”,让只有数字不同的同类 SQL 聚到一起。self.sql_stats[sql_norm]["count"] += 1累计该类 SQL 出现的次数,用于发现高频慢查询。self.sql_stats[sql_norm]["total_time"] += exec_time累计该类 SQL 的总耗时,这是排名时最重要的依据。key=lambda kv: kv[1]["total_time"]按总耗时从大到小排序,让“全城最大的堵点”排在最前面。parser.parse()入口处执行解析,一行代码吃进整个日志文件。
常见误区
误区我刚接触慢查询优化,觉得设得越小越好,干脆把 long_query_time 设成 0,把所有 SQL 全记下来,这样不是最安全吗?
正解阈值设得太小会让慢查询日志疯狂增长,像把整座城市每一辆车的行驶轨迹都录下来,存储很快爆掉,而且真正有价值的慢 SQL 会被淹没在海量记录中。合适的做法是从 1 秒起步,观察日志量后逐步调整。判断标准是:日志量只占系统总体的“黄金少数”,能精准暴露问题即可。
为何常见:初学者的心态是“宁可错杀一千,不能放过一个”,忽略了日志本身也有 IO 开销和存储成本,慢查询日志记录越密集,对线上性能的反噬越明显。
误区我用 EXPLAIN 一查,看到 type 是 ALL,立刻慌了,觉得必须马上加个索引,不然系统就要崩了。
正解type=ALL 只是表明走的是全表扫描,但全表扫描不等于慢。如果表只有几千行,全表扫描可能比走索引更快——就像在只有两辆车的小路口,装红绿灯纯属多余。正确做法是结合 rows 列看估算扫描行数,再对比实际表大小。如果扫描量远大于表行数的合理范围,或者该 SQL 执行频繁,才需要加索引。
为何常见:很多人把 ALL 当成了绝对错误,却没有理解 EXPLAIN 的本质是“执行方式的快照”,忽略了表大小、数据分布和 SQL 频率这些更基础的判断维度。
误区既然索引能加速查询,那我给每个查询条件列都建一个索引,这样无论怎么写 SQL 都能走索引,天下无敌。
正解索引不是免费的,每一棵 B+Tree 都要占用磁盘空间,每次 INSERT/UPDATE/DELETE 都要同步维护索引树,索引越多写入越慢。这就像城市里修了太多单行道,虽然方便了部分直达,却让整体路网变得拥堵。正确的做法是优先用复合索引覆盖高频查询,删除重复或低选择率的索引,让“路网密度”恰到好处。
为何常见:查询优化是显性收益,写入开销是隐性成本。初学者只看到了 SELECT 变快,忽略了写放大效应,等上线后写入性能暴跌才追悔莫及。
误区我建了复合索引 `(a,b,c)`,一直以为查询时必须写成 `WHERE a=1 AND b=2 AND c=3`,如果只写 b 和 c 就用不上索引,所以经常被迫给后端加一个无意义的 a 条件。
正解最左前缀法则说的是“查询条件中必须包含最左列 a,索引才能被用来定位”,并没有要求把 a 放在 WHERE 的第一个位置,也不要求 b、c 必须全部出现。WHERE b=2 AND a=1 和 WHERE a=1 AND c=3 都能用上索引的 a 部分。MySQL 优化器会帮你调整 WHERE 顺序。关键是理解:“从最左列开始连续匹配”是必要条件,不是“所有列必须全用上”。
为何常见:这个误区源于网上的简化表述被层层转述后走样,很多人只记住了“最左”两个字,而没有理解 B+Tree 索引按列顺序构造的本质。
面试题
Q1. MySQL 中控制慢查询是否开启的开关参数是?
- A.slow_query_log 开关参数← 正确答案
- B.long_query_time 阈值参数
- C.log_output 输出目标参数
- D.slow_query_log_file 路径参数
答案:A。slow_query_log 是总开关,置为 ON 才会开始记录慢查询;long_query_time 是判定阈值,log_output 控制日志输出到文件还是表,slow_query_log_file 指定文件路径。四个参数共同协作,但“开不开”由开关决定。
Q2. MySQL 默认的 long_query_time 阈值是多少秒?
- A.0.5 秒超时阈值
- B.1 秒执行时间
- C.10 秒执行时间← 正确答案
- D.5 秒执行时间
答案:C。MySQL 默认 long_query_time 为 10 秒。这个值对大多数互联网业务过于宽松,一条 3 秒的慢 SQL 在默认配置下不会被记录。生产环境通常需要手动调低到 1 秒或 0.5 秒。
Q3. EXPLAIN 输出中哪种 type 列值意味着全表扫描?
- A.type 值为 ALL 全表扫← 正确答案
- B.type 值为 ref 普通索引
- C.type 值为 range 范围扫
- D.type 值为 const 常量查
答案:A。ALL 表示全表扫描,是最差的访问类型,通常意味着扫描行数巨大,需要重点优化。ref 是普通索引等值匹配,range 是索引范围扫描,const 是通过主键或唯一索引定位,效率远高于 ALL。
Q4. 以下哪些情况会导致索引失效?
- A.对索引列使用函数运算← 正确答案
- B.对索引列做隐式类型转换← 正确答案
- C.查询条件中无任何索引列
- D.使用 LIKE 前导通配符查← 正确答案
答案:ABD。对索引列使用函数(如 YEAR(date))会让 B+Tree 无法按序定位;隐式类型转换会破坏索引列的值比较;LIKE 以通配符开头导致无法从树根开始匹配后缀。选项 C 是“本来就没索引可用”,不属于“索引失效”。
Q5. 关于最左前缀法则的正确理解是?
- A.查询条件必须覆盖全部索引
- B.需从复合索引最左列开始← 正确答案
- C.不使用最左列也能走索引
- D.复合索引任意列都能生效
答案:B。最左前缀法则要求查询必须包含复合索引的最左列,索引才能被用来加速定位。但不是必须覆盖全部列,也不是要求 WHERE 里最左列写在第一个位置。不使用最左列时,索引无法用于定位。
Q6. 深分页 LIMIT 1000000,10 的优化方案有哪些?
- A.使用延迟关联先查主键← 正确答案
- B.用上一页最后 ID 做游标← 正确答案
- C.把 LIMIT 数值继续调大
- D.增加 SELECT 返回列数
答案:AB。延迟关联先利用索引查主键,再回表取完整数据,避免扫描大量无关行;游标分页(WHERE id > last_id)让扫描量恒定。调大 LIMIT 只会更慢,增加返回列会增加 IO 开销,都是反向操作。
Q7. 关于覆盖索引的优势描述最准确的是?
- A.覆盖索引可以完全避免回表← 正确答案
- B.覆盖索引可以避免排序操作
- C.覆盖索引可以让全表扫描提速
- D.覆盖索引可以减少所有锁等待
答案:A。覆盖索引指索引中包含了查询所需的全部列,InnoDB 无需回主键索引取完整行,减少一次随机 IO。它并不保证避免排序,也不能让全表扫描变快,更与锁等待无直接关系。
Q8. EXPLAIN 中 KEY 列为 NULL 代表什么?
- A.查询发生了死锁需要重试
- B.该查询没有使用任何索引← 正确答案
- C.索引已损坏需要重建
- D.查询返回了空结果集
答案:B。KEY 列显示实际使用的索引名,NULL 说明查询没有用到任何索引,大概率走全表扫描。这和张三的 WHERE 条件没命中索引规则有关,也可能是无索引可命中。死锁、索引损坏、空结果集与 KEY 列无关。
Q9. 优化器进行代价估算时通常考虑哪些因素?
- A.表行数和索引基数统计← 正确答案
- B.WHERE 条件的过滤因子← 正确答案
- C.数据页位于内存或磁盘← 正确答案
- D.客户端与服务端网络延迟
答案:ABC。优化器依赖统计信息估算行数(A),用过滤因子估算 WHERE 条件的命中率(B),内存与磁盘的访问代价权重不同(C)。网络延迟属于客户端到服务器的传输环节,不在 MySQL 优化器的代价模型范围内。
Q10. pt-query-digest 分析报告主要按哪个指标排序?
- A.按平均耗时从高到低排
- B.按总耗时从高到低排列← 正确答案
- C.按查询次数从高到低排
- D.按锁等待时间从高到低排
答案:B。pt-query-digest 的报告默认按总耗时(Sum)排序,因为总耗时反映该类 SQL 对系统的累计影响,等于平均耗时乘执行次数。只看平均耗时容易漏掉高频但单次不慢的 SQL,只看次数又会忽略真正耗时的重 SQL。