3. 慢查询定义与分类:数据库慢查询的判定标准与常见类型

在 BFF 聚合层场景中,慢查询是连接池耗尽、请求堆积、接口超时的直接诱因。要精准调优连接池,必须先建立统一的慢查询判定标准,并能够快速识别慢查询的类型。本章将定义慢查询的量化指标,并分类剖析常见的慢查询模式。

3.1 慢查询的判定标准

数据库慢查询并非仅由执行时间决定,而是由执行时间与扫描行数两个核心维度共同判定。单一维度的阈值设置往往会导致误判或漏判。

判定维度 典型阈值(参考) 说明
执行时间 > 100ms(OLTP)
> 1s(OLAP/聚合查询)
超过该时间的查询被视为慢查询。BFF 层建议阈值设为 200ms,超过则触发告警。
扫描行数 > 1000 行(小表)
> 10000 行(大表)
扫描行数远大于返回行数时,即使执行时间未超标,也需关注索引效率。
逻辑读次数 > 1000 次 InnoDB 缓冲池的逻辑读次数,反映查询对内存的消耗。
临时表使用 出现 Using temporary 查询使用了磁盘临时表,通常意味着排序或分组操作未命中索引。
判定原则:执行时间超标是“结果”,扫描行数超标是“原因”。调优时应优先关注扫描行数,而非仅降低时间阈值。

3.2 常见慢查询类型

根据 BFF 聚合层的实际故障案例,慢查询可归纳为以下四种核心类型。每种类型都有其典型的 SQL 特征和执行计划表现。

3.2.1 全表扫描

查询未使用任何索引,导致数据库必须扫描整张表的所有数据页。在 BFF 层,常见于对非索引字段进行 LIKE '%keyword%' 模糊查询或 WHERE 条件中函数包裹索引列。

-- 典型全表扫描示例
SELECT * FROM orders WHERE status = 'PENDING';
-- 若 status 字段无索引,则扫描全表

3.2.2 索引失效

即使表上存在索引,由于 SQL 写法不当,优化器仍选择放弃索引。BFF 层常见索引失效场景包括:

  • 隐式类型转换:字段为 varchar,传入 INT 类型。
  • 不满足最左前缀:复合索引 (a, b, c),查询条件只用到 b 或 c。
  • 使用 OR 条件:当 OR 两侧的字段并非都有索引时,优化器可能放弃索引。
-- 索引失效示例(隐式转换)
SELECT * FROM user WHERE phone = 13800138000;  -- phone 为 varchar,传入数字

3.2.3 JOIN 过多

BFF 聚合层常需关联多张业务表。当 JOIN 数量超过 3 张且关联字段无索引时,会产生笛卡尔积式的扫描。尤其在使用 LEFT JOIN 驱动表选择错误时,性能急剧下降。

-- JOIN 过多示例(3 表关联,无索引)
SELECT o.id, u.name, p.product_name
FROM orders o
LEFT JOIN user u ON o.user_id = u.id
LEFT JOIN product p ON o.product_id = p.id
WHERE o.create_time > '2024-01-01';

3.2.4 子查询嵌套

MySQL 5.x 版本对子查询的优化能力有限,尤其是 IN 子查询和 EXISTS 子查询,可能导致外层查询对每一行都执行一次子查询(相关子查询)。BFF 层应优先使用 JOIN 重写。

-- 子查询嵌套示例(相关子查询)
SELECT * FROM orders o
WHERE o.user_id IN (
    SELECT id FROM user WHERE level = 'VIP'
);
-- 优化建议:改为 JOIN 或 EXISTS 改写
BFF 层特别警示:子查询嵌套在 BFF 聚合查询中极易引发连接池雪崩。因为每个子查询都会占用一个数据库连接,嵌套层数越多,连接池耗尽速度越快。

3.3 慢查询类型与执行计划特征对照

通过 EXPLAIN 输出中的 type 和 Extra 字段,可以快速定位慢查询类型。下表总结了各类型的典型特征:

慢查询类型 EXPLAIN type 字段 EXPLAIN Extra 字段 典型扫描行数
全表扫描 ALL Using where 全表行数
索引失效 index 或 ref Using index condition; Using where 索引全扫描
JOIN 过多 ALL 或 ref Using join buffer (Block Nested Loop) 驱动表行数 × 被驱动表行数
子查询嵌套 DEPENDENT SUBQUERY Using where 外层行数 × 内层扫描行数

3.4 慢查询判定与分类流程

下图展示了从捕获慢查询到判定类型的完整流程,帮助 SRE 工程师快速定位问题根因。

慢查询判定与分类流程 慢查询判定与分类流程 捕获慢查询日志 判定:执行时间 > 200ms? 是 → 进入慢查询分析 判定:扫描行数 > 10000? 是 → 索引效率低 EXPLAIN 分析类型 全表扫描 type: ALL 索引失效 type: index/ref JOIN 过多 Extra: Using join buffer 子查询嵌套 DEPENDENT SUBQUERY 说明:执行时间与扫描行数双重判定,结合 EXPLAIN 输出,可快速定位慢查询类型 BFF 层建议优先排查 JOIN 过多和子查询嵌套,这两类对连接池压力最大

3.5 慢查询对连接池的影响

不同类型的慢查询对连接池的消耗方式不同。理解这种差异,才能制定针对性的连接池参数调优策略。

慢查询类型 连接持有时间 连接数消耗 典型影响
全表扫描 长(秒级) 1 个连接 / 查询 连接池快速被占满,后续请求排队
索引失效 中(百毫秒级) 1 个连接 / 查询 连接池水位持续高位,响应时间波动
JOIN 过多 长(秒级) 1 个连接 + 临时表资源 连接池 + 磁盘 IO 双重压力
子查询嵌套 极长(秒级+) 1 个连接 / 外层行 连接池瞬间耗尽,引发雪崩