数据库应试笔记
大意了,没有闪。
之前学习的数据库偏实践,还不足以应付考试。
另外应该是能进复试的。
概念模型
关系模型、关系代数
关系代数的形式定义
订正:PDF 的最后,教材上的答案没有疏漏,是我看不仔细。⋈符号下面没有东西,则默认为自然连接。
函数依赖及范式
SQL Server
这个 pdf 很大,有 80+ 页,大小 50M 左右,再放页面就要卡了。
存储过程和触发器
问:
sqlserver有存储过程的概念,mysql有吗
答:
是的,MySQL也有存储过程的概念,类似于SQL Server。存储过程是一组经过预编译并存储在数据库中的SQL语句,它们可以被视为一种数据库对象。存储过程可以接受参数、执行SQL查询、包含条件逻辑和循环,还可以在应用程序和数据库之间提供更高的安全性和性能。
在MySQL中,你可以使用CREATE PROCEDURE
语句来定义存储过程。存储过程的创建和调用与其他关系型数据库系统中的类似。
一个简单的MySQL存储过程创建示例:
1 | DELIMITER // |
要调用存储过程,可以使用CALL
语句:
1 | CALL sample_procedure(); |
这是一个简单的例子,实际上,存储过程可以包含更复杂的逻辑和多个SQL语句。
问:
上面的代码中,DELIMITER 是什么
答:
在MySQL中,DELIMITER
是一个指令,用于更改语句的结束符。默认情况下,MySQL使用分号;
作为语句的结束符。但是,在存储过程和触发器等复合语句中,会包含多个SQL语句,因此需要使用DELIMITER
来更改结束符,以便正确解析整个存储过程或触发器的定义。
在上面的例子中,DELIMITER //
表示将语句结束符更改为双斜杠//
。这样,在存储过程的定义中,就可以使用分号作为每个SQL语句的结束符,而不会导致MySQL在存储过程体中认为分号是语句结束。
DELIMITER ;
表示将结束符还原为默认的分号。这样,后续的SQL语句在执行时将再次使用分号作为结束符。
接下来验证上述 AIGC :
1 | mysql> select * from member; |
问:
介绍一下sqlserver的触发器机制
答:
在 SQL Server 中,触发器是一种特殊类型的存储过程,它会在数据库表上的特定事件(例如插入、更新或删除操作)发生时自动执行。触发器允许开发者在数据库中实现某些自动化的行为,无需手动触发。以下是 SQL Server 触发器的一般特性:
- 事件触发: 触发器与特定的数据库表相关联,并在该表上的特定事件(INSERT、UPDATE、DELETE)发生时被触发。
- 逐行操作: 触发器通常逐行操作,即对每一行的变化都触发一次,以执行相关的逻辑。
- 分为两类: SQL Server 触发器分为两类:AFTER触发器(在触发事件之后执行)和INSTEAD OF触发器(代替触发事件的默认行为执行)。
- 多事件支持: 一个表可以拥有多个触发器,每个触发器关联到表的不同事件,如INSERT、UPDATE、DELETE。
- 嵌套触发器: 触发器可以是嵌套的,即一个触发器的执行可以导致另一个触发器的调用。
- 引用关键字: 触发器可以使用
INSERTED
和DELETED
虚拟表,这些表包含了由触发事件引起的行的新值和旧值。
一个简单的示例:
1 | CREATE TRIGGER example_trigger |
此触发器在your_table
表上的任何插入、更新或删除后触发,允许你执行相应的逻辑。触发器提供了一种有效的方法,通过在数据库引擎级别执行代码,对表上的事件作出响应。
问:
详细介绍一下sqlserver 的INSTEAD OF触发器
答:
在 SQL Server 中,INSTEAD OF
触发器是一种特殊类型的触发器,与AFTER
和BEFORE
触发器不同,它不是在触发事件之前或之后执行,而是代替触发事件的默认行为执行。主要应用于视图上的INSERT、UPDATE、DELETE等操作,允许自定义逻辑来替代默认的数据操作行为。
以下是INSTEAD OF
触发器的主要特性:
替代默认操作:
INSTEAD OF
触发器允许在触发事件发生时代替默认的数据操作行为,例如,替代视图上的INSERT、UPDATE、DELETE操作。对视图的支持: 通常,
INSTEAD OF
触发器常用于与视图关联的表,以允许在视图上执行的数据修改操作。自定义逻辑: 可以在
INSTEAD OF
触发器中包含自定义的逻辑,例如插入、更新、删除数据之前的验证、转换等。使用
INSERTED
和DELETED
: 与AFTER
和BEFORE
触发器类似,INSTEAD OF
触发器也可以使用INSERTED
和DELETED
虚拟表来引用新插入和被删除的数据。
一个简单的INSTEAD OF
触发器的示例:
1 | CREATE TRIGGER instead_of_trigger |
此触发器在your_view
视图上的插入、更新或删除操作时执行,允许你根据需要完全自定义数据修改的行为。
问:
介绍一下mysql的触发器机制
答:
MySQL的触发器(Triggers)是与表相关联的一种特殊的存储过程,它会在表上的特定事件(INSERT、UPDATE、DELETE)发生时自动执行。以下是MySQL触发器的一般特性:
事件触发: 触发器与特定表关联,并在表上的特定事件发生时被触发,例如INSERT、UPDATE、DELETE。
逐行操作: 触发器通常逐行操作,即对每一行的变化都触发一次,执行相关的逻辑。
BEFORE和AFTER触发器: MySQL支持BEFORE和AFTER触发器。BEFORE触发器在触发事件执行之前执行,而AFTER触发器在触发事件执行之后执行。
OLD和NEW别名: 触发器可以使用OLD和NEW别名引用旧值和新值。比如,在UPDATE触发器中,OLD表示原始值,NEW表示更新后的值。
多事件支持: 一个表可以有多个触发器,每个触发器可以关联到表的不同事件。
不支持INSTEAD OF触发器: 不像某些数据库系统(如SQL Server),MySQL不支持INSTEAD OF触发器。MySQL中的BEFORE触发器在事件发生前执行,AFTER触发器在事件发生后执行。
一个简单的MySQL触发器的创建示例:
1 | CREATE TRIGGER example_trigger |
此触发器在your_table
表上的任何插入后触发,允许你执行相关的逻辑。触发器提供了在数据库引擎级别执行代码以响应表上事件的手段。
下面放一个很长的例子,演示 mysql 的触发器。
设计一个触发器,当在member表中插入或更新记录时,自动将math列的值限制在0到100之间。
1 | mysql> select * from member; |
在上面的代码中,FOR EACH ROW
:
在MySQL触发器中,FOR EACH ROW 是用来指定触发器的执行频率的一部分。这个短语表示触发器将逐行执行,即对每一行受到影响的行执行一次触发器的逻辑。
请注意,FOR EACH ROW 在 MySQL 触发器中是一个必需的元素,因为 MySQL 触发器默认就是逐行执行的,因此在触发器定义中省略这个短语是允许的。
行业黑话
问:
什么是数据库的数据模型和概念模型
答:
数据库的数据模型是描述数据组织、存储和操作方式的抽象表示,常见的数据模型包括关系型、面向对象、层次型等。概念模型则是用户对数据库中信息的认知和理解,通常以实体-关系图等方式展现,有助于沟通数据库设计的概念和结构。
问:
数据库系统的三级模式结构是什么
答:
数据库系统的三级模式结构包括:
- 外部模式(视图层): 这一层描述了用户或应用程序所看到的数据视图,即用户如何看待和访问数据。每个用户可能有自己的外部模式,以满足其特定需求。
- 概念模式(逻辑层): 这是整个数据库的逻辑结构,描述了数据之间的关系、约束和完整性规则。概念模式是整个数据库的逻辑模型,是数据库管理员和数据库设计者关注的层次。
- 内部模式(物理层): 内部模式描述了数据在存储介质上的实际存储方式和存储结构,涉及到物理存储的细节,如存储引擎、索引等。通常,数据库管理系统(DBMS)负责处理内部模式的实现和管理。
这三个层次的模式结构有助于实现数据的抽象和独立性,使用户和应用程序能够更方便地使用数据库系统,而不必关心底层的物理实现。
问:
什么是数据库的两级映像功能
答:
数据库的两级映像功能通常指外部模式与概念模式之间的映像和概念模式与内部模式之间的映像,这体现了数据库系统的数据独立性。
- 外部模式与概念模式的映像: 外部模式是用户或应用程序看到的数据视图,而概念模式是整个数据库的逻辑结构。两者之间的映像确保了用户可以通过其定义的外部模式访问数据库,而不受数据库内部结构的影响。这提供了逻辑数据独立性,允许数据库管理员对数据库的概念模式进行调整,而不影响用户的应用程序。
- 概念模式与内部模式的映像: 概念模式是逻辑层次上的数据库结构,而内部模式涉及数据在物理存储上的具体实现。映像功能确保了对数据库的逻辑设计变更不会对物理存储产生负面影响,从而提供了物理数据独立性。这使得数据库管理员能够优化内部存储结构,而不影响用户和应用程序的逻辑视图。
这两级映像功能带来了数据独立性,使数据库系统更加灵活、可维护,并减少了对数据库结构变更的影响。
问:
什么是数据库的视图?
答:
数据库的视图是一种虚拟表,它是由一个或多个基本表的列组成的查询结果。视图是数据库中的一种逻辑结构,而不是实际存储数据的表。通过视图,用户可以定义一个虚拟表,该表包含特定条件下的数据,而不需要实际创建物理表。
主要作用包括:
- 简化数据访问: 视图允许用户以更简单、更易理解的方式访问数据,通过隐藏表的复杂性,只暴露出用户感兴趣的部分数据。
- 控制数据访问: 视图可以限制用户对数据的访问,只暴露他们需要的信息,从而提高数据库的安全性和隐私性。
- 简化查询: 可以通过视图将复杂的查询逻辑封装起来,用户只需直接查询视图而不必关心底层的表结构和连接。
- 提高数据独立性: 视图可以作为概念模式与外部模式之间的桥梁,使数据库系统更具弹性,可以更轻松地调整表结构而不影响用户的数据访问。
总的来说,数据库的视图提供了一种抽象层,使用户能够以更方便、安全的方式访问和操作数据。
问:select * from customer cross join order_table;
我可以理解为这句创建了一个视图吗
答:
不完全正确。这SQL查询语句是一个交叉连接(cross join)操作,它返回两个表(customer 和 order_table)的所有可能的组合,而不考虑它们之间的任何匹配条件。这不是创建视图的语句,而是一个返回结果的查询。
如果你希望将这个查询的结果保存为一个虚拟表,你可以使用以下语句创建一个视图:
1 | CREATE VIEW your_view_name AS |
这样,你就创建了一个名为 your_view_name 的视图,该视图包含了 customer 表和 order_table 表的交叉连接的结果。随后,你可以通过查询这个视图来获取这个结果,就好像查询一个普通的表一样。
请注意,视图本身并不存储数据,只是一个定义好的查询,每次查询视图时都会动态地生成相应的结果。
问:
上面是mysql的语法吗?如果重启mysql这个视图会消失吗
答:
是的,上述语法是MySQL的语法。在MySQL中,你可以使用CREATE VIEW语句来创建视图。
关于视图的消失问题,视图是数据库中的元数据(metadata),而不是实际存储数据的地方。因此,当你重启MySQL服务时,已创建的视图不会消失,因为它们的定义存储在数据库中。只有在你显式删除视图或删除整个数据库的情况下,相关的视图才会被移除。
所以,一般情况下,重启MySQL服务不会导致已创建的视图消失。
以上 AIGC 已经过实操验证。
查询处理、查询优化
查询处理可分为4个阶段:查询分析、查询检查、查询优化、查询执行。
查询检查:
对合法的查询语句进行语义检查,即根据数据字典中有关的模式定义检查语句中的数据库对象,如关系名、属性名是否存在和有效。如果是对视图的操作,则要用视图消解方法把对视图的操作转换成对基本表的操作。还要根据数据字典中的用户权限和完整性约束定义对用户的存取权限进行检查。如果该用户没有相应的访问权限或违反了完整性约束,就拒绝执行该查询。当然,这时的完整性检查是初步的、静态的检查。检查通过后便把SQL查询语句转换成内部表示,即等价的关系代数表达式。这个过程中要把数据库对象的外部名称转换为内部表示。关系数据库管理系统一般都用查询树(query tree),也称为语法分析树(syntax tree)来表示扩展的关系代数表达式。
查询优化:按照优化的层次一般可将查询优化分为代数优化和物理优化。
查询优化的优点在于:用户不必考虑如何写出最好的查询,因为系统可以做得更好。这是因为:
- 优化器可以从数据字典中获取许多统计信息,例如每个关系表中的元组数、关系中每个属性值的分布情况、哪些属性上已经建立了索引等。优化器可以根据这些信息做出正确的估算,选择高效的执行计划,而用户程序则难以获得这些信息。
- 若数据库的物理统计信息改变,系统可以自动对查询重新优化,以选择相适应的执行计划。在非关系系统中则必须重写程序,而重写程序在实际应用中往往是不太可能的。
- 优化器可以考虑数百种不同的执行计划。
- 优化器中包括了很多复杂的优化技术。
目前关系型数据库管理系统通过某种代价模型计算各种查询执行策略的代价,然后选取代价最小的执行方案。
集中式数据库中,查询执行开销主要包括:I/O代价、CPU代价、内存开销。在分布式数据库中还有通信代价,即:
总代价 = I/O代价 + CPU代价 + 内存代价 + 通信代价
磁盘I/O涉及机械动作,时间比内存操作高几个数量级,因此,计算查询代价时一般用该操作读写的块数作为衡量单位。
代数优化
关系代数表达式的等价变换规则:
对查询树进行代数优化:
模式分解、Armstrong公理系统
这部分内容和实践有些脱节。而且就应试的目的来说,模式分解很多时候凭着直觉就行了。很后悔花了大力气去学一个包装精致、难以理解的形式系统。
数据库安全性
概述
数据库的安全性:保护数据库,防止非法使用造成的数据泄露、更改、破坏。
数据库的不安全因素:
- 非授权用户对数据库的恶意存取和破坏。数据库管理系统提供的安全措施主要包括用户身份鉴别、存取控制和视图等技术。
- 数据库中重要或敏感的数据被泄露。数据库管理系统提供的主要技术有强制存取控制、数据加密存储和加密传输等。此外,在安全性要求较高的部门提供审计功能,通过分析审计日志,可以对潜在的威胁提前采取措施加以防范,对非授权用户的入侵行为及信息破坏情况能够进行跟踪,防止对数据库安全责任的否认。
- 安全环境的脆弱性。数据库的安全性与计算机系统的安全性,包括计算机硬件、操作系统、网络系统等的安全性是紧密联系的。
计算机安全技术方面逐步发展了一套可信(trusted)计算机系统的概念和标准。
计算机以及信息安全技术方面有一系列的安全标准,最有影响的当推 TCSEC 和 CC 这两个标准。
CC 基本取代了 TCSEC ,成为评估信息产品安全性的主要标准。
TCSEC/TDI 安全级别划分:
安全级别 | 定义 |
A1 | 验证设计(verified design) |
B3 | 安全域(security domains) |
B2 | 结构化保护(structural protection) |
B1 | 标记安全保护(labeled security protection) |
C2 | 受控的存取保护(controlled access protection) |
C1 | 自主安全保护(discretionary security protection) |
D | 最小保护(minimal protection) |
与数据库有关的安全性,主要包括用户身份鉴别、多层存取控制、审计、视图和数据加密等安全技术。
上图是数据库安全保护的存取控制流程。
首先,数据库管理系统对提出 SQL 访问请求的数据库用户进行身份鉴别,防止不可信用户使用系统;然后,在 SQL 处理层进行自主存取控制和强制存取控制,进一步还可以进行推理控制。为监控恶意访问,可根据具体安全需求配置审计规则,对用户访问行为和系统关键操作进行审计。通过设置简单入侵检测规则,对异常用户行为进行检测和处理。在数据存储层,数据库管理系统不仅存放用户数据,还存储与安全有关的标记和信息(称为安全数据),提供存储加密功能等。
用户身份鉴别
方法:静态口令鉴别
当前常用的鉴别方法。静态口令一般由用户自己设定,这些口令是静态不变的。这种方式虽然简单,但容易被攻击,安全性较低。数据库管理系统会从口令的复杂度,口令的管理、存储及传输等多方面来保障口令的安全可靠。
方法:动态口令鉴别
目前较为安全的鉴别方式。这种方式的口令是动态变化的,每次鉴别时均需使用动态产生的新口令登录数据库管理系统,即采用一次一密的方法。常用的方式如短信密码和动态令牌方式,每次鉴别时要求用户使用通过短信或令牌等途径获取的新口令登录数据库管理系统。与静态口令鉴别相比,这种认证方式增加了口令被窃取或破解的难度,安全性相对高一些。
方法:生物特征鉴别
它是一种通过生物特征进行认证的技术,其中,生物特征是指生物体唯一具有的,可测量、识别和验证的稳定生物特征,如指纹、虹膜和掌纹等。这种方式通过采用图像处理和模式识别等技术实现了基于生物特征的认证,与传统的口令鉴别相比,无疑产生了质的飞跃,安全性较高。
方法:智能卡鉴别
智能卡是一种不可复制的硬件,内置集成电路的芯片,具有硬件加密功能。智能卡由用户随身携带,登录数据库管理系统时用户将智能卡插入专用的读卡器进行身份验证。由于每次从智能卡中读取的数据是静态的,通过内存扫描或网络监听等技术还是可能截取到用户的身份验证信息,存在安全隐患。因此,实际应用中一般采用个人身份识别码(PIN)和智能卡相结合的方式。这样,即使PIN或智能卡中有一种被窃取,用户身份仍不会被冒充。
存取控制
存取控制机制主要包括定义用户权限和合法权限检查两部分。
- 定义用户权限,并将用户权限登记到数据字典中
- 数据库管理系统必须提供适当的语言来定义用户权限,这些定义经过编译后存储在数据字典中,被称做安全规则或授权规则。
- 合法权限检查
- 每当用户发出存取数据库的操作请求后(请求一般应包括操作类型、操作对象和操作用户等信息),数据库管理系统查找数据字典,根据安全规则进行合法权限检查,若用户的操作请求超出了定义的权限,系统将拒绝执行此操作。
- 定义用户权限和合法权限检查机制一起组成了数据库管理系统的存取控制子系统。
C2 级的数据库管理系统支持自主存取控制(Discretionary Access Control, DAC),B1 级的数据库管理系统支持强制存取控制(Mandatory Access Control, MAC)。
在自主存取控制方法中,用户对于不同的数据库对象有不同的存取权限,不同的用户对同一对象也有不同的权限,而且用户还可将其拥有的存取权限转授给其他用户。因此自主存取控制非常灵活。
在强制存取控制方法中,每一个数据库对象被标以一定的密级,每一个用户也被授予某一个级别的许可证。对于任意一个对象,只有具有合法许可证的用户才可以存取。强制存取控制因此相对比较严格。
自主存取控制
大型数据库管理系统都支持自主存取控制,SQL标准也对自主存取控制提供支持,这主要通过SQL的 GRANT 语句和 REVOKE 语句来实现。
用户权限由两个要素组成:数据库对象和操作类型。在数据库系统中,定义存取权限称为授权(authorization)。
在非关系系统中,用户只能对数据进行操作,存取控制的数据库对象也仅限于数据本身。
在关系数据库系统中,存取控制的对象不仅有数据本身(基本表中的数据、属性列上的数据),还有数据库模式(包括数据库、基本表、视图和索引的创建等)。
数据库角色
数据库角色是被命名的一组与数据库操作相关的权限,角色是权限的集合。因此,可以为一组具有相同权限的用户创建一个角色,使用角色来管理数据库权限可以简化授权的过程。
在SQL中首先用CREATE ROLE语句创建角色,然后用 GRANT 语句给角色授权,用 REVOKE 语句收回授予角色的权限。
强制存取控制
强制存取控制适用于那些对数据有严格而固定密级分类的部门,例如军事部门或政府部门。强制存取控制不为用户直接感知、控制。
在强制存取控制中,数据库管理系统所管理的全部实体被分为主体和客体。
主体是系统中的活动实体,既包括数据库管理系统所管理的实际用户,也包括代表用户的各进程。客体是系统中的被动实体,是受主体操纵的,包括文件、基本表、索引、视图等。对于主体和客体,数据库管理系统为它们每个实例(值)指派一个敏感度标记(label)。
敏感度标记被分成若干级别,例如绝密(Top Secret,TS)、机密(Secret,S)、可信(Confidential,C)、公开(Public,P)等。密级的次序是 TS>=S>=C>=P 。主体的敏感度标记称为许可证级别(clearance level),客体的敏感度标记称为密级(classification level)。强制存取控制机制就是通过对比主体的敏感度标记和客体的敏感度标记,最终确定主体是否能够存取客体。
当某一用户(或某一主体)以标记label注册入系统时,系统要求他对任何客体的存取必须遵循如下规则:
- 仅当主体的许可证级别大于或等于客体的密级时,该主体才能读取相应的客体。
- 仅当主体的许可证级别小于或等于客体的密级时,该主体才能写相应的客体。
规则(1)的意义是明显的,而规则(2)需要解释一下。按照规则(2),用户可以为写入的数据对象赋予高于自己的许可证级别的密级。这样一旦数据被写入,该用户自己也不能再读该数据对象了。如果违反了规则(2),就有可能把数据的密级从高流向低,造成数据的泄漏。例如,某个TS密级的主体把一个密级为TS的数据恶意地降低为P,然后把它写回。这样原来是TS密级的数据大家都可以读到了,造成了TS密级数据的泄漏。
并发控制
封锁协议(locking protocol):约定的一些规则,如何时申请 X 锁或 S 锁、持锁时间、何时释放等。
一级封锁协议:事务T在修改数据R之前必须先对其加X锁,直到事务结束才释放。事务结束包括正常结束(COMMIT)和非正常结束(ROLLBACK)。解决了丢失修改(脏写)的问题。
二级封锁协议:在一级封锁协议基础上增加事务T在读取数据R之前必须先对其加S锁,读完后即可释放S锁。解决了脏读问题。
三级封锁协议:在一级封锁协议的基础上增加事务T在读取数据R之前必须先对其加S锁,直到事务结束才释放。解决了不可重复读、幻读的问题。
活锁:
如果事务 T1 封锁了数据 R,事务 T2 又请求封锁 R,于是 T2 等待;T3 也请求封锁 R,当 T1 释放了 R 上的封锁之后系统首先批准了 T3 的请求,T2 仍然等待;然后 T4 又请求封锁 R,当 T3 释放了 R 上的封锁之后系统又批准了 T4 的请求…… T2 有可能永远等待,这就是活锁的情形,如图11.5(a)所示。
避免活锁的简单方法是采用先来先服务的策略。
死锁:
如果事务 T1 封锁了数据 R1,T2 封锁了数据 R2,然后 T1 又请求封锁 R2,因 T2 已封锁了 R2,于是 T1 等待 T2 释放 R2 上的锁;接着 T2 又申请封锁 R1,因 T1 已封锁了 R1,T2 也只能等待 T1 释放 R1 上的锁。这样就出现了 T1 在等待 T2,而 T2 又在等待 T1 的局面,T1 和 T2 两个事务永远不能结束,形成死锁。如图11.5(b)所示。
OS 中广为采用的预防死锁策略不适合数据库,因此数据库在死锁问题上普遍采用诊断并解除死锁的方法。
诊断死锁的方法,有超时法、事务等待图法。
超时法:
如果一个事务的等待时间超过了规定的时限,就认为发生了死锁。超时法实现简单,但其不足也很明显,一是有可能误判死锁,如事务因为其他原因而使等待时间超过时限,系统会误认为发生了死锁;二是时限若设置得太长,死锁发生后不能及时发现。
事务等待图动态地反映了所有事务的等待情况。并发控制子系统周期性地(比如每隔数秒)生成事务等待图,并进行检测。如果发现图中存在回路,则表示系统中出现了死锁。
多个事务的并发执行是正确的,当且仅当其结果与按某一次序串行地执行这些事务时的结果相同,称这种调度策略为可串行化(serializable)调度。
【例】现在有两个事务,分别包含下列操作:
事务T1: 读B; A=B+1; 写回A;
事务T2: 读A; B=A+1; 写回B。
两段锁协议指所有事务必须分两个阶段对数据项加锁和解锁:
- 在对任何数据进行读、写操作之前,首先要申请并获得对该数据的封锁
- 在释放一个封锁之后,事务不再申请和获得任何其他封锁
“两段”锁的含义是,事务分为两个阶段,第一阶段是获得封锁,也称为扩展阶段,在这个阶段,事务可以申请获得任何数据项上的任何类型的锁,但是不能释放任何锁;第二阶段是释放封锁,也称为收缩阶段,在这个阶段,事务可以释放任何数据项上的任何类型的锁,但是不能再申请任何锁。
可以证明,若并发执行的所有事务均遵守两段锁协议,则对这些事务的任何并发调度策略都是可串行化的。
事务遵守两段锁协议是可串行化调度的充分条件,而不是必要条件。
两段锁协议并不要求事务必须一次将所有要使用的数据全部加锁,因此遵守两段锁协议的事务可能发生死锁。
在一个系统中同时支持多种封锁粒度供不同的事务选择是比较理想的,这种封锁方法称为多粒度封锁(multiple granularity locking)。选择封锁粒度时应该同时考虑封锁开销和并发度两个因素,适当选择封锁粒度以求得最优的效果。一般说来,需要处理某个关系的大量元组的事务可以以关系为封锁粒度;需要处理多个关系的大量元组的事务可以以数据库为封锁粒度;而对于一个处理少量元组的用户事务,以元组为封锁粒度就比较合适了。
下面讨论多粒度封锁,首先定义多粒度树。多粒度树的根结点是整个数据库,表示最大的数据粒度。叶结点表示最小的数据粒度。
一个三级粒度树,根结点为数据库,数据库的子结点为关系,关系的子结点为元组。也可以定义4级粒度树,例如数据库、数据分区、数据文件、数据记录。
多粒度封锁协议允许多粒度树中的每个结点被独立地加锁。对一个结点加锁意味着这个结点的所有后裔结点也被加以同样类型的锁。因此,在多粒度封锁中一个数据对象可能以两种方式封锁,显式封锁和隐式封锁。
显式封锁是应事务的要求直接加到数据对象上的锁;隐式封锁是该数据对象没有被独立加锁,是由于其上级结点加锁而使该数据对象加上了锁。
多粒度封锁方法中,显式封锁和隐式封锁的效果是一样的,因此系统检查封锁冲突时不仅要检查显式封锁还要检查隐式封锁。例如事务T要对关系R1加X锁,系统必须搜索其上级结点数据库、关系R1以及R1的下级结点,即R1中的每一个元组,上下搜索。如果其中某一个数据对象已经加了不相容锁,则T必须等待。
一般地,对某个数据对象加锁,系统要检查该数据对象上有无显式封锁与之冲突;再检查其所有上级结点,看本事务的显式封锁是否与该数据对象上的隐式封锁(即由于上级结点已加的封锁造成的)冲突;还要检查其所有下级结点,看它们的显式封锁是否与本事务的隐式封锁(将加到下级结点的封锁)冲突。显然,这样的检查方法效率很低。为此人们引进了一种新型锁,称为意向锁(intention lock)。
意向锁的含义是如果对一个结点加意向锁,则说明该结点的下层结点正在被加锁;对任一结点加锁时,必须先对它的上层结点加意向锁。
意向共享锁(Intent Share Lock,IS锁):
如果对一个数据对象加IS锁,表示它的后裔结点拟(意向)加S锁。
例如,事务T1要对R1中某个元组加S锁,则要首先对关系R1和数据库加IS锁。
意向排他锁(IntentExclusive Lock, IX 锁):
如果对一个数据对象加IX锁,表示它的后裔结点拟(意向)加X锁。例如,事务T1要对R1 中某个元组加X锁,则要首先对关系R1 和数据库加IX锁。
共享意向排他锁(Share Intent Exclusive Lock,SIX锁):
如果对一个数据对象加SIX锁,表示对它加S锁,再加IX锁,即SIX=S+IX。例如对某个表加SIX锁,则表示该事务要读整个表(所以要对该表加S锁),同时会更新个别元组(所以要对该表加IX锁)。
锁的强度是指它对其他锁的排斥程度。一个事务在申请封锁时以强锁代替弱锁是安全的,反之则不然。
在具有意向锁的多粒度封锁方法中,任意事务T要对一个数据对象加锁,必须先对它的上层结点加意向锁。申请封锁时应该按自上而下的次序进行,释放封锁时则应该按自下而上的次序进行。
例如,事务T1要对关系R1加S锁,则要首先对数据库加IS锁。检查数据库和R1是否已加了不相容的锁(X或IX)。不再需要搜索和检查R1中的元组是否加了不相容的锁(X锁)。
故障与恢复
故障的种类:
- 事务内部的故障
- 有的可以通过事务程序本身发现的。
- 事务内部更多的故障是非预期的,不能由应用程序处理。如运算溢出、并发事务发生死锁而被选中撤销该事务、违反某些完整性限制而被终止等。后续内容中,事务障故仅指这类非预期的故障。
- 事务故障意味着事务没有达到预期终点(COMMIT或显式的ROLLBACK),因此,数据库可能处于不正确状态。恢复程序要在不影响其他事务运行的情况下,强行回滚该事务,即撤销该事务已作出的修改,使得该事务好像根本没有启动一样。这类恢复操作称为事务撤销(UNDO)。
- 系统故障
- 系统故障是指造成系统停止运转的任何事件,使得系统要重新启动。例如,特定类型的硬件错误(CPU故障)、操作系统故障、DBMS代码错误、系统断电等。这类故障影响正在运行的所有事务,但不破坏数据库。此时主存内容,尤其是数据库缓冲区(在内存)中的内容都被丢失,所有运行事务都非正常终止。发生系统故障时,一些尚未完成的事务的结果可能已送入物理数据库,从而造成数据库可能处于不正确的状态。为保证数据一致性,需要清除这些事务对数据库的所有修改。
- 恢复子系统必须在系统重新启动时让所有非正常终止的事务回滚,强行撤销所有未完成事务。
- 另一方面,发生系统故障时,有些已完成的事务可能有一部分甚至全部留在缓冲区,尚未写回到磁盘上的物理数据库中,系统故障使得这些事务对数据库的修改部分或全部丢失,这也会使数据库处于不一致状态,因此应将这些事务已提交的结果重新写入数据库。所以系统重新启动后,恢复子系统除需要撤销所有未完成的事务外,还需要重做(REDO)所有已提交的事务,以将数据库真正恢复到一致状态。
- 介质故障
- 系统故障常称为软故障(soft crash),介质故障称为硬故障(hard crash)。硬故障指外存故障,如磁盘损坏、磁头碰撞,瞬时强磁场干扰等。
- 计算机病毒
恢复的基本原理:冗余。
建立冗余数据最常用的技术是数据转储和登记日志文件(logging)。
转储十分耗费时间和资源,不能频繁进行。
转储可分为静态转储和动态转储。
静态转储是在系统中无运行事务时进行的转储操作。静态转储简单,但转储必须等待正运行的用户事务结束才能进行。同样,新的事务必须等待转储结束才能执行。
动态转储在转储期间允许对数据库存取或修改。必须把转储期间各事务对数据库的修改活动登记下来,建立日志文件(log file)。这样,后援副本加上日志文件就能把数据库恢复到某一时刻的正确状态。
转储还可分为海量转储和增量转储两种方式。
综上,数据转储方法可以分为 4 类:动态海量转储、动态增量转储、静态海量转储和静态增量转储。
日志文件主要有两种格式:以记录为单位的日志文件和以数据块为单位的日志文件。
对于以记录为单位的日志文件,日志文件中需要登记的内容包括:
- 各个事务的开始(BEGIN TRANSACTION)标记
- 各个事务的结束(COMMIT或ROLLBACK)标记
- 各个事务的所有更新操作。
这里每个事务的开始标记、每个事务的结束标记和每个更新操作均作为日志文件中的一个日志记录(log record)。
每个日志记录的内容主要包括:
- 事务标识(标明是哪个事务)
- 操作的类型(插入、删除或修改)
- 操作对象(记录内部标识)
- 更新前数据的旧值(对插入操作而言,此项为空值)
- 更新后数据的新值(对删除操作而言,此项为空值)
对于以数据块为单位的日志文件,日志记录的内容包括事务标识和被更新的数据块。由于将更新前的整个块和更新后的整个块都放入日志文件中,操作类型和操作对象等信息就不必放入日志记录中了。
事务故障恢复和系统故障恢复必须用日志文件。
在动态转储方式中必须建立日志文件,后备副本和日志文件结合起来才能有效地恢复数据库。
在静态转储方式中也可以建立日志文件,当数据库毁坏后可重新装入后援副本把数据库恢复到转储结束时刻的正确状态,然后利用日志文件把已完成的事务进行重做处理,对故障发生时尚未完成的事务进行撤销处理。
为保证数据库是可恢复的,登记日志文件时必须遵循两条原则:
- 登记的次序严格按并发事务执行的时间次序。
- 必须先写日志文件,后写数据库。
事务故障是指事务在运行至正常终止点前被终止,这时恢复子系统应利用日志文件撤销(UNDO)此事务已对数据库进行的修改。事务故障的恢复是由系统自动完成的,对用户是透明的。系统的恢复步骤是:
- 反向扫描日志文件(即从最后向前扫描日志文件),查找该事务的更新操作。
- 对该事务的更新操作执行逆操作,即将日志记录中“更新前的值”写入数据库。这样,如果记录中是插入操作,则相当于做删除操作(因此时“更新前的值”为空);若记录中是删除操作,则做插入操作;若是修改操作,则相当于用修改前值代替修改后值。
- 继续反向扫描日志文件,查找该事务的其他更新操作,并做同样处理。
- 如此处理下去,直至读到此事务的开始标记,事务故障恢复就完成了。
系统故障造成数据库不一致状态的原因有两个,一是未完成事务对数据库的更新可能已写入数据库,二是已提交事务对数据库的更新可能还留在缓冲区没来得及写入数据库。因此恢复操作就是要撤销故障发生时未完成的事务,重做已完成的事务。
系统故障的恢复是由系统在重新启动时自动完成的,不需要用户干预。
系统的恢复步骤是:
- 正向扫描日志文件(即从头扫描日志文件),找出在故障发生前已经提交的事务(这些事务既有BEGIN TRANSACTION记录,也有COMMIT记录),将其事务标识记入重做队列(REDO-LIST)。同时找出故障发生时尚未完成的事务(这些事务只有 BEGIN TRANSACTION 记录,无相应的 COMMIT 记录),将其事务标识记入撤销队列(UNDO-LIST).
- 对撤销队列中的各个事务进行撤销(UNDO)处理。进行撤销处理的方法是,反向扫描日志文件,对每个撤销事务的更新操作执行逆操作,即将日志记录中“更新前的值”写入数据库。
- 对重做队列中的各个事务进行重做处理。进行重做处理的方法是:正向扫描日志文件,对每个重做事务重新执行日志文件登记的操作,即将日志记录中“更新后的值”写入数据库。
发生介质故障后,磁盘上的物理数据和日志文件被破坏,这是最严重的一种故障,恢复方法是重装数据库,然后重做已完成的事务。
- 装入最新的数据库后备副本(离故障发生时刻最近的转储副本),使数据库恢复到最近一次转储时的一致性状态。对于动态转储的数据库副本,还需同时装入转储开始时刻的日志文件副本,利用恢复系统故障的方法(即REDO+UNDO),才能将数据库恢复到一致性状态。
- 装入相应的日志文件副本(转储结束时刻的日志文件副本),重做已完成的事务。即首先扫描日志文件,找出故障发生时已提交的事务的标识,将其记入重做队列;然后正向扫描日志文件,对重做队列中的所有事务进行重做处理。即将日志记录中“更新后的值”写入数据库。
介质故障的恢复需要数据库管理员介入,但数据库管理员只需要重装最近转储的数据库副本和有关的各日志文件副本,然后执行系统提供的恢复命令即可,具体的恢复操作仍由数据库管理系统完成。
利用日志技术进行数据库恢复时,恢复子系统必须搜索日志,确定哪些事务需要重做,哪些事务需要撤销。一般来说,需要检查所有日志记录。这样做有两个问题,一是搜索整个日志将耗费大量的时间,二是很多需要重做处理的事务实际上已经将它们的更新操作结果写到了数据库中,然而恢复子系统又重新执行了这些操作,浪费了大量时间。为了解决这些问题,又发展了具有检查点的恢复技术。这种技术在日志文件中增加一类新的记录——检查点(checkpoint)记录,增加一个重新开始文件,并让恢复子系统在登录日志文件期间动态地维护日志。
检查点记录的内容包括:
- 建立检查点时刻所有正在执行的事务清单。
- 这些事务最近一个日志记录的地址。
重新开始文件用来记录各个检查点记录在日志文件中的地址。图10.3说明了建立检查点Ci时对应的日志文件和重新开始文件。
在上图中,检查点处的数据库是合法状态。若不然,则可以通过“这些事务最近一个日志记录的地址”达到合法状态。
动态维护日志文件的方法是,周期性地执行建立检查点、保存数据库状态的操作。具体步骤是:
- 将当前日志缓冲区中的所有日志记录写入磁盘的日志文件上。
- 在日志文件中写入一个检查点记录。
- 将当前数据缓冲区的所有数据记录写入磁盘的数据库中。
- 把检查点记录在日志文件中的地址写入一个重新开始文件。恢复子系统可以定期或不定期地建立检查点,保存数据库状态。检查点可以按照预定的一个时间间隔建立,如每隔一小时建立一个检查点;也可以按照某种规则建立检查点,如日志文件已写满一半建立一个检查点。
使用检查点方法可以改善恢复效率。当事务T在一个检查点之前提交,T对数据库所做的修改一定都已写入数据库,写入时间是在这个检查点建立之前或在这个检查点建立之时。这样,在进行恢复处理时,没有必要对事务T执行重做操作。
系统出现故障时,恢复子系统将根据事务的不同状态采取不同的恢复策略:
T3和T5在故障发生时还未完成,所以予以撤销;T2和T4在检查点之后才提交,它们对数据库所做的修改在故障发生时可能还在缓冲区中,尚未写入数据库,所以要重做;T1在检查点之前已提交,所以不必执行重做操作。
系统使用检查点方法进行恢复的步骤是:
- 从重新开始文件中找到最后一个检查点记录在日志文件中的地址,由该地址在日志文件中找到最后一个检查点记录。
- 由该检查点记录得到检查点建立时刻所有正在执行的事务清单ACTIVE-LIST。这里建立两个事务队列:
- UNDO-LIST: 需要执行UNDO操作的事务集合
- REDO-LIST: 需要执行REDO操作的事务集合
把 ACTIVE-LIST 暂时放入 UNDO-LIST 队列,REDO 队列暂为空。
- 从检查点开始正向扫描日志文件。
- 如有新开始的事务Ti,把Ti暂时放入UNDO-LIST队列;
- 如有提交的事务Tj,把Tj从UNDO-LIST队列移到REDO-LIST队列;直到日志文件结束。
- 对UNDO-LIST中的每个事务执行UNDO操作,对REDO-LIST中的每个事务执行REDO操作。
数据库镜像:
数据库的实施与维护
数据库应用程序的设计应该与数据库设计同时进行,因此在组织数据入库的同时还要调试应用程序。
在原有系统的数据有一小部分已输入数据库后,就可以开始对数据库系统进行联合调试了,这又称为数据库的试运行。
这一阶段要实际运行数据库应用程序,执行对数据库的各种操作,测试应用程序的功能是否满足设计要求。如果不满足,对应用程序部分则要修改、调整,直到达到设计要求为止。
在数据库试运行时,还要测试系统的性能指标,分析其是否达到设计目标。
这里特别要强调两点。第一,上面已经讲到组织数据入库是十分费时、费力的事,如果试运行后还要修改数据库的设计,还要重新组织数据入库。因此应分期分批地组织数据入库,先输入小批量数据做调试用,待试运行基本合格后再大批量输入数据,逐步增加数据量,逐步完成运行评价。第二,在数据库试运行阶段,由于系统还不稳定,硬、软件故障随时都可能发生;而系统的操作人员对新系统还不熟悉,误操作也不可避免,因此要做好数据库的转储和恢复工作。一旦故障发生,能使数据库尽快恢复,尽量减少对数据库的破坏。
在运行阶段,数据库的维护工作主要包括以下几方面:
- 数据库的转储和恢复:数据库管理员要针对不同的应用要求制定不同的转储计划。
- 数据库的安全性、完整性控制
- 数据库性能的监督、分析和改造
- 数据库的重组织与重构造
数据库运行一段时间后,由于记录不断增、删、改,将会使数据库的物理存储情况变坏,降低数据的存取效率,使数据库性能下降,这时数据库管理员就要对数据库进行重组织或部分重组织(只对频繁增、删的表进行重组织)。关系数据库管理系统一般都提供数据重组织用的实用程序。在重组织的过程中,按原设计要求重新安排存储位置、回收垃圾、减少指针链等,提高系统性能。
数据库的重组织并不修改原设计的逻辑和物理结构,而数据库的重构造则不同,它是指部分修改数据库的模式和内模式。
由于数据库应用环境发生变化,增加了新的应用或新的实体,取消了某些应用,有的实体与实体间的联系也发生了变化等,使原有的数据库设计不能满足新的需求,需要调整数据库的模式和内模式。例如,在表中增加或删除某些数据项,改变数据项的类型,增加或删除某个表,改变数据库的容量,增加或删除某些索引等。当然数据库的重构也是有限的,只能做部分修改。如果应用变化太大,重构也无济于事,说明此数据库应用系统的生命周期已经结束,应该设计新的数据库应用系统了。
了解内容
面向对象数据库模型
一系列面向对象核心概念构成了面向对象数据模型(Object Oriented Data Model,OO模型)的基础,主要包括以下一些概念:
- 现实世界中的任何事物都被建模为对象。每个对象具有一个唯一的对象标识(OID)。
- 对象是其状态和行为的封装,其中状态是对象属性值的集合,行为是变更对象状态的方法集合。
- 具有相同属性和方法的对象的全体构成了类,类中的对象称为类的实例。
- 类的属性的定义域也可以是类,从而构成了类的复合。类具有继承性,一个类可以继承另一个类的属性与方法,被继承类和继承类也称为超类和子类。类与类之间的复合与继承关系形成了一个有向无环图,称为类层次。
- 对象是被封装起来的,它的状态和行为在对象外部不可见,从外部只能通过对象显式定义的消息传递对对象进行操作。
分布式数据库系统
分布式数据库系统是在集中式数据库系统和计算机网络的基础上发展起来的,它是分布式数据处理的关键技术之一。分布式数据库由一组数据组成,这组数据分布在计算机网络的不同计算机上,网络中的每个结点具有独立处理的能力(称为场地自治),可以执行局部应用。同时,每个结点也能通过网络通信系统执行全局应用。
这个定义强调了分布式数据库系统的场地自治性以及自治场地之间的协作性。这就是说,每个场地是独立的数据库系统,它有自己的数据库、自己的用户、自己的服务器,运行自己的DBMS,执行局部应用,具有高度的自治性。同时各个场地的数据库系统又相互协作组成一个整体。这种整体性的含义是,对于用户来说,一个分布式数据库系统逻辑上看如同一个集中式数据库系统一样,用户可以在任何一个场地执行全局应用。
因此,分布式数据库系统不是简单地把集中式数据库连网就能实现的。分布式数据库系统具有自己的性质和特征。集中式数据库的许多概念和技术,如数据独立性、数据共享和数据冗余、并发控制、完整性、安全性和恢复等,在分布式数据库系统中都有了新的更加丰富的内容。
分布式数据库系统的本地自治性(local autonomy)是指局部场地的数据库系统可以自已决定本地数据库的设计、使用以及与其他节点的数据库系统的通信。分布式数据库系统的分布透明性(distributed transparency)是指分布式数据库管理系统将数据的分布封装起来,用户访问分布式数据库就像与集中式数据库打交道一样,不必知道也不必关心数据的存放和操作位置等细节。
分布式数据库系统在集中式数据库系统的组成基础上增加了三个部分: DDBMS、全局字典和分布目录、网络访问进程。全局字典和分布目录为DDBMS提供了数据定位的元信息,网络访问进程使用高级协议来执行局部站点和分布式数据库之间的通信。
应试练习
在上面的练习中,发现一类复杂的 SQL 查询可以转化成类似下面的便于理解的查询:
1 | mysql> select * from member; |
数据库我卷卷卷: