软件开发进阶技能之数据库进阶(二)

简介: 教程来源 https://feikanbuke.cn/ 本节聚焦查询性能诊断核心——执行计划:详解MySQL/PostgreSQL中EXPLAIN获取与解读方法,涵盖type、Extra关键字段含义,结合联合索引优化、JOIN驱动表选择、SELECT *陷阱、子查询改写及深分页避坑等实战策略。

第二部分:查询分析与执行计划 —— 让数据库告诉你慢在哪

写了 SQL 之后,数据库到底是如何执行的?读懂执行计划(Execution Plan)是进阶数据库技能的核心。

2.1 获取执行计划
MySQL:EXPLAIN SELECT ... 或者 EXPLAIN ANALYZE SELECT ...(MySQL 8.0.18+ 会实际执行并返回耗时和行数)。

PostgreSQL:EXPLAIN (ANALYZE, BUFFERS) SELECT ...。

2.2 解读执行计划的关键指标
以 MySQL 的 EXPLAIN 输出为例,最重要的列:
image.png
2.2.1 type 详解
const:主键或唯一索引等值查询,最多返回一行,常量级别的快。

eq_ref:连接查询中,被驱动表使用主键或唯一索引,每次只匹配一行。

ref:非唯一索引等值匹配,可能返回多行。

range:索引范围扫描(BETWEEN, >, <, IN 等)。

index:全索引扫描(遍历整个索引树),比全表扫描好,但依然慢。

ALL:全表扫描,最差。

2.2.2 Extra 详解
Using index:覆盖索引,好。

Using index condition:索引下推(Index Condition Pushdown),MySQL 5.6+ 特性,存储引擎层过滤索引记录,减少回表。

Using where:在 Server 层过滤行,通常与索引一起使用。

Using filesort:需要额外的排序操作,如果无法避免,尽量让排序字段利用索引(ORDER BY 遵循最左前缀)。

Using temporary:使用临时表,通常出现在 GROUP BY 或 DISTINCT 没有使用索引时,应尽量优化。

2.3 实战:优化一条慢 SQL
场景:订单表 orders 有百万级数据,需要查询某个用户在最近 30 天的订单总额。

原始 SQL:

SELECT SUM(amount) 
FROM orders 
WHERE user_id = 12345 
  AND order_date >= '2025-01-01' 
  AND order_date <= '2025-01-31';

执行计划分析:
假设当前只有 (user_id) 和 (order_date) 两个单列索引。MySQL 只能选择其中一个索引。如果选择 user_id 索引,会找到该用户的所有订单(可能很多),然后逐行过滤 order_date;如果选择 order_date 索引,则扫描一月的数据,再过滤 user_id。都不是最优。

优化方案:创建联合索引 (user_id, order_date)。

jALTER TABLE orders ADD INDEX idx_user_date (user_id, order_date);

执行计划:

type: ref
key: idx_user_date
rows: 估算该用户的1月订单数(很小)
Extra: Using index (如果只查 amount 且 amount 不在索引中,会回表;但 SUM 需要 amount 值,无法覆盖)

如果经常需要按用户和时间范围做聚合,可以进一步考虑物化视图或汇总表。

2.4 连接查询的优化
多表连接(JOIN)是 SQL 中最复杂且容易出性能问题的地方。优化器需要决定哪张表作为驱动表(小表),使用什么连接算法(Nested Loop、Hash Join、Sort-Merge Join)。

2.4.1 驱动表的选择
在 EXPLAIN 中,第一行表示驱动表,第二行表示被驱动表。基本原则:用小结果集驱动大结果集。

示例:用户表 1000 行,订单表 100 万行,查询用户及其订单。

SELECT * FROM users u 
JOIN orders o ON u.id = o.user_id 
WHERE u.registration_date > '2024-01-01';

如果满足条件的用户只有 10 个,那么优化的执行计划应该是:先找到这 10 个用户,然后对每个用户去订单表通过索引 (user_id) 查找订单(被驱动表访问次数 = 10,而不是 100 万)。

2.4.2 尽量避免 SELECT *
只选择需要的列可以:减少网络传输,更有可能使用覆盖索引,减少回表。

2.4.3 子查询优化
某些子查询会被优化器转为连接(semi-join),但也有写得很差的子查询导致逐行执行。

反例:

SELECT * FROM users 
WHERE id IN (SELECT user_id FROM orders WHERE amount > 1000);

MySQL 5.6 以后会做 semi-join 优化,性能尚可。但有时将 IN 改为 EXISTS 或者显式 JOIN 更可控。

推荐写法:

SELECT DISTINCT u.* 
FROM users u 
JOIN orders o ON u.id = o.user_id 
WHERE o.amount > 1000;

2.5 分页查询优化 —— 避免深分页
OFFSET 很大时,数据库需要扫描并丢弃前 N 行,非常低效。

反例:

SELECT * FROM products ORDER BY id LIMIT 1000000, 10;

这会扫描 1,000,010 行。

