性能反模式与常见误区清单

kp-029 · 工程实践与历史脉络核心约 25 分钟已校对

前置知识点

前置:

相关知识点

相关:

学习进度:

一句话定义

性能反模式是「代码能跑、上线出事」的高频错误集合——N+1 查询、索引失效写法、深分页、大事务、COUNT 滥用等——它们可被静态识别、可被 EXPLAIN 证实、也有成熟的修复方案。

为什么重要

前置知识

kp-011(联合索引与覆盖索引)、kp-012(EXPLAIN 阅读能力)。

核心概念

原理与机制

反模式共通的机制根源有三个。其一是「让索引的白名单失效」:优化器只能在索引可用路径里选路,函数套列、隐式转换、前导 % 都把条件变成无法走树的非 SARGable 谓词,全表扫描随之而来。其二是「往返次数支配延迟」:单次查询 1ms 的 N+1 在网络 RTT 1ms 时总延迟 N+2ms,且连接池被占满——数据库性能问题常是「调用次数问题」。其三是「把开销搬进事务与复制链」:大事务拉长锁持有(kp-016)、撑大 Undo 链(kp-017)、延后一致性点(kp-020),一行代码毁掉三层机制。深分页的机制是 OFFSET 必须物化并丢弃前 N 行,游标分页把「位置」编码为条件谓词,让每页都是一次索引区间扫描。

实例或案例

修复深分页:

-- 反模式:第 50001 页,扫 100 万行再丢弃
SELECT id, title FROM articles ORDER BY id DESC LIMIT 1000000, 20;

-- 修复一:游标分页(索引区间扫描,每页代价恒定)
SELECT id, title FROM articles WHERE id < :last_seen_id ORDER BY id DESC LIMIT 20;

-- 修复二:延迟关联(先用覆盖索引定位 id,再回表 20 行)
SELECT a.id, a.title
FROM articles a
JOIN (SELECT id FROM articles ORDER BY id DESC LIMIT 1000000, 20) t USING (id);

N+1 修复:ORM 层开启预加载(IN 批量取关联)或改写为一次 JOIN;识别手段是在开发环境开 SQL 日志数「同构 SQL 执行次数」。

常见误区

自测题

  1. WHERE DATE(created_at) = '2026-10-01' 为什么慢?怎么改? 答:函数套列不可走索引(非 SARGable);改为 WHERE created_at >= '2026-10-01 00:00:00' AND created_at < '2026-10-02 00:00:00' 的区间写法。
  2. 如何在评审时一眼识别 N+1? 答:循环体内出现按外层记录查库的调用(ORM 惰性属性访问);或日志中同构 SQL(仅参数不同)重复出现 N 次。

公式或模型

N+1 总延迟 ≈ 1 次 RTT + N × (RTT + 单查耗时);深分页代价 ≈ OFFSET + page_size 行扫描 vs 游标分页恒定 page_size × log N。

图示

本节不适用:反模式是清单式知识, kp-012 的 EXPLAIN 实验已提供「证据图」,重复画图无增量信息。

直观类比

反模式清单像厨房里的「常翻车菜谱」:油温没到就下锅(索引未就绪就压测)、一锅炖三天(大事务)、客人每点一道菜跑一趟菜市场(N+1)——每条都有名字,说出口大家就知道怎么改。

与其他知识点的关系

kp-011/kp-012 是诊断工具;kp-016/kp-017/kp-020 解释大事务的连锁反应;kp-008 提示「无维护的冗余列」也是一类反模式;kp-030 的容量评估以反模式清理为前提。

延伸阅读