SQL数据库的核心是关系模型与执行引擎:前者基于数学理论定义数据组织规则,后者将SQL转化为物理操作;二者共同决定SQL的正确性、性能与行为。

SQL数据库的核心在于两块:关系模型定义数据怎么组织,执行引擎决定查询怎么运行。理解这两者,才能真正看懂SQL为什么这样写、为什么慢、为什么报错。
关系模型:不只是“表”这么简单
关系模型不是简单把数据存成表格,而是建立在严格数学基础(集合论和谓词逻辑)上的抽象体系。它的核心是“关系”(relation),对应到数据库中就是一张表,但每张表必须满足几个关键约束:
- 属性(列)有唯一名称且不可再分——不支持嵌套结构或JSON字段(除非用扩展类型);
- 元组(行)无序,且不能重复——所以ORDER BY不是关系操作,去重要用DISTINCT;
- 每个属性有明确的数据类型和域(domain)——NULL不是值,而是“缺失信息”的标记,参与比较时多数返回UNKNOWN;
- 主键与外键构成完整性约束——不是可选功能,而是维系关系语义的骨架,删除父记录前必须处理子引用。
正因如此,JOIN不是“连两张表”,而是对两个关系做笛卡尔积后按条件筛选;GROUP BY也不是“分组显示”,而是将输入关系划分为若干子集,每组输出一行聚合结果。
执行引擎:SQL语句如何变成磁盘读写
你写的SELECT不会直接执行。它先被解析成语法树,再经由优化器生成执行计划,最后由执行器驱动存储层完成实际操作。这个过程里最关键的环节是:
- 逻辑计划 → 物理计划的转换——比如优化器可能把WHERE条件下推到JOIN之前,把子查询转为JOIN,甚至重排JOIN顺序以减少中间结果大小;
- 算子实现方式决定性能——Hash Join适合大表关联(需内存建哈希表),Merge Join依赖排序,Nested Loop适合小表驱动大表;
- 统计信息驱动决策——表行数、列基数、直方图等信息影响索引是否被选中、是否走全表扫描;
- 缓冲区与预读机制隐藏I/O成本——数据页常驻Buffer Pool,顺序扫描会触发预读,但随机跳转访问仍可能频繁刷盘。
EXPLAIN(或EXPLAIN ANALYZE)看到的“Index Scan”“Bitmap Heap Scan”等,都是物理算子名,背后对应不同的内存使用模式和磁盘访问路径。
关系模型与执行引擎如何互相制约
两者不是割裂的:模型决定了能表达什么,引擎决定了能多快、多稳地实现它。
- 视图(VIEW)本质是保存的SELECT语句,没有独立存储——每次查询都重写并内联进主SQL,可能让优化器错过更优路径;
- 窗口函数(如ROW_NUMBER())要求逻辑上“先分区排序再计算”,执行器必须维护滑动窗口状态,无法流式处理,内存压力明显;
- 事务隔离级别(如READ COMMITTED vs SERIALIZABLE)改变执行器加锁/版本控制策略,直接影响并发行为和性能,但不改变SQL语义本身;
- NULL语义渗透到所有比较和聚合中——COUNT(*)统计行数,COUNT(col)忽略NULL,这要求执行器在扫描时实时判断空值,不能简单累加。
一个看似简单的ORDER BY LIMIT 10,在没有索引时,引擎必须排序全部数据再取头十行;而关系模型只保证结果满足“有序+截断”,不承诺实现方式——这也正是优化器存在意义。
不复杂但容易忽略
写SQL时盯着语法,调性能时盯着执行计划,但真正卡住问题的,往往是模型和引擎之间的隐含契约:比如默认不启用并行查询、临时表未走内存、统计信息过期导致误判、或者把关系运算当成过程式逻辑来理解。抓住这两根主线,很多“奇怪现象”就自然清晰了。










