文章
数据库的工作流程—自顶向下
整个流程可以大致分为以下几个核心阶段: 1. 连接管理 (Connection Handling) 2. 查询处理 (Query Processing) 3. 事务管理与并发控制 (Transaction & Concurrency Management) 4. 存储引擎与数据访问 (Storage Engine & Data Access) 5. 结果返回 (Result Returning)
目录
让我们以一个典型的关系型数据库系统(如 PostgreSQL, MySQL, SQL Server 等)为例,自顶向下地详细说明其处理一个查询(例如 SELECT 语句)的工作流程。
可以把数据库想象成一个管理着巨大图书馆(数据)的高度智能化的机器人系统(数据库管理系统 DBMS)。当一个用户(客户端应用)需要信息时,它会向系统提交一个请求(SQL 查询),然后系统内部经过一系列复杂的步骤来找到并返回用户需要的信息。
整个流程可以大致分为以下几个核心阶段:
- 连接管理 (Connection Handling)
- 查询处理 (Query Processing)
- 事务管理与并发控制 (Transaction & Concurrency Management)
- 存储引擎与数据访问 (Storage Engine & Data Access)
- 结果返回 (Result Returning) 下面我们详细展开每一个阶段:
阶段一:连接管理 (Connection Handling)#
- 1.1 监听与接受 (Listening & Accepting):
- 数据库服务器启动后,会有一个监听进程 (Listener) 在特定的网络端口(如 PostgreSQL 的 5432, MySQL 的 3306)上等待客户端的连接请求。
- 当一个客户端应用(如 Web 服务器、桌面应用或命令行工具)发起连接时,监听进程接收到请求。
- 1.2 握手与认证 (Handshake & Authentication):
- 监听进程通常会为这个新连接派生(fork/spawn)一个工作进程/线程 (Worker Process/Thread),或者将其交给一个连接池 (Connection Pool) 中的可用工作者。
- 这个工作者与客户端进行通信,进行网络协议的握手。
- 客户端发送用户名和密码(或其他凭证)。数据库会对照内部的用户账户信息和认证规则(如密码哈希、主机限制等)进行身份验证 (Authentication)。
- 1.3 会话建立与授权 (Session Setup & Authorization):
- 验证通过后,数据库为这个连接建立一个会话 (Session)。这会分配一些服务器端的内存资源,并设置会话相关的上下文信息(如当前数据库、字符集、时区等)。
- 数据库会检查该用户拥有的权限 (Privileges),确定该用户在这个会话中可以执行哪些操作(如 SELECT, INSERT, UPDATE, DELETE)以及可以访问哪些对象(表、视图等)。这称为授权 (Authorization)。
阶段二:查询处理 (Query Processing) - 数据库的大脑#
这是最核心的部分,负责理解、优化并执行 SQL 查询。
- 2.1 查询解析 (Query Parsing):
- 接收 SQL: 工作进程/线程从客户端接收到 SQL 语句字符串(例如
SELECT name FROM users WHERE age > 30;)。 - 词法分析 (Lexical Analysis): 将 SQL 字符串分解成一系列有意义的标记 (Tokens),如关键字 (
SELECT,FROM,WHERE)、标识符 (name,users,age)、操作符 (>) 和字面量 (30)。 - 语法分析 (Syntax Analysis): 根据 SQL 的语法规则,检查这些标记的组合是否构成一个合法的 SQL 语句。如果合法,它会构建一个抽象语法树 (Abstract Syntax Tree - AST) 或解析树 (Parse Tree)。这棵树清晰地表达了查询的结构和意图。
- 接收 SQL: 工作进程/线程从客户端接收到 SQL 语句字符串(例如
- 2.2 查询验证与绑定 (Query Validation & Binding):
- 语义分析: 检查解析树在语义上是否有效。这包括:
- 对象存在性: 查询中引用的表(
users)和列(name,age)是否存在于数据库元数据 (Metadata) / 系统目录 (System Catalog) 中? - 类型检查: 操作是否兼容(例如,
age是否是可与30进行比较的类型)? - 权限检查: 当前用户是否有权限读取
users表和其中的列?
- 对象存在性: 查询中引用的表(
- 名称解析/绑定: 将查询中的标识符明确地绑定到数据库中的具体对象。
- 输出: 生成一个内部的、经过验证和注解的查询表示,通常是一个逻辑查询计划 (Logical Query Plan)。
- 语义分析: 检查解析树在语义上是否有效。这包括:
- 2.3 查询优化 (Query Optimization):
- 这是数据库“智能”的关键体现。目标是找到执行这个逻辑查询计划的成本最低的物理执行计划 (Physical Execution Plan)。
- 逻辑优化/重写: 应用一系列基于关系代数的等价变换规则来重写逻辑计划,使其可能更有效率。例如:
- 谓词下推 (Predicate Pushdown): 尽可能早地进行数据过滤(将
WHERE条件下推到扫描或连接操作之前)。 - 连接重排 (Join Reordering): 改变多表连接的顺序,通常先连接能产生更小中间结果的表。
- 视图展开/子查询解耦: 简化查询结构。
- 谓词下推 (Predicate Pushdown): 尽可能早地进行数据过滤(将
- 物理优化 (Cost-Based Optimization):
- 生成候选计划: 对于重写后的逻辑计划,生成多种可能的物理执行方式。例如:
- 访问表的方式:全表扫描 (Full Table Scan) 还是索引扫描 (Index Scan)?用哪个索引?
- 连接算法:嵌套循环连接 (Nested Loop Join)、哈希连接 (Hash Join) 还是合并连接 (Merge Join)?
- 成本估算: 使用数据库维护的统计信息 (Statistics)(如表的大小、列的基数、数据分布直方图等)来估算每个候选物理计划的执行成本(主要是 I/O 成本和 CPU 成本)。
- 选择最佳计划: 选择估算成本最低的那个物理执行计划。
- 生成候选计划: 对于重写后的逻辑计划,生成多种可能的物理执行方式。例如:
- 输出: 最终选定的、可执行的物理执行计划。它通常是一个由各种物理操作符 (Physical Operators)(如
IndexScan,HashJoin,Sort,Filter)组成的树状结构。
- 2.4 查询执行 (Query Execution):
- 执行引擎 (Execution Engine) 接收物理执行计划。
- 它按照计划树的结构,通常采用火山模型/迭代器模型 (Volcano/Iterator Model) 来执行。这意味着每个操作符都实现了
open(),next(),close()这样的接口。 - 执行从根节点开始,根节点调用其子节点的
next()方法来请求数据行,子节点再调用其子节点的next(),如此层层递进,直到叶子节点(通常是扫描操作符)。 - 数据行从叶子节点产生,然后像流水线一样向上流经各个操作符,被过滤、连接、排序、聚合,最终到达根节点。
阶段三:事务管理与并发控制 (Transaction & Concurrency Management)#
在查询执行的每一步,尤其是涉及数据修改(INSERT, UPDATE, DELETE)或需要一致性读取时,这一层都在默默工作,确保 ACID 属性。
- 3.1 事务开始: 如果查询是一个事务的一部分(或者是一个隐式事务),事务管理器会启动一个新事务或关联到现有事务。
- 3.2 并发控制: 当多个查询同时执行时,并发控制管理器确保它们互不干扰,维护隔离性 (Isolation)。常用技术:
- 锁 (Locking): 通过锁管理器获取对数据(行、页、表)的读锁或写锁(如两阶段锁定 2PL)。如果请求的锁与现有锁冲突,则该事务可能需要等待。
- 多版本并发控制 (Multi-Version Concurrency Control - MVCC): 数据库为数据维护多个版本。读操作读取事务开始时可见的版本,写操作则创建新版本。这大大减少了读写冲突,提高了并发性(PostgreSQL 和 Oracle 常用)。
- 3.3 日志记录 (Logging): 对于数据修改操作,日志管理器会生成日志记录 (Log Records),并遵循预写日志 (Write-Ahead Logging - WAL) 原则,将这些记录先于数据本身写入到稳定的事务日志 (Transaction Log) 中。这保证了原子性 (Atomicity) 和持久性 (Durability)。
阶段四:存储引擎与数据访问 (Storage Engine & Data Access) - 数据库的手脚#
执行引擎需要数据时,会向这一层发出请求。
- 4.1 访问方法 (Access Methods):
- 根据执行计划的要求(如
IndexScan或TableScan),调用相应的访问方法。 - 如果是索引扫描,会使用 B+ 树或其他索引结构来快速定位数据。
- 如果是全表扫描,会顺序读取所有数据。
- 根据执行计划的要求(如
- 4.2 缓冲池管理 (Buffer Pool Management):
- 数据库的核心性能组件。它在内存 (RAM) 中维护一个缓冲池 (Buffer Pool),缓存磁盘上的数据页 (Data Pages)。
- 当访问方法需要一个数据页时,缓冲池管理器首先检查该页是否已在缓冲池中(缓存命中)。
- 命中: 直接从内存返回该页,速度非常快。
- 未命中:
- 如果缓冲池已满,根据替换策略(如 LRU 或 Clock)选择一个页面逐出 (Evict)。如果被逐出的页面是“脏”的(被修改过),则必须先将其写回磁盘。
- 从磁盘读取所需的页面到缓冲池中。
- 将该页返回给访问方法。
- 4.3 磁盘空间管理与 I/O (Disk Space & I/O):
- 当需要从磁盘读写页面时,缓冲池管理器会与磁盘空间管理器协作。
- 后者知道数据库文件在磁盘上的布局,负责分配和释放磁盘块,并将读写请求转换为对操作系统文件系统的
read()和write()调用。
- 4.4 恢复管理 (Recovery Management):
- 利用事务日志,确保即使发生系统崩溃(如断电),数据库也能恢复到一致的状态。
- 在启动时,它会分析日志,重做 (Redo) 已提交但可能未写入磁盘的事务,并撤销 (Undo) 未提交的事务。
阶段五:结果返回 (Result Returning)#
- 5.1 结果集生成: 查询执行引擎的根节点产生最终的结果行。
- 5.2 格式化与传输:
- 这些行被格式化成客户端期望的格式和数据类型。
- 通过之前建立的网络连接,将结果集流式 (Streaming) 或分批 (Batching) 发送回客户端应用。
- 5.3 事务结束: 如果是事务的一部分,此时可能会执行
COMMIT或ROLLBACK,事务管理器会记录事务结束,并释放相应的锁。 - 5.4 清理: 工作进程/线程完成请求后,可能会释放一些资源,并准备处理来自同一客户端的下一个请求,或者将连接返回连接池。
这个流程描绘了一个相当复杂但高度优化的系统。每个步骤都可能包含大量的算法和数据结构,但它们共同协作,为用户提供了一个看似简单(通过 SQL)却功能强大的数据管理平台。