Skip to content

数据库分层架构

数据库系统可以按"请求走过的路径"分为三层:访问层(接收和解析请求)、执行层(计划和执行操作)、存储层(持久化数据)。这个分层不是教科书式的形式划分——每一层有不同的扩展点、不同的性能特征、不同的故障模式。理解分层是理解数据库的一切高级话题(优化器、索引、事务、复制)的地基。

SQL 语句: SELECT name FROM users WHERE id = 42


┌─────────────── 访问层 ───────────────┐
│ 连接管理 / 协议解析 / 认证鉴权        │
│ 词法语法解析 → AST                    │
│ 语义分析(表/列存在性、权限)          │
└──────────────┬───────────────────────┘

┌─────────────── 执行层 ───────────────┐
│ 优化器(规则 + 代价估算 → 执行计划)    │
│ 执行器(火山模型迭代器树)              │
│ 并发控制(锁 / MVCC)                  │
└──────────────┬───────────────────────┘

┌─────────────── 存储层 ───────────────┐
│ 缓冲池(Buffer Pool)                 │
│ 索引结构(B+树 / LSM)                │
│ 事务日志(WAL / Redo Log)            │
│ 磁盘空间管理                          │
└──────────────────────────────────────┘

访问层:请求的入口

访问层处理一切与"连接和语言"相关的问题。连接管理维护客户端会话——每个连接对应服务端的一个线程/协程(MySQL 每连接一线程,PostgreSQL 每连接一进程),连接池解决的是这层的建立成本(TLS 握手 + 认证的往返)。协议解析处理数据库的网络协议(MySQL 的二进制协议、PostgreSQL 的前端/后端协议)。认证鉴权校验身份和权限。

SQL 解析是访问层的计算密集部分:词法分析(切分关键字、标识符、字面量)→ 语法分析(生成抽象语法树 AST)→ 语义分析(表和列是否存在、类型是否匹配、用户有无权限——这一步要查系统目录)。解析结果 AST 是纯语法结构——"SELECT 什么 FROM 什么 WHERE 什么",不含任何执行策略。

这层的典型故障:连接风暴(应用重启时百万连接同时建立——连接池的重连策略问题)、慢查询在解析阶段的开销(超长 IN 列表的解析成本)、SQL 注入(拼接的字符串绕过参数化——访问层的防御是预编译语句)。

执行层:从声明到操作

执行层把"要什么"(AST)翻译成"怎么做"。优化器是这层的明星:它枚举可能的执行计划(用哪个索引、表以什么顺序连接、用什么连接算法),用统计信息(表行数、列基数、数据分布直方图)估算每个计划的代价,选择最优者。优化器质量直接决定同一个 SQL 的性能差距——差计划慢一千倍是常见的现实。

执行器是计划的运转机器。主流实现是火山模型(Volcano/Iterator Model)——执行计划是一棵迭代器树,每个节点实现 next() 接口:Scan 节点吐出一行、Filter 节点吞一行判断条件、Join 节点组合两个子节点的输出。拉取式的流水线让内存占用极小(一次一行),代价是虚函数调用的开销——向量化执行(一次一批)和编译执行(把计划编译成机器码,如 Hyper/DuckDB)是两种加速路线。

并发控制也在这层:锁管理器(两阶段锁 2PL)或多版本并发控制(MVCC——写不阻塞读、读不阻塞写,通过快照隔离实现)。事务的 ACID 语义中,隔离性(I)由这层保证,原子性(A)和持久性(D)由这层与存储层的 WAL 协作完成。

这层的典型调优对象:执行计划的稳定性(统计信息过期导致的计划回退)、锁等待和死锁检测、MVCC 的旧版本清理(PostgreSQL 的 VACUUM、MySQL 的 Purge)。

存储层:比特的归宿

存储层回答"数据在磁盘上如何组织"和"如何既快又可靠"。缓冲池是内存与磁盘之间的缓存——以页(16KB/8KB)为单位缓存热数据,脏页由后台线程刷盘。缓冲池命中率是数据库性能的第一指标(命中率 99% vs 95% 意味着磁盘 IO 差 5 倍)。

索引结构决定数据的物理组织。B+ 树(InnoDB、PostgreSQL)——聚簇或非聚簇,点查和范围查都高效,写入代价是维护树的平衡。LSM 树(RocksDB、LevelDB)——写优化(顺序写 WAL + 内存表,批量合并到磁盘层),读放大和空间放大是代价(需要 Bloom Filter 减少无效查找)。行存(OLTP 友好)与列存(OLAP 友好——只读需要的列,压缩率高)是同一层的选择。

事务日志(WAL, Write-Ahead Logging)是可靠性的基石:修改先顺序写日志再改数据页——崩溃后重放日志恢复。顺序写的性能远高于随机写,这是数据库敢承诺持久性的物理基础。Checkpoint 机制控制日志重放的起点(日志太长则恢复时间太长)。

这层的典型故障:缓冲池不足导致的 IO 飙升、LSM 的写停顿(Compaction 与写入争抢 IO)、页损坏(需要检测和修复机制)。

分层的工程意义

排障按层定位。慢查询的第一步是 EXPLAIN——看执行层选了什么计划;计划没问题再看缓冲池命中率和 IO 等待——那是存储层的事;连接堆积、解析报错——访问层的问题。每层有自己的观测指标(连接数/解析耗时、计划/锁等待、命中率/IO),按层排查避免盲人摸象。

扩展点按层选择。加只读副本扩展访问层(读写分离)、加缓存(Redis 在访问层之前)扩展读吞吐;换存储引擎(MySQL 的 InnoDB/MyISAM、RocksDB)是存储层的替换;分区和分片跨层影响。知道"瓶颈在哪一层"才知道"扩展点选在哪一层"。

产品对比按层理解。OLTP 与 OLAP 的本质差异在存储层(行存 vs 列存);MySQL 与 PostgreSQL 的气质差异在执行层(优化器策略、MVCC 实现);NewSQL(TiDB)与单机数据库的差异是三层的全面分布式化。分层的思维让"数据库太多学不过来"变成"每一层的选项就那么几种"。