SQL 必知必会:从 SQL 语言、DBMS 到一条 SQL 的执行全过程
很多开发者会写 SQL,但真正遇到慢查询、索引失效、执行计划异常、数据库迁移时,才会发现:“会写 SQL”与“理解数据库如何执行 SQL”完全是两件事。
这篇文章把 SQL 基础、DBMS 体系和 SQL 执行原理串成一条完整主线:
- SQL 到底是什么,为什么它值得长期学习;
- DB、DBMS、DBS 有什么区别;
- SQL 数据库和 NoSQL 数据系统分别解决什么问题;
- Oracle 如何执行一条 SQL,什么是硬解析和软解析;
- MySQL 如何执行一条 SQL,存储引擎又处于什么位置;
- 在现代 MySQL 中,应该如何使用
EXPLAIN ANALYZE、Performance Schema 等工具分析 SQL。
本文整理自三份 SQL/DBMS 学习资料,并结合当前 MySQL、Oracle 官方文档对部分 2019 年的内容进行了更新。尤其是 MySQL Query Cache、
SHOW PROFILE、默认存储引擎等内容,不能再完全照搬旧资料。
一、SQL 是什么
SQL,全称 Structured Query Language,即结构化查询语言。
它不是某一个数据库产品独有的语言,而是一套用于描述和操作关系型数据的标准化语言。MySQL、Oracle、PostgreSQL、SQL Server 等数据库管理系统都会支持 SQL,只是在标准 SQL 的基础上增加了自己的扩展语法,也就是常说的“数据库方言”。
可以把它简单理解成:
1 | |
例如:
1 | |
这条 SQL 只描述了“我要什么”,却没有告诉数据库:
- 先扫描哪张表;
- 是否使用索引;
- 从哪个索引开始;
- 采用 Nested Loop、Hash Join 还是其他连接算法;
- 数据应该从内存还是磁盘读取。
这些事情由数据库的优化器和执行器决定。
这也是 SQL 最重要的特点之一:SQL 是声明式语言,而不是命令式语言。
开发者负责声明结果,数据库负责思考实现路径。
二、为什么 SQL 的“半衰期”很长
软件行业变化非常快,框架、语言、工具可能几年就完成一轮更替,但 SQL 是一个相对特殊的存在。
原因并不复杂。
1. 数据是绝大多数业务系统的核心
订单、商品、库存、财务、用户、权限、日志、风控、报表,本质上都在处理数据。
只要业务系统还需要结构化数据,SQL 就很难退出历史舞台。
2. SQL 标准稳定
SQL 已经发展了几十年。标准一直在演进,但大量核心语法和关系模型思想非常稳定。
例如:
1 | |
这些东西不会因为某个 Java 框架升级就突然失效。
3. SQL 不只属于 DBA
今天 SQL 已经不只是 DBA 或数据分析师的技能。
后端开发、数据工程、测试、运维、产品、运营、BI 都可能直接或间接使用 SQL。
对于后端开发尤其如此:ORM 可以减少 SQL 的书写,但不能替你理解 SQL 的成本。
JPA、MyBatis、Hibernate 最终还是要落到数据库执行。
三、SQL 的四类基本能力
学习 SQL 时,经常会按照功能将语句分成 DDL、DML、DCL、DQL 四类。这是一种非常实用的学习分类。
3.1 DDL:Data Definition Language
DDL 是数据定义语言,用于定义数据库对象。
常见操作包括:
1 | |
例如:
1 | |
DDL 解决的是:数据应该以什么结构存在。
3.2 DML:Data Manipulation Language
DML 是数据操作语言,用于修改数据。
典型语句:
1 | |
例如:
1 | |
DML 解决的是:数据应该如何发生变化。
3.3 DCL:Data Control Language
DCL 是数据控制语言,主要用于权限控制。
常见语句:
1 | |
例如:
1 | |
DCL 解决的是:谁可以访问什么数据,以及能够执行什么操作。
3.4 DQL:Data Query Language
DQL 是数据查询语言,核心就是 SELECT。
1 | |
在实际业务系统中,查询往往也是 SQL 使用频率最高、最容易出现性能问题的部分。
所以真正拉开 SQL 水平差距的,通常不是会不会写 SELECT,而是:
- 能不能写出正确的关联关系;
- 能不能控制扫描数据量;
- 能不能设计合理索引;
- 能不能读懂执行计划;
- 能不能判断慢在优化器、锁、I/O、网络还是业务模型。
四、写 SQL 之前,先理解 ER 模型
关系型数据库不是“先建表,再想业务”,而应该先从数据模型出发。
经典的建模方法是 ER 模型,即 Entity Relationship Model。
三个核心概念:
1 | |
例如一个电商系统:
1 | |
用户和订单通常是:
1 | |
常见关系有:
- 一对一;
- 一对多;
- 多对多。
其中多对多关系在关系型数据库中通常通过中间表拆解。
例如用户与角色:
1 | |
所以,表设计并不是 SQL 之外的东西,而是 SQL 是否好写、是否高效的前提。
五、SQL 书写规范
SQL 是否大小写通常不会决定语义,但团队最好保持统一风格。
一种常见规范是:
- SQL 关键字大写;
- 表名、字段名、别名小写;
- 多单词字段使用下划线;
- 避免无意义缩写;
- 查询复杂时合理换行。
例如:
1 | |
比下面这种更适合长期维护:
1 | |
注意:不同 DBMS 对标识符大小写、字符串比较、排序规则等细节并不完全一致。不要把“SQL 关键字大小写不敏感”误解成“所有数据库对象和字符串都永远不区分大小写”。
六、DB、DBMS、DBS 到底有什么区别
数据库领域经常出现三个容易混淆的缩写。
6.1 DB:Database
DB 就是数据库,本质是被组织起来的数据集合。
例如:
1 | |
一个数据库中通常包含多张表、索引、视图等对象。
6.2 DBMS:Database Management System
DBMS 是数据库管理系统。
它是一套软件,负责:
- 数据存储;
- 查询解析;
- 查询优化;
- 事务;
- 锁与并发控制;
- 权限;
- 日志;
- 恢复;
- 索引;
- 缓存;
- 网络连接。
MySQL、Oracle、PostgreSQL、SQL Server 都属于 DBMS。
因此严格来说:
1 | |
日常口语把“MySQL 数据库”作为产品名称使用没有问题,但理解底层概念时最好区分开。
6.3 DBS:Database System
DBS 是数据库系统,范围更大。
可以粗略理解为:
1 | |
所以三者的层级关系大致是:
1 | |
这里的“小于”不是数学意义,而是表达概念范围逐渐扩大。
七、为什么已经有 SQL,还会存在这么多 DBMS
SQL 是标准,但标准并不等于实现。
这和 Java 很像:
1 | |
不同数据库产品会在以下方向作出不同取舍:
- 性能;
- 成本;
- 高可用;
- 分布式;
- 分析能力;
- 安全能力;
- 运维成本;
- 生态;
- 商业支持;
- 云原生能力。
因此不可能存在一个数据库在所有业务场景中都绝对最优。
例如:
- MySQL 常用于通用 OLTP 业务;
- PostgreSQL 强调标准、扩展能力和复杂数据能力;
- Oracle 在大型商业系统、复杂企业场景中长期存在;
- Redis 适合低延迟 Key-Value 访问、缓存等场景;
- MongoDB 适合文档模型;
- Elasticsearch 适合全文检索和搜索分析;
- 图数据库适合复杂关系遍历。
真正应该问的不是“哪个数据库最好”,而是:
当前业务的数据模型、访问模式、一致性需求、规模和成本,适合什么数据库。
八、SQL 与 NoSQL:不是二选一
NoSQL 的关键不是“反对 SQL”,而是针对关系模型之外的数据访问模式提供不同的数据模型和扩展方式。
常见类型包括:
8.1 Key-Value
典型代表:Redis。
数据模型:
1 | |
优势:
- 查找路径短;
- 低延迟;
- 数据结构简单;
- 很适合缓存、计数、会话、热点数据。
缺点是它不像关系型数据库那样天然擅长复杂 JOIN 和任意条件组合查询。
8.2 文档数据库
典型代表:MongoDB。
文档通常以类似 JSON/BSON 的结构组织。
适合:
- Schema 变化较频繁;
- 一个业务对象天然就是聚合文档;
- 不希望拆成大量关系表。
8.3 搜索系统
典型代表:Elasticsearch。
它的核心优势并不是事务,而是:
- 全文检索;
- 分词;
- 倒排索引;
- 相关性排序;
- 搜索聚合。
所以在一个典型系统中,经常会出现:
1 | |
三者是协作关系,不是谁替代谁。
8.4 列式/宽列存储
列式系统按照列组织数据,非常适合分析场景。
如果一个查询只需要:
1 | |
列式系统可以尽量只读取相关列,而不是整行读取大量无关字段。
相同列的数据类型一致,也更容易获得较高压缩率,从而降低 I/O。
需要注意:
- “列式分析数据库”和“宽列 NoSQL”并不是完全相同的概念;
- 具体实现差异很大,不能只看“按列”两个字就认为它们属于同一种系统。
8.5 图数据库
图数据库把数据抽象成:
1 | |
适合:
- 社交关系;
- 风控关系网;
- 知识图谱;
- 路径分析;
- 多跳关系查询。
当问题本身就是“关系之间的关系”时,图模型会比传统多表 JOIN 更自然。
九、一条 SQL 到底是如何执行的
从开发者角度看,我们写的是:
1 | |
从数据库角度看,它至少要回答下面这些问题:
1 | |
不同 DBMS 的实现不同,但可以抽象出一条非常重要的通用主线:
1 | |
理解这条链路,是从“会写 SQL”走向“会优化 SQL”的分界线。
十、Oracle 中 SQL 的执行过程
Oracle 的 SQL 处理可以抽象成:
1 | |
其中最值得理解的是:Shared Pool、Library Cache、Hard Parse、Soft Parse。
10.1 语法和语义检查
数据库首先需要判断 SQL 是否有效。
语法检查
例如:
1 | |
关键字写错,直接失败。
语义检查
例如:
1 | |
语法没有问题,但字段不存在,仍然无法执行。
10.2 Shared Pool 与 Library Cache
Oracle 的 SGA 中包含 Shared Pool。
Shared Pool 内部包含 Library Cache 等结构,用于保存可共享的 SQL、PL/SQL 及相关执行信息。
当一条 SQL 到来时,Oracle 会尝试判断是否存在可以复用的已有游标和执行信息。
于是出现两个重要概念。
10.3 Soft Parse:软解析
如果数据库发现:
1 | |
那么就可以减少重新生成执行计划的工作。
这种路径通常称为 Soft Parse。
软解析并不是“零成本”,但通常比硬解析便宜。
10.4 Hard Parse:硬解析
如果无法找到可复用的 SQL,数据库就需要完成更多工作,例如:
- 解析;
- 优化;
- 访问数据字典;
- 生成新的执行计划;
- 创建新的共享游标。
这就是 Hard Parse。
硬解析会消耗更多 CPU 和共享池相关资源。
因此大量结构完全相同、仅字面值不同的 SQL,可能造成不必要的解析压力。
10.5 为什么绑定变量很重要
假设业务不断执行:
1 | |
虽然业务意图相同,但 SQL 文本不同。
使用绑定变量:
1 | |
不同调用只改变参数值。
这样做通常更有利于 SQL 共享和游标复用,也能减少大量重复硬解析。
但需要注意:
参数化并不意味着“永远只有一个最优执行计划”。
当不同参数对应的数据分布差异极大时,一个固定执行计划未必适合所有参数。现代 Oracle 也提供自适应游标共享等机制处理这类问题。
所以数据库优化永远不是一句“绑定变量一定最快”就能结束。
十一、MySQL 的整体架构应该怎么理解
旧资料中经常把 MySQL 简化成三层:
1 | |
另一种常见讲法是:
1 | |
这两种说法并不矛盾,只是抽象粒度不同。
可以统一理解成:
1 | |
对于开发者来说,最关键的是两件事:
- Server 层决定 SQL 怎么理解、怎么优化、怎么执行;
- 存储引擎真正负责数据如何组织、读取和修改。
十二、MySQL 一条 SELECT 的执行主线
对于现代 MySQL,可以把核心过程简化为:
1 | |
12.1 Parser:解析器
解析器负责理解 SQL。
典型工作包括:
- 词法分析;
- 语法分析;
- 构建内部语法结构;
- 检查相关对象与表达式。
如果这一层过不了,优化器根本不会开始工作。
12.2 Optimizer:优化器
优化器负责回答:
这条 SQL 有很多种执行方式,我应该选哪一种?
例如:
1 | |
可能的路径包括:
1 | |
如果还有 JOIN,问题会更复杂:
1 | |
优化器会结合:
- 索引;
- 统计信息;
- 数据分布;
- 成本模型;
- 查询条件;
- JOIN 关系;
- 排序和分组;
- 可用优化规则。
最终选择执行计划。
12.3 Executor:执行器
执行器拿到计划后开始真正执行。
它并不是自己直接解析 InnoDB 页文件,而是通过存储引擎接口与 InnoDB 等引擎交互。
这也是 MySQL “可插拔存储引擎”设计的意义。
十三、一个必须修正的旧知识:MySQL Query Cache 已经被移除
一些较老的 MySQL 架构图经常画成:
1 | |
这个图今天已经不能直接照搬。
MySQL 的 Query Cache 在 5.7 后期已经被废弃,并在 MySQL 8.0 中移除。
原因之一是:
当相关表发生修改时,查询缓存需要失效。对频繁更新的 OLTP 系统来说,维护成本非常高,收益反而有限。
所以理解现代 MySQL 时,不要再把 Query Cache 当作 SQL 执行主链路中的一个组件。
需要区分的是:
1 | |
InnoDB Buffer Pool、表缓存、元数据缓存、操作系统页缓存等仍然非常重要,只是它们和过去“按 SQL 文本缓存整个查询结果”的 Query Cache 不是一回事。
十四、MySQL 的存储引擎
MySQL 很有代表性的设计是存储引擎层。
可以查看当前实例支持的存储引擎:
1 | |
也可以查看默认存储引擎:
1 | |
14.1 InnoDB
现代 MySQL 的绝对主力。
MySQL 8.4 中,InnoDB 仍然是默认存储引擎。
核心能力包括:
- ACID 事务;
- Commit / Rollback;
- 崩溃恢复;
- 行级锁;
- MVCC;
- 外键;
- Buffer Pool;
- Redo Log / Undo 等事务机制。
对于绝大多数 Java 业务系统,如果没有非常特殊的理由,业务表优先使用 InnoDB。
1 | |
事实上,如果默认引擎没有被修改,ENGINE = InnoDB 都可以省略。
14.2 MyISAM
MyISAM 是 MySQL 历史上非常重要的存储引擎,但已经不是现代业务系统的默认选择。
它不支持 InnoDB 那样的完整事务能力,也不支持外键,锁粒度和崩溃恢复能力也不适合大多数现代 OLTP 业务。
今天再看到“MyISAM 速度快,所以读多写少就优先 MyISAM”这种经验,应该谨慎。
不要把二十年前的数据库选型经验直接复制到今天。
14.3 MEMORY
MEMORY 引擎把数据放在内存中。
适合部分临时数据场景,但它并不是 Redis 的替代品,也不适合保存需要可靠持久化的核心业务数据。
14.4 NDB
NDB 用于 MySQL NDB Cluster,采用分布式架构,和普通单机/主从 InnoDB 不是同一种使用模式。
不要看到“分布式”三个字就直接使用,选型需要结合网络延迟、事务模型、运维复杂度和业务访问模式。
十五、今天应该怎么分析一条 MySQL SQL
旧资料通常会使用:
1 | |
这些命令在学习 MySQL SQL 执行阶段时很直观,但今天已经不应该作为新的性能分析方案。
MySQL 8.4 官方文档明确标注 SHOW PROFILE / SHOW PROFILES 为 deprecated,并建议使用 Performance Schema。
现在更值得掌握的是下面几组工具。
15.1 EXPLAIN
先看优化器打算怎么执行:
1 | |
常见关注项包括:
- 使用了哪张表;
- 使用了哪个索引;
- 访问类型;
- 预估扫描行数;
- 是否排序;
- 是否临时表;
- 是否出现明显全表扫描。
EXPLAIN 回答的是:
优化器计划怎么做。
15.2 EXPLAIN ANALYZE
如果只看估算还不够,可以使用:
1 | |
它会真正执行 SQL,并提供实际执行时间、实际返回行数等信息。
这使我们可以对比:
1 | |
如果估算是 10 行,实际却是 100 万行,优化器就很可能基于错误或不足的统计信息作出糟糕选择。
所以 EXPLAIN ANALYZE 是现代 MySQL SQL 调优非常重要的工具。
注意:它会真实执行语句。对
UPDATE、DELETE等语句使用前一定要明确其实际行为,不要在生产环境随手执行。
15.3 Performance Schema
Performance Schema 是 MySQL 内置的性能观测体系。
它可以帮助分析:
- SQL 执行;
- 等待事件;
- 锁;
- I/O;
- 线程;
- 事务;
- 阶段耗时。
例如可以通过 events_statements_summary_by_digest 观察相似 SQL 的聚合情况:
1 | |
这比只盯着某一条偶发 SQL 更适合定位系统级热点。
15.4 sys Schema
sys Schema 对 Performance Schema 的信息进行了更易读的封装。
例如想找全表扫描明显的表,可以关注类似:
1 | |
还可以使用 statement 相关视图寻找耗时 SQL、扫描量高的 SQL 等。
对于日常排查,它通常比直接啃 Performance Schema 原始表更舒服。
十六、从 SQL 执行原理反推性能优化
理解执行链路后,会发现很多性能问题都能归类。
16.1 解析阶段的问题
例如:
- 大量动态拼接 SQL;
- SQL 文本不断变化;
- Oracle 大量硬解析;
- 连接层频繁创建和销毁连接。
应用侧可能的方向:
- 参数化 SQL;
- PreparedStatement;
- 合理连接池;
- 减少无意义 SQL 变体。
16.2 优化器阶段的问题
例如:
- 没有合适索引;
- 联合索引顺序不合理;
- 数据统计不准确;
- 条件选择性差;
- JOIN 顺序估算错误;
- 返回行数估算偏差巨大。
工具:
1 | |
16.3 执行阶段的问题
例如:
- 扫描行数太多;
- 回表太多;
- 随机 I/O 多;
- 大排序;
- 临时表;
- 大量锁等待;
- 网络返回结果集太大。
此时继续“加索引”不一定是正确答案。
SQL 性能应该拆成:
1 | |
这样排查会比“SQL 慢了,先加索引”靠谱得多。
十七、一个 Java 后端开发应该建立的数据库思维
对于 Java 开发者,数据库最危险的误区是:
把 ORM 当成数据库抽象层之后,就认为可以不用理解 SQL。
实际上:
1 | |
最终都要变成 SQL 交给 DBMS。
所以排查一个接口性能问题时,链路应该是:
1 | |
如果只在 Java 代码层找问题,可能永远找不到真正的瓶颈。
建议后端开发至少具备以下数据库能力:
- 能写复杂 JOIN、GROUP BY、子查询、CTE;
- 能设计联合索引;
- 能读
EXPLAIN; - 能用
EXPLAIN ANALYZE验证估算与实际; - 能判断是否发生全表扫描;
- 能看锁等待和事务;
- 能使用慢查询日志、Performance Schema、sys Schema;
- 理解 Buffer Pool、Redo、Undo、MVCC;
- 知道 ORM 生成的 SQL 不一定合理;
- 优化前先验证,不凭感觉改索引。
十八、把三部分知识串起来
到这里,可以把全文压缩成三层。
第一层:SQL 是什么
1 | |
第二层:DBMS 是什么
1 | |
SQL 是通用语言,但不同 DBMS 有不同实现和使用场景。
第三层:SQL 怎么执行
1 | |
Oracle 在这一过程中非常强调 Shared Pool、Library Cache、Cursor Sharing、Hard Parse / Soft Parse。
MySQL 则具有清晰的 Server Layer + Storage Engine Layer 结构,InnoDB 是现代 MySQL 的核心存储引擎。
十九、资料与现代 MySQL 的几个差异
最后专门列一下容易踩坑的旧知识。
| 旧资料常见说法 | 今天应该怎么理解 |
|---|---|
| SQL 执行先查询 Query Cache | MySQL 8.0 已移除 Query Cache |
使用 SHOW PROFILE 分析 SQL |
该功能已 deprecated,优先使用 Performance Schema |
| MyISAM 读性能快,可作为常规读库引擎 | 对现代业务系统不应作为默认经验,通常优先 InnoDB |
只看 EXPLAIN 就够了 |
还应使用 EXPLAIN ANALYZE 比较估算与实际执行 |
| 数据库优化就是建索引 | 解析、统计信息、执行计划、锁、I/O、事务、返回数据量都可能是瓶颈 |
技术资料最危险的不是“旧”,而是:
过去正确的结论,在新的版本里已经改变,但使用者没有意识到版本边界。
所以阅读任何数据库文章时,都建议先看:
1 | |
二十、总结
SQL 的真正门槛从来都不是语法。
SELECT、INSERT、UPDATE、DELETE 几天就能学会,但理解数据库,需要继续向下走:
1 | |
当你理解一条 SQL 是如何被数据库“看见、理解、规划、执行”的时候,很多以前靠经验记忆的规则就会变成可以推导的结论。
比如:
- 为什么索引不是越多越好;
- 为什么同一条 SQL 在不同数据量下执行计划会变化;
- 为什么参数不同可能导致性能差异;
- 为什么扫描行数比结果行数大几个数量级时值得警惕;
- 为什么 SQL 没变,统计信息变化后性能可能突然恶化;
- 为什么数据库优化不能只盯着 SQL 文本本身。
会写 SQL,只是开始;能站在数据库的角度理解 SQL,才真正进入数据库性能优化。
参考资料
本文主体根据提供的三份学习资料重新整理、合并,并对过时部分进行了版本修订。现代数据库相关信息参考官方文档:
MySQL 8.4 Reference Manual - Introduction to InnoDB
https://dev.mysql.com/doc/refman/8.4/en/innodb-introduction.htmlMySQL 8.4 Reference Manual - SHOW PROFILE Statement
https://dev.mysql.com/doc/refman/8.4/en/show-profile.htmlMySQL 8.4 Reference Manual - Query Profiling Using Performance Schema
https://dev.mysql.com/doc/refman/8.4/en/performance-schema-query-profiling.htmlMySQL 8.4 Reference Manual - EXPLAIN Statement
https://dev.mysql.com/doc/refman/8.4/en/explain.htmlOracle Database - SQL Processing
https://docs.oracle.com/en/database/oracle/oracle-database/19/tgsql/sql-processing.htmlOracle Database - Tuning the Shared Pool and the Large Pool
https://docs.oracle.com/en/database/oracle/oracle-database/19/tgdba/tuning-shared-pool-and-large-pool.html