在 DBS 运行时,DBMS 要对 DB 进行监控,以保证整个系统的正常运转,防止数据意外丢失和不一致数据的产生。DBMS 对 DB 的监控,称为 数据库管理。
主要通过四个方面实现:数据库的恢复、并发控制、完整性控制、安全性控制。每一方面构成了 DBMS 的一个子系统。
DBS 运行的最小逻辑工作单位是“事务”,所有的数据库操作都要以事务作为一个整体单位来执行或撤销。
事务
事务(Transaction) 是构成单一逻辑工作单元的操作集合,要么完整的执行,要么完全不执行。不论发生何种情况,DBS 必须保证事务能正确、完整地执行。
DBS 执行事务,相当于操作系统环境中的“进程”概念,一个事务由 BEGIN TRANSACTION 语句开始,以 COMMIT 或 ROLLBACK 语句结束。
- 事务的 ACID 性质(为保证数据库中数据总是正确的)
- 原子性(Atomic)。一个事务的所有操作是不可分割的单元,要么全部执行,要么什么也不做。由 DBMS 的事务管理子系统实现。
- 一致性(Consistency)。一个事务独立执行的结果,应保持数据库状态一致。由 DBMS 的完整性子系统实现。
- 隔离性(Isolation)。多个事务并发执行的结果,系统应保证与它们先后单独执行时的结果一样。由 DBMS 的并发控制子系统实现。
- 持久性(Durability)。一个事务完成全部操作后,对数据库的更新应永久的反映在数据库中,不会丢失。由 DBMS 的恢复管理子系统实现。
数据库的恢复
系统能把数据库从被破坏、不正确的状态,恢复到最近一个正确的状态,DBMS 的这种能力称为 数据库的可恢复性。恢复的基本原则很简单,就是转储和建立日志数据库。当遭遇物理性灾难故障,可装入最近一次拷贝的数据库备份到新的磁盘,然后利用日志库执行“重做(REDO)”已提交的事务,可恢复到故障前。当遇到破坏了数据库的一致性时,不必拷贝存档,只要利用日志库“撤销(UNDO)”所有不可靠的修改。
- 检查点方法
大多数 DBMS 产品都提供这个方法来实现 REDO 和 UNDO。DBMS 定时设置检查点(Check point),在检查点时刻才真正把对 DB 的修改写到磁盘,同时在日志里写入一条检查点记录,以便恢复时使用。
p1-------p2---------p3--------
T1--->
T2--------->
T3---------------------->
如果检查点 p3 为故障点,则 T1 不必恢复,因为执行结果已写入数据库;T2 需 REDO,虽然执行完,但对 DB 的修改尚在内存缓冲区,还未写到磁盘;T3 需 UNDO,因为还未做完。
数据库的并发控制
并发操作可能会破坏数据库的完整性。这里的并发指在单 CPU 上用分时方法实行多个事务同时做,并发操作通常会带来三个问题:丢失更新问题、读脏数据问题、不可重复读问题。
- 丢失更新问题
例1. A = 100,T1 对 A 减 30,T2 对 A 加倍,若两个事务单独做,可见 A = 140 或 A = 170 都是正确的,看先后顺序。而当两个事务都读取了 A 值(100),T1 更新后 T2 再更新,则 A = 200,T1 的更新丢失了。
- 读脏数据问题
例2.(读脏数据但未破坏完整性) T1 把 A 改为 70,T2 读取 A 值 70,T1 执行了 ROLLBACK 操作,把 A 值 恢复为 100,而 T2 仍在使用被撤销了的 A 值 70。数据库中把未提交随后又撤销了的数据称为“脏数据”。
例3.(读脏数据,引起自身更新被丢失,破坏了完整性)T1 把 A 改为 70,T2 读取 A 值 70,T2 执行了翻倍操作 A = 140 并更新,之后 T1 又执行了 ROLLBACK 操作,把 A 值从 70 又恢复为 100。这种情况更糟,T2 不仅读了未提交的 A 值(70),最后还丢失了子集的更新操作,破坏了数据库的完整性。
- 不可重复读问题
例4. T1 在一次事务中需要读取两次 A 值,但在这个时间间隔中 T2 改变了 A 的值,这就导致 T1 两次读取同一数据项 A 却出现不同的值。
封锁技术
锁(Lock) 是一个与数据项相关的变量,对可能应用于该数据项上的操作而言,锁描述了该数据项的状态。为解决并发控制带来的问题,通常要采用封锁技术,常用的封锁有:排他型封锁(X 锁) 和 共享型封锁(S 锁)。
- 排他型封锁(简称 X 锁,最常用,又称为 写锁)
如果事务 T 对某个数据项 R 实现了 X 锁,那么在 T 对 R 解除封锁之前,不允许其他事务再对该数据加任何类型的锁,这种锁称为 X 锁。
使用 X 锁的操作有两个:
- 申请 X 锁操作“XFIND R”:事务对 R 申请加 X 锁,若成功,则可读写数据 R,如果不成功,那么这个事务将进入等待队列,一直到获准 X 锁,才能继续下去。
- 解除 X 锁操作“XRELEASE R”:表示事务要解除对数据 R 的 X 锁。为防止过早的解锁而造成其他事务读取了未提交的数据,系统中没有解除 X 锁操作的语句,而是合并在了 COMMIT 和 ROLLBACK 中。
- 共享型封锁(简称 S 锁,又称为 读锁)
采用 X 锁的并发控制,并发度低,只允许一个事务独锁数据,而其他申请封锁的事务只能排队等待。为此,降低要求,允许并发的读,就引入了共享型封锁(Shared Lock)。允许多个事务都可对数据项 R 加 S 锁,但在该数据项上所有的 S 锁都解除之前决不允许任何事务对该数据加 X 锁。
使用 S 锁的操作有三个:
- 申请 S 锁操作“SFIND R”:事务对 R 申请加 S 锁,若成功,则可读数据 R,但不可以写数据 R;如果不成功,那么这个事务将进入等待队列,一直到获准 S 锁,才能继续下去。
- 升级和写操作“UPDX R”:事务要把对数据项 R 上的 S 锁升级为 X 锁,若成功则可更新数据 R,否则进入等待队列。
- 解除 S 锁操作“SRELEASE R”:解除对数据项 R 的 S 锁。由于 S 锁只允许读取数据,因此解除 S 锁不必非要合并到事务的结束操作中。
S 锁可以解决丢失更新的问题,但带来了新的问题:死锁。如两个事务都申请了 S 锁,又都要升级为 X 锁去更新,谁也不解除,那就形成了死锁。
- 封锁的粒度
封锁的对象的大小称为 封锁的粒度。封锁对象可以很大,如整个数据库;也可以很小,如某个属性值。封锁力度与系统的并发度和并发控制密切相关,封锁的粒度越大,并发度就越低,但同时系统的开销也就越小。
- 封锁协议
运用封锁机制时,要约定一些规则,称为协议。下表为三级封锁协议,在不同程度上解决了并发操作带来的问题,为正确调度提供一定的保证。
并发操作的调度
事务的执行次序称为 事务的调度。如果多个事务依次执行,称为 事务的串行调度。如果利用分时的方法,同时处理多个事务,则称为 事务的并发调度。
如果有 n 个事务串行调度,则可能有 n! 种不同的有效调度。如果有 n 个事务并发调度,可能的并发调度数目远远大于 n!,但其中有的并发调度是正确的,有的是错误的。
- 可串行化概念
每个事务中,语句的先后顺序在各种调度中始终保持一致。在这个前提下,如果一个并发调度的执行结果与某一串行调度的执行结果等价,那么,这个并发调度称为 可串行化的调度,否则是不可串行化的调度。
例如之前的例1,先 T1 后 T2 或者 先 T2 后 T1,A 的结果为 140 或 170,这两种串行调度都认为是正确的。而当 A 的执行结果为 200 时,与任何一个串行调度的结果都不相同,因而这种并发调度是错误的,即称为不可串行化的调度。
SQL 对并发处理的支持
SQL2 对事务的存取模式(Access Mode)和隔离级别(Isolation Level)做了具体规定,并提供语句让用户使用,以控制事务的并发执行。
- 事务的存取模式
- 只读型:定义这个模式后,随后的事务均为只读型。
- 读写型:定义这个模式后,随后的事务均为读写型。这是默认模式。
SET TRANSACTION READ ONLY
SET TRANSACTION READ WRITE
- 事务的隔离级别
- SERIALIZABLE(可串行化):默认级别。允许事务并发执行,但系统必须保证并发调度是可串行化的,不致发生错误。
- REPEATABLE READ(可重复读):只允许事务读已提交的数据,并且在两次读同一数据时不允许其他事务修改此数据。
- READ COMMITTED(读提交数据):允许事务读已提交的数据,但不要求“可重复读”。
- READ UNCOMMITTED(可以读未提交数据):允许事务读已提交或未提交的数据。SQL2 中允许的最低一致性级别。
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
数据库的完整性
数据库的完整性 是指数据的正确性、有效性、相容性,防止错误的数据进入数据库。所谓正确性指数据的合法性,如数值型不能放字母;有效性指数据是否属于定义的有效范围;相容性指表示同一事实的两个数据应相同。
- DBMS 的完整性子系统功能:
- 监督事务的执行,并测试是否违反完整性规则。
- 若有违反现象,则采取适当的操作,如拒绝操作、报告违反情况、改正错误等方法来处理。
- SQL 中的完整性约束,有下面三类:
- 域约束。 以 CREATE DOMAIN 语句定义新的域,可以以 CHECK 子句附加条件表达式。
- 基本表约束。 包括候选键定义、外键定义、检查约束定义。UNIQUE (<列名序列>) 实际上定义了候选键,但只表示唯一,值非空还需在列定义时有选项 NOT NULL。PRIMARY KEY (<列名序列>) 定义了表的主键,一个表只能有一个主键,且主键指定的列自动被认为是非空的。FOREIGN KEY (<列名序列>) REFERENCES <参照表> (<列名序列>) 定义了外键。CHECK (<条件表达式>) 是对单个关系中的元组值加以约束,如 CHECK AGE >= 15 AND AGE <= 30。
- 断言。如果完整性约束与多个关系有关,或者涉及聚合操作,就可以使用断言机制书写。创建断言:CREATE ASSERTION <断言名> CHECK (<条件>);删除断言:DROP ASSERTION <断言名>。如 CREATE ASSERTION ass1 CHECK (10 >= SELECT COUNT(C#) FROM C GROUP BY T#) 表示教师授课不得超过 10 门课。
SQL 的触发器
触发器(Trigger) 是一个能由系统自动执行对数据库修改的语句,由事件、条件和动作三部分组成。有时也称为主动规则或 事件 –> 条件 –> 动作规则(ECA 规则,Event Condition Action Rule)。
- 事件:指对数据库的增删改操作,这些事件发生时,触发器开始工作。
- 条件:触发器判断条件是否成立,如果成立,则执行相应的动作,否则什么也不做。
- 动作:DBMS 开始执行这些动作,如删除一些数据,插入一些元组,或一系列的数据库操作等。
-- 修改成绩时,修改后的成绩不能比原来低,否则就拒绝修改
CREATE TRIGGER TRIGGER1
AFTER UPDATE OF SCORE ON SC
REFERENCING
OLD AS OLDTUPLE
NEW AS NEWTUPLE
FOR EACH ROW
WHEN (OLDTUPLE.SCORE > NEWTUPLE.SCORE)
UPDATE SC SET SCORE = OLDTUPLE.SCORE WHERE C# = NEWTUPLE.C#
SQL 触发器的组成
- 动作时间(定义了何时执行触发器的动作)
- BEFORE(SQL 标准):在触发事件进行前,先测试 WHEN 条件是否满足。若满足,则先执行触发器动作部分的操作,再执行触发事件的操作。
- AFTER(SQL 标准):在触发事件完成后,测试 WHEN 条件是否满足。若满足,则再执行触发器动作部分的操作。
- INSTEAD OF(Oracle 中):触发事件发生时,只要满足 WHEN 条件,就立刻执行动作部分的操作,而触发事件的操作不再执行。
- 触发事件:UPDATE、DELETE、INSERT。只有 UPDATE 后可跟 OF <属性表> 短语指定某个属性,其他两种情况都是对整个元组的操作,不允许跟 OF <属性表> 短语。
- 目标表:ON 短语
- 旧值和新值的别名:如果触发事件是 UPDATE,则用“OLD AS”和“NEW AS”子句定义修改前后的元组变量;如果是 DELETE,只要用“OLD AS”;如果是 INSERT,只要用“NEW AS”。
- 触发动作:定义了当触发器被激活时想要它执行的 SQL 语句,有下面三个部分:
- 动作间隔尺寸:FOR EACH ROW(对每一个修改的元组都要检查一次,元组级触发器,行级触发器) 和 FOR EACH STATEMENT(对 SQL 语句的执行结果去检查,语句级触发器)。如果一条 SQL 语句需要修改 10 条记录,前者要运行 10 次,后者只运行 1次。
- 动作执行的条件:WHEN 子句,可以是任意表达式。
- 动作体:想要 DBMS 执行的 SQL 语句(仅当 WHEN 条件为 true 时)。
数据库的安全性
数据库的安全性(Security) 是指保护数据库,防止不合法的使用,以免数据泄密、更改或破坏。安全性常与完整性混淆。安全性是保护数据以防止非法用户故意造成的破坏,完整性是保护数据以防止合法用户无意中造成的破坏。
- 安全性级别
- 环境级:对计算机系统的机房和设备应加以保护,防止人为的物理破坏。
- 职员级:工作人员应清正廉洁,正确授予用户访问数据库的权限。
- OS 级:应防止未经授权的用户从 OS 处着手访问数据库。
- 网络级:大多数 DBS 都运行用户通过网络远程访问,因此,网络内部的安全很重要。
- DBS 级:检查用户的身份是否合法,以及使用数据库的权限是否正确。
- 权限问题
用户使用数据库的方式称为“权限”,有两种:访问数据、修改数据库模式。
- 访问数据的权限(4 个):读、增、删、改。
- 修改数据库结构(4 个):索引(增删)、资源(创建新的关系)、修改(在关系中增删属性)、撤销(撤销关系,DROP TABLE)。
- SQL 中的安全机制
- 视图机制(View):用来对无权用户屏蔽数据,使用户只能使用视图定义中的数据,从而保证了数据库安全性。
- 权限机制(Authorization):保证用户只能进行其权限范围内的操作。
- 角色机制(Role):对具有不同权限的用户进行分组。
- 审计机制(Audit):建立用于安全性目的的数据库日志,以检查某一时间段内所有作用于数据库的存取动作和操作。
-- 权限表: SELECT INSERT UPDATE DELETE REFERENCES USAGE
-- REFERENCES:允许用户定义新关系时,引用其他关系的主键作为外键
-- USAGE:允许用户使用已定义的域
-- WITH GRANT OPTION:表示获得权限的用户还能转授权限给其他用户
GRANT <权限表> ON <数据库元素> TO <用户名表> [WITH GRANT OPTION]
-- 例如
GRANT SELECT ON S TO ZHANGSAN WITH GRANT OPTION
GRANT SELECT, UPDATE ON S TO WANG
-- 如要授予权限表中全部六种权限,可用 ALL PRIVILEGES
GRANT ALL PRIVILEGES ON S TO WANG
-- 回收权限语句
-- CASCADE:表示连锁回收权限
-- RESTRICT:仅当没有连锁权限时才能回收该用户的权限
REVOKE <权限表> ON <数据库元素> FROM <用户名表> [ RESTRICT | CASCADE ]
- 常用的安全性措施
- 强制存取控制:对每个数据对象和用户各自赋予一定的级别,用户能查看比它级别低或同级的数据,但只能修改和它同级的数据。(这种方法只在专用数据库中有用)
- 统计数据库的安全性:在“统计数据库”中,通过对用户查询得到的记录数加以控制和“数据污染”两种方法来保证数据库的安全性。
- 自然环境的安全性:即 DBS 的设备和硬件的安全性。