在 DBS 运行时,DBMS 要对 DB 进行监控,以保证整个系统的正常运转,防止数据意外丢失和不一致数据的产生。DBMS 对 DB 的监控,称为 数据库管理。
主要通过四个方面实现:数据库的恢复、并发控制、完整性控制、安全性控制。每一方面构成了 DBMS 的一个子系统。
DBS 运行的最小逻辑工作单位是“事务”,所有的数据库操作都要以事务作为一个整体单位来执行或撤销。
事务(Transaction) 是构成单一逻辑工作单元的操作集合,要么完整的执行,要么完全不执行。不论发生何种情况,DBS 必须保证事务能正确、完整地执行。
DBS 执行事务,相当于操作系统环境中的“进程”概念,一个事务由 BEGIN TRANSACTION 语句开始,以 COMMIT 或 ROLLBACK 语句结束。
系统能把数据库从被破坏、不正确的状态,恢复到最近一个正确的状态,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 锁)。
如果事务 T 对某个数据项 R 实现了 X 锁,那么在 T 对 R 解除封锁之前,不允许其他事务再对该数据加任何类型的锁,这种锁称为 X 锁。
使用 X 锁的操作有两个:
采用 X 锁的并发控制,并发度低,只允许一个事务独锁数据,而其他申请封锁的事务只能排队等待。为此,降低要求,允许并发的读,就引入了共享型封锁(Shared Lock)。允许多个事务都可对数据项 R 加 S 锁,但在该数据项上所有的 S 锁都解除之前决不允许任何事务对该数据加 X 锁。
使用 S 锁的操作有三个:
S 锁可以解决丢失更新的问题,但带来了新的问题:死锁。如两个事务都申请了 S 锁,又都要升级为 X 锁去更新,谁也不解除,那就形成了死锁。
封锁的对象的大小称为 封锁的粒度。封锁对象可以很大,如整个数据库;也可以很小,如某个属性值。封锁力度与系统的并发度和并发控制密切相关,封锁的粒度越大,并发度就越低,但同时系统的开销也就越小。
运用封锁机制时,要约定一些规则,称为协议。下表为三级封锁协议,在不同程度上解决了并发操作带来的问题,为正确调度提供一定的保证。
事务的执行次序称为 事务的调度。如果多个事务依次执行,称为 事务的串行调度。如果利用分时的方法,同时处理多个事务,则称为 事务的并发调度。
如果有 n 个事务串行调度,则可能有 n! 种不同的有效调度。如果有 n 个事务并发调度,可能的并发调度数目远远大于 n!,但其中有的并发调度是正确的,有的是错误的。
每个事务中,语句的先后顺序在各种调度中始终保持一致。在这个前提下,如果一个并发调度的执行结果与某一串行调度的执行结果等价,那么,这个并发调度称为 可串行化的调度,否则是不可串行化的调度。
例如之前的例1,先 T1 后 T2 或者 先 T2 后 T1,A 的结果为 140 或 170,这两种串行调度都认为是正确的。而当 A 的执行结果为 200 时,与任何一个串行调度的结果都不相同,因而这种并发调度是错误的,即称为不可串行化的调度。
SQL2 对事务的存取模式(Access Mode)和隔离级别(Isolation Level)做了具体规定,并提供语句让用户使用,以控制事务的并发执行。
SET TRANSACTION READ ONLY
SET TRANSACTION READ WRITE
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
数据库的完整性 是指数据的正确性、有效性、相容性,防止错误的数据进入数据库。所谓正确性指数据的合法性,如数值型不能放字母;有效性指数据是否属于定义的有效范围;相容性指表示同一事实的两个数据应相同。
触发器(Trigger) 是一个能由系统自动执行对数据库修改的语句,由事件、条件和动作三部分组成。有时也称为主动规则或 事件 –> 条件 –> 动作规则(ECA 规则,Event Condition Action Rule)。
-- 修改成绩时,修改后的成绩不能比原来低,否则就拒绝修改
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#
数据库的安全性(Security) 是指保护数据库,防止不合法的使用,以免数据泄密、更改或破坏。安全性常与完整性混淆。安全性是保护数据以防止非法用户故意造成的破坏,完整性是保护数据以防止合法用户无意中造成的破坏。
用户使用数据库的方式称为“权限”,有两种:访问数据、修改数据库模式。
-- 权限表: 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 ]