第六章-数据库保护
1.数据库保护
graph TB
A(数据库保护)-->B(安全性)
A-->C(完整性)
A-->D(并发控制)
A-->F(恢复)
2.常用的数据库安全控制的方法和技术
- 用户标识与鉴别: 该方法由系统提供一定的方式让用户标识自己的名字或身份,每次用户要求进入系统时,由系统进行核对,通过鉴定后才提供系统的使用权;
- 存取控: 通过用户权限定义和合法权检查确保只有合法权限的用户访问数据库,所有未被授权的人员无法存取数据;
- 视图机制: 为不同的用户定义视图,通过视图机制把要保密的数据对无权存取的用户隐藏起来,从而自动地对数据提供一定程度的安全保护;
- 审计: 建立审计日志,把用户对数据库的所有操作自动记录下来放入审计日志中,DBA可以利用审计跟踪的信息,重现导致数据库现有状况的一系列事件,找出非法存取数据的人、时间和内容等;
- 数据加密: 对存储和传输的数据进行加密处理,从而使得不知道解密算法的人无法获知数据的内容;
3.数据库实现完整性检查的方法
要实现数据库完整性的检查, 系统要提供定义完整性约束条件的机制和提供检查是 否违背完整性约束条件的方法。当 DBMS发现用户的操作违背了完整性约束条件时,就采取 一定的控制。具体的控制是随系统而定的。
完整性检查和控制的防范对象不合语义的数据,不正确的数据,防止它们进入数据库;安全性控制的防范对象是非法用户,防止他们对数据库数据的存取。
4.数据库的安全性是指保护数据库以防止不合法的使用所造成的数据泄露、数据更改或数据破坏。
5.数据库中的自主存取控制方法和强制存取控制方法
- 自主存取控制方法: 定义各个用户对不同数据对象的存取权限,当用户对数据库访问时受首先检查用户的存取权限,防止非法用户对数据库的存取;
- 强制存取控制方法: 每一个数据对象被(强制地)标以一定的密级,每一个用户也被(强制地)授予某一个级别的许可证,系统规定只有具有某一许可证级别的用户才能存取某一个密级的数据对象;
6.综合题
今有两个关系模式:
职工(职工号,姓名,年龄,职务,工资,部门号)
部门(部门号,名称,经理名,地址,电话号)
请用SQL的GRANT和REVOKE语句(加上视图机制)完成以下授权定义或存取控制功能:
1) 用户王明对两个表有select权力;
grant select on 职工,部门 to 王明;
revoke select on 职工,部门 from 王明;
2) 用户李勇对两个表有insert和delete权力;
grant insert,delete on 职工,部门 to 李勇;
revoke insert,delete on 职工,部门 from 李勇;
3) 用户刘星对职工表有select权力,对工资字段具有更新权力;
grant select,update(工资) on 职工 to 刘星;
revoke select,update(工资) on 职工 from 刘星;
4) 用户张新具有修改这两个表的结构的权力;
grant alter table on 职工,部门 to 张新;
revoke alter table on 职工,部门 from 张新;
5) 用户周平具有对两个表所有权力(读,插,改,删),并具有给其他用户授权的权力;
grant all privileges on 职工,部门 to 周平 with grant option;
revoke all privileges on 职工,部门 from 周平;
6) 用户杨兰具有从每个部门职工中select最高工资,最低工资,平均工资的权力,但他不能查看每个人的工资;
答: 首先建立一个视图,然后对这个视图定义杨兰的存取权限。
create view 部门工资 as
select 部门.名称,max(工资),min(工资),avg(工资) from 职工,部门
where 职工.部门号 = 部门.部门号 group by 职工.部门号;
grant select on 部门工资 to 杨兰;
revoke select on 部门工资 from 杨兰;
drop view 部门工资;
7.数据库系统必须提供的数据控制功能
- (1)数据库恢复:在系统失效后的数据库恢复,配合定时备份数据库,使数据库不丢失数据。
- (2)并发控制:保证多用户能共享数据库,并维护数据的一致性。
- (3)安全性保护:防止对数据库的非法使用,以避免数据的泄露、纂改或破坏。 授权编译系统和合法性检查机制一起组成了++安全性++子系统。
- (4)完整性保护:保证数据的正确性和一致性/相容性。
8.事务
事务是DBMS的基本工作单位,它是用户定义的一组逻辑一致的程序序列。
1.原子性(Atomicity)
原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。
2.一致性(Consistency)
一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态;
举例来说,假设用户A和用户B两者的钱加起来一共是1000,那么不管A和B之间如何转账、转账几次,事务结束后两个用户的钱相加起来应该还是得是1000,这就是事务的一致性。
3.隔离性(Isolation)
一个事务内部的操作及使用的数据对并发的其他事务是隔离的
隔离性是指当多个用户并发访问数据库时,比如同时操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
4.持久性(Durability)
事务一旦提交,对数据库的改变是永久的
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
例如我们在使用JDBC操作数据库时,在提交事务方法之后,提示用户事务操作完成,当我们程序执行完成直到看到提示后,就可以认定事务已经正确提交,即便这时候数据库出现了问题,也必须要将我们的事务完全执行完成;否则,就会造成我们虽然看到提示事务处理完毕,但是数据库因为故障而没有执行事务的重大错误,这是不被允许的。
9.并发操作会带来的==数据不一致性==
(1)丢失修改
当两个或多个事务读入同一数据并修改,会发生丢失更新问题,即前一个事物更新的结果被后一事务所做更新覆盖,即当事务A和B同事进行时,事务A对数据已经改变但并未提交时B又对同一数据进行了修改(注意此时数据是A还未提交改变的数据),到时A做的数据改动丢失了。
T1 | T2 |
---|---|
读 A=10 | |
读 A=10 | |
A=A-5 写回 | |
A=A-8 写回 |
(2)不可重复读
当两个数据读取某个数据后,另一事务执行了对该数据的更新,当前一事务再次读取该数据(希望与第一次读取的是相同的值)时,得到的数据与前一次的不一样,这是由于第一次读取数据后,事务B对其做了修改,导致再次读取数据时与第一次读取的数据不相同。
T1 | T2 |
---|---|
读 A=10,B=5 | |
读 A=10 A=A*2 写回 | |
读 A=20,B=5 求和 25 验证错 |
(3)读‘脏数据’
当一个事务修改某个数据后,另一事务对该数据进行了读取,由于某种原因前一事务撤销了对该数据的修改,即将修改过的数据恢复原值,那么另一事务读到的数据与原数据可能不一致,称之为读脏数据。
T1 | T2 |
---|---|
读 A=100 A=A*2 写回 | |
读 A=200 | |
ROLLBACK 恢复 A=100 |
注意:还有一个叫“幽灵数据” 幽灵数据与脏数据类似,不过幽灵数据是指事务提交之后读到的数据,但是在读取之后又进行了对前一事务的恢复,而脏数据是指并未提交前读取的数据。
10.事务的四种隔离级别
数据库事务的隔离级别有4种,由低到高分别为Read uncommitted 、Read committed 、Repeatable read 、Serializable 。而且,在事务的并发操作中可能会出现脏读,不可重复读,幻读。下面通过事例一一阐述它们的概念与联系。
(1) Read uncommitted
读未提交,顾名思义,就是一个事务可以读取另一个未提交事务的数据。
事例:老板要给程序员发工资,程序员的工资是3.6万/月。但是发工资时老板不小心按错了数字,按成3.9万/月,该钱已经打到程序员的户口,但是事务还没有提交,就在这时,程序员去查看自己这个月的工资,发现比往常多了3千元,以为涨工资了非常高兴。但是老板及时发现了不对,马上回滚差点就提交了的事务,将数字改成3.6万再提交。
分析:实际程序员这个月的工资还是3.6万,但是程序员看到的是3.9万。他看到的是老板还没提交事务时的数据。这就是脏读。
那怎么解决脏读呢?Read committed!读提交,能解决脏读问题。
(2) Read committed
读提交,顾名思义,就是一个事务要等另一个事务提交后才能读取数据。
事例:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(程序员事务开启),收费系统事先检测到他的卡里有3.6万,就在这个时候!!程序员的妻子要把钱全部转出充当家用,并提交。当收费系统准备扣款时,再检测卡里的金额,发现已经没钱了(第二次检测金额当然要等待妻子转出金额事务提交完)。程序员就会很郁闷,明明卡里是有钱的…
分析:这就是读提交,若有事务对数据进行更新(UPDATE)操作时,读操作事务要等待这个更新操作事务提交后才能读取数据,可以解决脏读问题。但在这个事例中,出现了一个事务范围内两个相同的查询却返回了不同数据,这就是不可重复读。
那怎么解决可能的不可重复读问题?Repeatable read !
(3) Repeatable read
重复读,就是在开始读取数据(事务开启)时,不再允许修改操作
事例:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(事务开启,不允许其他事务的UPDATE修改操作),收费系统事先检测到他的卡里有3.6万。这个时候他的妻子不能转出金额了。接下来收费系统就可以扣款了。
分析:重复读可以解决不可重复读问题。写到这里,应该明白的一点就是,不可重复读对应的是修改,即UPDATE操作。但是可能还会有幻读问题。因为幻读问题对应的是插入INSERT操作,而不是UPDATE操作。
什么时候会出现幻读(phantom read)?
事例:程序员某一天去消费,花了2千元,然后他的妻子去查看他今天的消费记录(全表扫描FTS,妻子事务开启),看到确实是花了2千元,就在这个时候,程序员花了1万买了一部电脑,即新增INSERT了一条消费记录,并提交。当妻子打印程序员的消费记录清单时(妻子事务提交),发现花了1.2万元,似乎出现了幻觉,这就是幻读。
那怎么解决幻读问题?Serializable!
(4) Serializable 序列化
Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。
值得一提的是:大多数数据库默认的事务隔离级别是Read committed,比如Sql Server , Oracle。Mysql的默认隔离级别是Repeatable read。
不可重复度和幻读区别:
不可重复读主要是修改操作,幻读的主要在于新增或者删除。
幻读主要在于数据的条数变了,而不可重复读主要在于数据内容变了。
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(read uncommitted) | √ | √ | √ |
读已提交(READ-COMMITTED) | × | √ | √ |
可重复读(REPEATABLE-READ) | × | × | √ |
可串行化(SERIALIZABLE) | × | × | × |
11.并发控制机制的任务
- 对并发操作进行正确调度;
- 保证事务的隔离性;
- 保证数据库的一致性;
12.数据库中的封锁机制
解决并发操作带来的数据不一致性总是普遍采用封锁。同时,只有出现并发操作时,才有可能出现死锁。
(1) 封锁技术
封锁包含四种:共享锁(S锁)、排他锁(X锁)、更新锁(U锁)、意向锁
排它锁(Exclusive Locks,即X锁)
若事务T对数据对象A加上X锁,则只允许T读取和修改A,其他任何事务都不能再对A加任何类型的锁,直到T释放A上的锁。
这就保证了其他事务在T释放A上的锁之前不能再读取和修改A,因此,X锁又被称为写锁。
共享锁(Share Locks,即S锁)
若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加上S锁,而不能加X锁,直到T释放A上的S锁。
这就保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改,因此,S锁又被称为读锁。
(2) 封锁协议
在运用X锁和S锁对数据对象加锁时,需要约定一些规则,这些规则为封锁协议(Locking Protocol)。封锁协议分为一级封锁协议、二级封锁协议、三级封锁协议。
- 何时申请X锁或S锁;
- 持锁时间;
- 何时释放;
对封锁方式规定不同的规则,就形成了各种不同的封锁协议,在不同的程度上保证并发操作的正确调度。
一级封锁协议
事务T在修改数据R之前必须先对其加X锁,直到事务结束才释放;一级封锁协议可以防止丢失修改,并保证事务是可恢复的。
T1 | T2 |
---|---|
X lock A | |
R(A) = 16 | |
X lock A | |
A = A - 1 W(A) = 15 Commit Unlock A | 等待 |
获得X lock A R(A) = 15 A = A - 3 | |
W(A) = 12 Commit Unlock A |
在一级封锁协议中,如果仅仅是读数据不对其进行修改,是不需要加锁的,所以它不能保证可重复读和不读"脏"数据。
二级封锁协议
一级封锁协议加上事务T在读取数据R之前必须先对其加S锁,读完后方可释放S锁。
二级封锁协议除防止了丢失修改,还可以进一步防止读“脏”数据。但在二级封锁协议中,由于读完数据后即可释放S锁,所以它不能保证可重复读。
T1 | T2 |
---|---|
X lock C R(C) = 100 C = C * 2 W(C) = 200 | |
S lock C 等待 | |
Rollback (C恢复为100) Unlock C | 等待 |
获得S lock C R(C) = 100 Commit C Unlock C |
三级封锁协议
一级封锁协议加上事务T在读取数据R之前必须先对其加S锁,直到事务结束才释放。
三级封锁协议除防止了丢失修改和不读“脏”数据外,还进一步防止了不可重复读。
T1 | T2 |
---|---|
S lock A S lock B R(A) = 50 R(B) = 100 求和 = 150 | |
X lock B 等待 | |
R(A) = 50 R(B) = 100 求和 = 150 Commit Unlock A Unlock B | 等待 |
获得X lock B R(B) = 100 B = B * 2 W(B) = 200 Commit Unlock B |
13.数据库常见的四种故障
(1) 事务内部的故障
事务内部故障可分为预期的和非预期的,其中大部分的故障都是非预期的。预期的事务内部故障是指可以通过事务程序本身发现的事务内部故障; 非预期的事务内部故障是不能由事务程序处理的,如运算溢出故障、并发事务死锁故障、违反了某些完整性限制而导致的故障等。
(2) 系统故障
系统故障也称为软故障,是指数据库在运行过程中,由于硬件故障、数据库软件及操作系统的漏洞、突然停电灯情况,导致系统停止运转,所有正在运行的事务以非正常方式终止,需要系统重新启动的一类故障。这类事务不破坏数据库,但是影响正在运行的所有事务。
(3) 介质故障
介质故障也称为硬故障,主要指数据库在运行过程中,由于磁头碰撞、磁盘损坏、强磁干扰、天灾人祸等情况,使得数据库中的数据部分或全部丢失的一类故障。
(4) 计算机病毒故障
计算机病毒故障是一种恶意的计算机程序,它可以像病毒一样繁殖和传播,在对计算机系统造成破坏的同时也可能对数据库系统造成破坏(破坏方式以数据库文件为主) 。
14.数据库的恢复
数据库中为什么要有恢复子系统 ?它的功能是什么 ?
由于硬件的故障、 系统软件和应用软件的错误、 操作的失误以及恶意的破坏都是不 可避免的, 这些故障, 轻则会造成运行事务非正常中断, 影响数据库中数据的正确性, 重则 破坏数据库, 使数据库中的数据部分丢失或全部丢失。 为了保证各种故障发生后, 数据库中 的数据都能从错误状态恢复到某种逻辑一致状态, DBMS中的恢复子系统是必不可少的。
恢复子系统的功能就是利用冗余数据, 再根据故障的类型采取相应的恢复措施, 把数据库恢复到故障前的某一时刻的一致性状态。
15.数据库中死锁产生的原因和解决死锁的方法
死锁产生的原因
封锁可以引起死锁。比如事务 T1 封锁了数据 A,事务 T2 封锁了数据 B。T1 又申请封锁数据 B,但因 B 被 T2 封锁,所以 T1 只能等待。 T2 又申请封锁数据 A,但 A已被 T1 封锁, 所以也处于等待状态。这样, T1 和 T2 处于相互等待状态而均不能结束,这就形成了死锁。
解决死锁的常用方法有如下三种
(1) 要求每个事务一次就要将它所需要的数据全部加锁。
(2) 预先规定一个封锁顺序,所有的事务都要按这个顺序实行封锁。
(3) 允许死锁发生, 当死锁发生时, 系统就选择一个处理死锁代价小的事务, 将其撤消,释放此事务持有的所有的锁,使其他事务能继续运行下去。
16.什么是数据库的转储 ?转储的意义是什么 ?
对数据库制作后援副本的过程称为转储。 转储是由 DBA定期将数据库复制到另一个 存档用的存储器上。 转储的意义就在于, 当数据库遭到破坏时, 可将后援副本重新装入从而 把数据库恢复到某个一致性状态。
17.为什么要设立日志文件?
设立日志文件的目的,是为了记录对数据库中数据的每一次更新操作。从而 DBMS 可以根据日志文件进行事务故障的恢复和系统故障的恢复,并可结合后援副本进行介质故障的恢复。
18.怎样进行系统故障的恢复?
当系统故障发生时, 造成数据库处于不一致状态的原因有两个,一个是一些未完成事务对数据库的部分更新已写入数据库中,另一个是一些已提交事务对数据库的更新还留在内存缓冲区,没来得及写回数据库。因此,基本的恢复分为以下两步:
(1) 根据日志文件建立重做队列和撤消队列从头扫描日志文件, 找出故障前已提交的事务记录, 将其事务标识放入重做队列;还要找出故障前尚未完成的事务,将该事务放入撤消队列。
(2) 对重做队列中的事务进行重做处理,对撤消队列中的事务进行撤消处理 对于重做处理 (REDO),正向扫描日志文件, 再根据重做队列, 将已完成的事务的所有操作重新执行; 对于撤消处理 (UNDO),反向扫描日志文件, 再根据撤消队列, 将未完成的事务的更新操作执行逆操作。
19.怎样进行介质故障的恢复
在发生介质故障时,磁盘上的物理数据库被破坏,这时的恢复操作分为以下几步:
(1) 重装转储后援副本,使数据库恢复到转储时的一致状态;
(2) 从故障开始,反向阅读日志文件,找出已提交事务标记作重做队列;
(3) 从起始点开始正向阅读日志文件,根据重做队列的记录,重做所有已完成的事务,将数据库恢复至故障前某一时刻的一致状态。