主内存数据库系统设计/DBMS概述
第 2 章:数据库管理系统简介
数据库管理系统 (DBMS) 是处理数据存储和检索的软件。如今大多数 DBMS 都是关系型 DBMS。本书只关注关系型数据库管理系统。RDBMS 有五个主要组件
- 接口驱动程序
- SQL 引擎
- 事务引擎
- 关系引擎
- 存储引擎
图 1 包含 DBMS 组件、内存布局和与关系型数据库管理系统相关的磁盘文件。从数据库系统发展初期,磁盘被认为是数据的持久存储,以实现持久性。上述架构适用于磁盘驻留数据库系统 (DRDB)。如今,除了 DRDB 之外,还有两种不同的方法。它们是
- 主内存数据库 (MMDB) – 数据存储在主内存中。
- 网络数据库 – 数据存储在网络上的另一台主机上。
上述 DRDB 系统架构中的大多数组件也存在于主内存和网络数据库中。
用户或应用程序程序应启动模式修改或内容修改。这些应用程序请求被 SQL 广泛分类为数据定义语言 (DDL)、数据操作语言 (DML) 和数据控制语言 (DCL)。DDL 处理模式修改;DML 处理内容修改;DCL 处理用户访问和权限修改。如果应用程序程序是用 C/C++ 编写的,它应使用 ODBC 驱动程序连接到 DBMS,或者如果它是用 Java 编写的,它应使用 JDBC 驱动程序连接到 DBMS。一些供应商提供特定于语言的专有接口。例如,MySQL 为 PHP、Python 等提供驱动程序。
这些驱动程序建立在 SQL 之上。它们提供用于准备语句、执行语句、获取结果等的方法。
此组件负责解释和执行 SQL 查询。它包含三个主要组件
编译器 – 从 SQL 语句构建数据结构,然后对查询进行语义检查,例如表是否存在、字段是否存在等。
优化器 – 将编译器创建的初始查询计划(数据结构)转换为通常管道化的操作序列,以实现快速执行。它引用有关数据的元数据(字典)和统计信息,以确定哪些操作序列可能更快,并据此创建最佳查询计划。在 DRDB 的情况下,使用成本和规则两种优化器。
执行引擎 – 执行优化器选择的查询计划中的每个步骤。它与关系引擎交互以检索和存储记录。
事务是读取或写入数据库元素的操作序列,这些操作被分组在一起。事务应具有以下 ACID 属性
原子性:在事务完成之后,只有所有效果或根本没有效果应该出现在数据库中。
一致性:约束应始终使数据库处于一致状态
隔离性:事务应像没有其他事务正在运行一样运行。
持久性:事务完成之后,事务对数据库的影响绝不应丢失。
所有上述属性都在事务章节中详细解释。
事务引擎包含三个主要组件
并发管理器 – 负责对数据的并发同步访问。这通常使用闩锁和锁来实现。闩锁或互斥锁用于短期同步,而锁用于长时间同步。
日志管理器 – 负责事务的原子性和持久性属性。撤消日志确保事务回滚将数据库状态恢复到事务开始之前的先前一致状态。重做日志确保在发生崩溃时恢复所有已提交的事务。
恢复管理器 – 负责从磁盘映像和重做日志文件恢复数据库。大多数数据库使用一种称为“影子分页”的技术来维护内存中磁盘的一致映像。
此组件实现了诸如表、索引和引用完整性约束之类的关系对象。一些主要组件是
字段 – 抽象出列级信息,包括类型、长度等。目录 – 维护有关关系数据库对象(如表、索引、触发器、字段等)的元数据信息。
表 – 负责插入、更新、删除、获取、执行。它与存储引擎的分配器子系统交互,分配器子系统又与缓冲区管理器交互来完成工作。
索引 – 负责所有索引类型的索引节点的插入、更新、删除和扫描。流行的索引类型有哈希和树。哈希索引用于提高点查找(使用主键上的等式作为谓词),而树索引用于提高范围查询(使用键上的大于或小于运算符作为谓词)。
表达式引擎 – 表示数据检索操作的谓词(SQL 语句的 WHERE 子句),并负责计算表达式,这些表达式应包括算术、比较和逻辑表达式。
此组件负责存储和检索数据记录。它还提供用于存储元数据信息和控制信息的机制,例如撤消日志、重做日志、锁定表等。重要的存储引擎组件是
缓冲区管理器 – 负责将页面从磁盘加载到内存,并根据最近最少使用 (LRU) 算法管理缓冲区池。它还具有用于存储控制信息的专用分配器,这些信息是临时的。缓冲区池是缓冲区管理器用于缓存与记录、索引信息、元数据信息相关的磁盘页面的内存空间。一些数据库系统在单个级别上有限制,而另一些数据库系统在全局级别上限制缓冲区池的大小。
文件管理器 – DRDB 中的数据库不过是在磁盘上的一个物理文件。文件管理器将文件的磁盘页面映射到内存页面,并在缓冲区管理器模块生成的重大故障情况下执行实际的磁盘 I/O 操作。
进程管理器 – 负责注册和注销数据库应用程序进程和线程,并记录它们获取的所有资源(事务、锁、闩锁)。
这是用户发出 SELECT SQL 语句时概念上发生的事情,图 2:SQL SELECT 执行顺序
- 用户发出事务开始请求 (startTrans())
- DBMS 为已启动的事务保留一个空闲插槽 (allocSlot())
- DBMS 返回给用户。
- 用户发出 SELECT SQL 请求 (stmtExecute())
- DBMS 解释请求并将其表示为数据结构 (parse())
- DBMS 检查数据库中是否存在表名和字段名 (check())
- DBMS 确定执行语句的最佳方式 (optimize())
- DBMS 通过与关系引擎交互来执行语句 (execute())
- DBMS 检查缓冲区管理器,以确定包含数据的磁盘页面是否已存在于内存中 (isPageInMemory())
- 如果页面尚未加载,DBMS 与文件管理器交互以将页面加载到内存缓冲区 (loadPage())
- DBMS 评估满足谓词的记录 (evaluate())
- DBMS 根据事务的隔离级别对记录加锁 (lockRecord())
- DBMS 检索记录并返回给应用程序 (returnRecords)
- 用户发出事务提交 (commit())
- DBMS 释放事务期间获得的所有锁 (releaseLocks())
- DBMS 释放为该事务分配的事务槽 (freeSlot())
- DBMS 返回应用程序
当用户发出 INSERT SQL 语句时,概念上会发生以下情况:
图 3:SQL INSERT 执行顺序
- 用户发出事务开始请求 (startTrans())
- DBMS 为已启动的事务保留一个空闲插槽 (allocSlot())
- DBMS 返回给用户。
- 用户发出 INSERT SQL 请求 (stmtExecute())
- DBMS 解释请求并将其表示为数据结构 (parse())
- DBMS 检查数据库中是否存在表名和字段名 (check())
- DBMS 确定执行语句的最佳方式 (optimize())
- DBMS 通过与关系引擎交互来执行语句 (execute())
- DBMS 检查缓冲区管理器,以确定需要分配记录的磁盘页面是否已存在于内存中 (isPageInMemory())
- 如果页面尚未加载,DBMS 会与文件管理器交互以将页面加载到内存缓冲区 (loadPage()),以上图中未显示。
- DBMS 为新插入的记录创建撤消日志记录 (createUndoLog())
- DBMS 将应用程序缓冲区中的值复制到分配的新记录 (copyValues())
- DBMS 为新插入的记录创建重做日志记录 (createRedoLog())
- DBMS 根据事务的隔离级别对分配的记录加锁 (lockRecord())
- DBMS 检查索引是否可用,如果可用,则对该表的所有索引进行索引节点插入 (insertIndexNode())
- DBMS 检查缓冲区管理器,以确定需要分配索引节点的索引磁盘页面是否已存在于内存中 (isPageInMemory())
- 如果页面尚未加载,DBMS 会与文件管理器交互以将页面加载到内存缓冲区 (loadPage()),以上图中未显示。
- DBMS 对分配的索引节点加锁 (lockIndexNode())
- DBMS 返回应用程序,并提供受影响的行数 (returnRowsAffected)
- 用户发出事务提交 (commit())
- DBMS 释放事务期间获得的所有锁 (releaseLocks())
- DBMS 释放为该事务分配的事务槽 (freeSlot())
- DBMS 返回应用程序