优化方案:使用游标/键集分页(Seek Method)。

-- 记住上一页最后一条记录的 id
SELECT * FROM products WHERE id > 1000000 ORDER BY id LIMIT 10;

需要保证 id 是递增且无空洞,或者使用多列排序时记住所有排序列的值。

更通用的方案:使用延迟关联(Deferred Join)。先只查询主键,再通过主键回表获取全部数据。

SELECT * FROM products 
WHERE id IN (
    SELECT id FROM products ORDER BY id LIMIT 1000000, 10
);

但注意 MySQL 对子查询 IN 的性能,可能需要改为 JOIN。
来源:
https://oplhc.cn/

相关文章
|
23天前
|
XML 前端开发 程序员
初级程序员必备的十大技能之 API 接口与前后端联调(一)
教程来源 http://qeext.cn/ 本文系统讲解API设计规范(RESTful/GraphQL)、HTTP协议核心(方法、状态码、头信息)、前后端联调流程及调试工具,助你打造标准化、高可用接口,打破前后端协作孤岛。
|
23天前
|
存储 程序员 Linux
初级程序员必备的十大技能之 Git 版本控制(一)
教程来源 http://xcfsr.cn Git是程序员的“后悔药”与“时光机”:可随时回退错误修改、隔离并行开发、一键恢复稳定版本。作为分布式版本控制系统,它本地全量存储、离线可用、安全可靠,支撑全球90%以上团队高效协作。
|
9天前
|
Ubuntu Linux KVM
虚拟机搭建教程(二)
教程来源 https://zlpow.cn/ 本文详解Windows、Linux三大平台虚拟化实战:Windows下用VMware安装Ubuntu 24.04(含Tools与快照),VirtualBox部署CentOS Stream 9;Linux主机通过KVM命令行及virt-manager搭建高性能虚拟机,覆盖配置、联网、增强工具与管理全流程。
|
8天前
|
SQL 网络协议 NoSQL
软件开发新手入门五大核心技能之计算机基础常识(五)
教程来源 http://vbzcj.cn/ 本章系统讲解网络与数据库核心知识:涵盖OSI/TCP/IP模型、IP/端口、TCP三次握手/四次挥手、HTTP协议、DNS解析;以及SQL基础、索引优化、ACID事务、NoSQL(如Redis)等,理论结合Python实战示例。
|
8天前
|
存储 缓存 固态存储
软件开发新手入门五大核心技能之计算机基础常识(一)
教程来源 http://oieaw.cn/ 本文以“内功”喻计算机基础,系统讲解CPU、内存、存储等硬件原理及冯·诺依曼体系,涵盖指令执行、缓存机制、内存布局、I/O特性等核心知识,并辅以可运行代码与典型问题分析,助程序员夯实底层认知,提升性能优化与系统设计能力。
|
25天前
|
存储 缓存 程序员
初级程序员必备的十大技能之计算机基础必备(一)
教程来源 http://fndvx.cn 本文系统讲解程序员必修的计算机基础,涵盖组成原理、操作系统、网络、数据库、编译原理五大核心。从冯·诺依曼结构、二进制与位运算(含权限系统实战),到内存层次与缓存优化,配原理图解与可运行代码,助你夯实内功、知其所以然。
|
1天前
|
存储 NoSQL 关系型数据库
软件开发进阶技能之数据库进阶(一)
教程来源 https://ypzt.cn/ 本文系统讲解数据库进阶核心知识,涵盖B+树/哈希/LSM索引原理、聚簇与二级索引、联合索引最左前缀原则、索引失效场景及维护代价,并延伸至事务隔离、锁机制、MVCC、分片高可用等。面向已掌握SQL基础的开发者,强调机制理解而非口诀记忆。
|
1天前
|
自然语言处理 JavaScript 前端开发
软件开发进阶技能之编程语言深度运用(五)
教程来源 https://tjxhrt.cn/ 本节深入函数式编程核心特性:高阶函数与闭包、不可变数据结构、纯函数设计及惰性求值。通过多语言示例(JS/Java/Python),讲解如何提升代码简洁性、可测试性与并发安全性,让数据流更清晰可控。
|
1天前
|
前端开发 安全 Java
软件开发进阶技能之编程语言深度运用(三)
教程来源 https://bslm2020.com/ 本节深入解析并发与异步编程核心:厘清并行/并发、线程/协程本质差异;梳理回调→Promise→async/await演进脉络;详解锁、原子操作、RWMutex等同步机制;剖析数据竞争与死锁成因及规避策略,助开发者安全高效驾驭多核时代。
|
1天前
|
安全 JavaScript 前端开发
软件开发进阶技能之编程语言深度运用(四)
教程来源 https://zgrsny.cn/ 元编程是“编写能编写代码的代码”,核心在于动态操作程序结构;反射是其基石,支持运行时获取类型、调用方法、访问字段。Java/Python均提供强大反射能力,配合注解/装饰器与动态代理,广泛应用于框架开发、AOP、序列化等场景,但需权衡性能与类型安全。