📖 前言:事务是数据库操作的基本逻辑单元,事务处理技术主要包括数据库并发控制技术和恢复技术。本章首先介绍了事务的基本概念和四个特性,然后讨论事务并发操作可能引起数据库的不一致性,继而引入数据库的并发控制技术。在并发控制技术中,介绍了并发调度的可串行化概念和封锁协议,提及了封锁可能带来的死锁问题、死锁检测和解决办法,最后对数据库可能出现的故障种类,备份策略和恢复技术进行分析。
目录
- 🕒 0. 思维导图
- 🕒 1. 事务
- 🕘 1.1 事务(Transaction)的基本概念
- 🕘 1.2 事务的ACID特性
- 🕒 2. 并发控制技术
- 🕘 2.1 并发调动引发的问题
- 🕤 2.1.1 丢失更新(Lost Update)
- 🕤 2.1.2 读“脏”数据(Dirty Read)
- 🕤 2.1.3 不可重复读(Non-repeatable Read)
- 🕘 2.2 调度的可串行性
- 🕘 2.3 封锁
- 🕘 2.4 封锁协议
- 🕤 2.4.1 一级封锁协议
- 🕤 2.4.2 二级封锁协议
- 🕤 2.4.3 三级封锁协议
- 🕘 2.5 两段锁协议
- 🕘 2.6 活锁和死锁
- 🕤 2.6.1 相关概念
- 🕤 2.6.2 死锁预防
- 🕤 2.6.3 死锁的检测
- 🕤 2.6.4 死锁的处理
- 🕒 3. 数据库的恢复技术
- 🕘 3.1 数据库故障分类
- 🕤 3.1.1 事务内部的故障
- 🕤 3.1.2 系统故障
- 🕤 3.1.3 介质故障
- 🕤 3.1.4 计算机病毒
- 🕘 3.2 数据库恢复的主要技术
- 🕤 3.2.1 数据转储技术
- 🕤 3.2.2 登记日志文件
- 🕘 3.3 数据库的恢复策略
- 🕤 3.3.1 事务故障的恢复
- 🕤 3.3.2 系统故障的恢复
- 🕤 3.3.3 介质故障的恢复
- 🕘 3.4 SQL Server 2014的数据库备份和恢复
- 🕤 3.4.1 完全备份
- 🕤 3.4.2 差异备份
- 🕤 3.4.3 事务日志备份
- 🕘 3.5 数据库的复制
- 🕤 3.5.1 快照复制
- 🕤 3.5.2 事务复制
- 🕤 3.5.3 合并复制
- 🕒 4. 课后习题
🕒 0. 思维导图
🕒 1. 事务
🕘 1.1 事务(Transaction)的基本概念
事务定义
- 一个数据库操作序列
- 一个不可分割的工作单位,即操作要么全做,要么全不做
- 恢复和并发控制的基本单位
事务和程序是两个概念
- 在关系数据库中,一个事务可以是一条SQL语句,一组SQL语句或整个程序
- 一个应用程序通常包含多个事务
在SQL语言中,定义事务的语句有三条:
BEGIN TRANSACTION
显式定义:
BEGIN TRANSACTION...(SQL语句)...COMMIT
or
BEGIN TRANSACTION...(SQL语句)...ROLLBACK # 回滚到事务开始的状态
其中值得一提的是,在Oracle中执行完语句不写COMMIT
,退出后是不会保存的,就像你写完一个Word却没点保存,而SSMS不存在此情况。
隐式定义:
当用户没有显式地定义事务时,DBMS按缺省规定自动划分事务。如update
也可以是一个事务。
COMMIT
- 事务正常结束
- 提交事务的所有操作(读+更新)
- 事务中所有对数据库的更新永久生效
ROLLBACK
- 事务异常终止
- 事务运行的过程中发生了故障,不能继续执行
- 回滚事务的所有更新操作
- 事务滚回到开始时的状态
- 事务异常终止
🕘 1.2 事务的ACID特性
为了保证数据库的完整性(一致性),数据库管理系统必须维护事务的以下特性:ACID
- 原子性(Atomicity):事务中的所有操作要么全部执行,要么都不执行。
- 一致性(Consistency):如果在执行事务之前数据库是一致的,那么在执行事务之后数据库也还应该是一致的。
- 持久性(Durability):事务成功执行后它对数据库的修改是永久的,即使系统出现故障。
- 隔离性(Isolation):并发的事务不会受其他事务影响
🕒 2. 并发控制技术
问题的产生:
- 多用户数据库系统的存在
允许多个用户同时使用的数据库系统- 航班订票数据库系统
- 核酸码数据库系统
特点:在同一时刻并发运行的事务数可达数百个
不同的多事务执行方式:
- 事务串行执行
- 每个时刻只有一个事务运行,其他事务必须等到这个事务结束以后方能运行
- 不能充分利用系统资源,发挥数据库共享资源的特点
- 交叉并发方式(Interleaved Concurrency)☆
- 在单处理机系统中,事务的并行执行是这些并行事务的并行操作轮流交叉运行
- 单处理机系统中的并行事务并没有真正地并行运行,但能够减少处理机的空闲时间,提高系统的效率
- 同时并发方式(simultaneous concurrency)
- 多处理机系统中,每个处理机可以运行一个事务,多个处理机可以同时运行多个事务,实现多个事务真正的并行运行
事务并发执行带来的问题:
- 会产生多个事务同时存取同一数据的情况
- 可能会存取和存储不正确的数据,破坏事务一致性和数据库的一致性
🕘 2.1 并发调动引发的问题
并发操作带来的数据不一致性
记号: R(x)表示读数据x;W(x)表示写数据x
🕤 2.1.1 丢失更新(Lost Update)
例子:
(1)甲事务T1读取存款余额R=5000元;
(2)乙事务T2想在网上购物,读取存款余额5000元;
(3)甲事务T1由于需要取走1000元,则系统修改存款余额R=R-1000=5000-1000=4000,并将存款余额4000元写回数据库;
(4)乙事务网上购物转帐支取300元,则系统修改存款余额R= R-300=5000-300=4700,并将存款余额4700元写回数据库。
两个事务T1和T2读入同一数据并修改,T2的提交结果破坏了T1提交的结果,导致T1的更新被丢失。
T 1 T 2 ① Read: R = 5000 ② Read: R = 5000 ③ R = R − 1000 Write: R = 4000 ④ R = R − 300 Write: R = 4700 \begin{array}{|l|l|} \hline {\mathbf{T1}} & \mathbf{T2} \\ \hline \text { ① } \text {Read: } \mathbf{R}=\mathbf{5 0 0 0} \\ \text { ② } & \text {Read: } \mathbf{R}=\mathbf{5 0 0 0} \\ \text { ③ } \mathbf{R}=\mathbf{R}-\mathbf{1 0 0 0} \\ \text { Write: } \mathbf{R}=\mathbf{4 0 0 0} \\ \text { ④ } & \mathbf{R}=\mathbf{R}-300 \\ & \text {Write: } \mathbf{R}=\mathbf{4700} \\ \hline \end{array} T1 ① Read: R=5000 ② ③ R=R−1000 Write: R=4000 ④ T2Read: R=5000R=R−300Write: R=4700
🕤 2.1.2 读“脏”数据(Dirty Read)
例子:
(1)甲事务T1读取存款余额R=5000元;
(2)甲事务T1由于需要取走1000元,则系统修改存款余额R=R-1000=5000-1000=4000,并将存款余额4000元写回数据库,此时取款的事务还未提交;
(3)乙事务T2由于某种需要,读取存款余额为4000元;
(4)因为某种原因,甲事务的操作要撤销,此时对甲事务执行ROLLBACK操作,该帐户余额恢复为R=5000元。
读“脏”数据是指:
- 事务T1修改某一数据,并将其写回磁盘
- 事务T2读取同一数据后,T1由于某种原因被撤销
- 这时T1已修改过的数据恢复原值,T2读到的数据就与数据库中的数据不一致
- T2读到的数据就为“脏”数据,即不正确的数据
T 1 T 2 ① Read: R = 5000 ② R = R − 1000 Write: R = 4000 ③ Read: R = 4000 ④ ROLLBACK R恢复为5000 \begin{array}{|l|l|} \hline {\mathbf{T1}} & \mathbf{T2} \\ \hline \text { ① } \text {Read: } \mathbf{R}=\mathbf{5 0 0 0} \\ \text { ② } \mathbf{R}=\mathbf{R}-\mathbf{1 0 0 0} \\ \text { Write: } \mathbf{R}=\mathbf{4 0 0 0} \\ \text { ③ } & \text {Read: } \mathbf{R}=\mathbf{4 0 0 0} \\ \text { ④ } \text {ROLLBACK} \\ \text { R恢复为5000 } \\ \hline \end{array} T1 ① Read: R=5000 ② R=R−1000 Write: R=4000 ③ ④ ROLLBACK R恢复为5000 T2Read: R=4000
- T1将R值修改为4000,T2读到R为4000
- T1由于某种原因撤销,其修改作废,R恢复原值5000
- 这时T2读到的R为4000,与数据库内容不一致,就是“脏”数据
🕤 2.1.3 不可重复读(Non-repeatable Read)
例子:
(1)甲事务T1读取存款余额R=5000元;
(2)乙事务T2想在网上购物,读取存款余额5000元;
(3)乙事务网上购物转帐支取300元,则系统修改存款余额R= R-300=5000-300=4700,并将存款余额4700元写回数据库。
(4)甲事务T1再次读取帐户余额进行验证时发现前后两次读取值发生了变化,无法读取前一次读取的结果。
- 不可重复读是指事务T1读取数据后,事务T2执行更新操作,使T1无法再现前一次读取结果。
不可重复读包括三种情况:
① 事务T1读取某一数据后,事务T2对其做了修改,当事务T1再次读该数据时,得到与前一次不同的值
T 1 T 2 ① Read: R = 5000 ② Read: R = 5000 ③ R = R − 300 Write: R = 4700 ④ Read: R = 4700 \begin{array}{|l|l|} \hline {\mathbf{T1}} & \mathbf{T2} \\ \hline \text { ① } \text {Read: } \mathbf{R}=\mathbf{5 0 0 0} \\ \text { ② } & \text {Read: } \mathbf{R}=\mathbf{5 0 0 0} \\ \text { ③ } & \mathbf{R}=\mathbf{R}-\mathbf{3 0 0} \\ & \text { Write: } \mathbf{R}=\mathbf{4 7 0 0} \\ \text { ④ } \text {Read: } \mathbf{R}=\mathbf{4 7 0 0} \\ \hline \end{array} T1 ① Read: R=5000 ② ③ ④ Read: R=4700T2Read: R=5000R=R−300 Write: R=4700
- T1读取R=5000进行运算
- T2读取同一数据R,对其进行修改后将R=4700写回数据库。
- T1为了对读取值校对重读R,R已为4700,与第一次读取值不一致
② 当事务T1按照一定条件从数据库中读取某些纪录后,事务T2删除了其中的某些纪录,结果当事务T1再次按照同样条件读取该数据时,发现某些纪录已经不存在了;
③当事务T1按照一定条件从数据库中读取某些纪录后,事务T2插入了一些纪录,结果当事务T1再次按照同样条件读取该数据时,发现多出了某些数据。
分析:
- 从事务的ACID性质考虑,产生上述三个问题的原因在于并发操作破坏了事务的隔离性。
- 并发控制的任务就是要用正确的调度方式控制并发的事务正确地执行,使多个事务互不干扰,以避免造成数据库中的数据不一致。
- 当多个事务并发时,当且仅当其结果与按某一次序串行地执行这些事务时的结果相同,称这种调度策略为可串行化的调度。
🕘 2.2 调度的可串行性
可串行化(Serializable)调度
- 多个事务的并发执行是正确的,当且仅当其结果与按某一次序串行地执行这些事务时的结果相同
可串行性(Serializability)
- 是并发事务正确调度的准则
- 一个给定的并发调度,当且仅当它是可串行化的,才认为是正确调度
例1:有两个事务T1和T2,分别包含如下操作:
T
1
:
R
e
a
d
(
A
)
T
2
:
R
e
a
d
(
B
)
T1:Read(A)~~~~~~~~~~~~T2:Read(B)
T1:Read(A) T2:Read(B)
A
:
=
A
−
2
B
:
=
B
−
2
~~~~~~~~~A:=A-2~~~~~~~~~~~~~~~~~~B:=B-2
A:=A−2 B:=B−2
W
r
i
t
e
(
A
)
W
r
i
t
e
(
B
)
~~~~~~~~~Write(A)~~~~~~~~~~~~~~~~~~Write(B)
Write(A) Write(B)
R
e
a
d
(
B
)
~~~~~~~~~Read(B)
Read(B)
B
:
=
B
+
2
~~~~~~~~~B:=B+2
B:=B+2
W
r
i
t
e
(
B
)
~~~~~~~~~Write(B)
Write(B)
现给出对这两个事务不同的调度策略
串行调度:
T
1
T
2
Read (A)
A:=A-2
Write (A)
Read (B)
B:=B+2
Write(B)
Read (B)
B:=B-2
Write (B)
\begin{array}{|l|l|} {T_{1}} & T_{2} \\ \hline \text { Read (A) } & \\ \text { A:=A-2 } & \\ \text { Write (A) } & \\ \text { Read (B) } & \\ \text { B:=B+2 } & \\ \text { Write(B) } \\ & \text { Read (B) } \\ & \text { B:=B-2 } \\ & \text { Write (B) } \end{array}
T1 Read (A) A:=A-2 Write (A) Read (B) B:=B+2 Write(B) T2 Read (B) B:=B-2 Write (B)
- 假设A、B的初值均为8。
- 按T1→T2次序执行结果为A=6,B=8
- 串行调度策略,正确的调度
T 1 T 2 Read (B) B:=B-2 Write (B) Read (A) A:=A-2 Write (A) Read (B) B:=B+2 Write(B) \begin{array}{|l|l|} {T_{1}} & T_{2} \\ \hline & \text { Read (B) } \\ & \text { B:=B-2 } \\ & \text { Write (B) } \\ \text { Read (A) } & \\ \text { A:=A-2 } & \\ \text { Write (A) } & \\ \text { Read (B) } & \\ \text { B:=B+2 } & \\ \text { Write(B) } \\ \end{array} T1 Read (A) A:=A-2 Write (A) Read (B) B:=B+2 Write(B) T2 Read (B) B:=B-2 Write (B)
- 假设A、B的初值均为8。
- T2→T1次序执行结果为A=6,B=8
- 串行调度策略,正确的调度
可串行化调度:
T
1
T
2
Read (A)
Read (B)
A:=A-2
B:=B-2
Write(A)
Write (B)
Read (B)
B:=B+2
Write(B)
\begin{array}{|l|l|} {T_{1}} & T_{2} \\ \hline \text { Read (A) } & \\ & \text { Read (B) } \\ \text { A:=A-2 } & \\ & \text { B:=B-2 } \\ \text { Write(A)} & \\ & \text { Write (B) } \\ \text { Read (B) } & \\ \text { B:=B+2 } & \\ \text { Write(B) } \\ \end{array}
T1 Read (A) A:=A-2 Write(A) Read (B) B:=B+2 Write(B) T2 Read (B) B:=B-2 Write (B)
- 执行结果为: A=6,B=8,与上述两个串行调度的执行结果相同
- 是正确的调度
不可串行化调度:
T
1
T
2
Read (A)
A:=A-2
Read (B)
Write(A)
B:=B-2
Read (B)
Write (B)
B:=B+2
Write(B)
\begin{array}{|l|l|} {T_{1}} & T_{2} \\ \hline \text { Read (A) } \\ \text { A:=A-2 } & \text { Read (B) } \\ \text { }\\ \text { Write(A)} & \\ & \text { B:=B-2 } \\ \text { Read (B) } & \\ & \text { Write (B) } \\ \text { B:=B+2 } & \\ \text { Write(B) } \\ \end{array}
T1 Read (A) A:=A-2 Write(A) Read (B) B:=B+2 Write(B) T2 Read (B) B:=B-2 Write (B)
- 执行结果为:A=6,B=10,与(a)、(b)的结果都不同
- 是错误的调度,丢失更新
为了保证调度的可串行性,采用技术
- 封锁(Locking)方法
- 时间戳(Timestamp)
- 乐观控制法
商用的DBMS一般都采用封锁方法
🕘 2.3 封锁
- 封锁就是事务T在对某个数据对象(例如表、记录等)操作之前,先向系统发出请求,对其加锁
- 加锁后事务T就对该数据对象有了一定的控制,在事务T释放它的锁之前,其它的事务不能更新此数据对象。
- 一个事务对某个数据对象加锁后究竟拥有什么样的控制由封锁的类型决定。
- 基本封锁类型
-
排它锁(Exclusive Locks,简记为X锁)
- 排它锁又称为写锁
- 若事务T对数据对象A加上X锁,则只允许T读取和修改A,其它任何事务都不能再对A加任何类型的锁,直到T释放A上的锁
- 保证其他事务在T释放A上的锁之前不能再读取和修改A
-
共享锁(Share Locks,简记为S锁)
- 共享锁又称为读锁
- 若事务T对数据对象A加上S锁,则其它事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁
- 保证其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改
-
T 2 X S − T 1 X N N Y S N Y Y − Y Y Y \begin{array}{|l|l|l|l|} {~~~~~~}T_{2} & X & S & -\\ {T_{1}} & & &\\ \hline X & N & N & Y \\ \hline S & N & Y & Y \\ \hline - & Y & Y & Y \\ \end{array} T2T1XS−XNNYSNYY−YYY
Y=Yes,相容的请求
N=No,不相容的请求
在锁的相容矩阵中:
- 最左边一列表示事务T1已经获得的数据对象上的锁的类型,其中横线表示没有加锁。
- 最上面一行表示另一事务T2对同一数据对象发出的封锁请求。
- T2的封锁请求能否被满足用矩阵中的Y和N表示
- Y表示事务T2的封锁要求与T1已持有的锁相容,封锁请求可以满足
- N表示T2的封锁请求与T1已持有的锁冲突,T2的请求被拒绝
如何利用封锁机制解决不可串行化调度?
T
1
T
2
Xlock A
Read (A)
A:=A-2
Xlock B
Read (B)
Write(A)
Unlock A
B:=B-2
Xlock B
等待
Write (B)
等待
Unlock B
Read (B)
B:=B+2
Write(B)
Unlock B
\begin{array}{|l|l|} {T_{1}} & T_{2} \\ \hline \text { Xlock A }\\ \text { Read (A) } \\ \text { A:=A-2 } \\ & \text { Xlock B }\\ & \text { Read (B) } \\ \text { Write(A)} & \\ \text { Unlock A }\\ & \text { B:=B-2 } \\ \text { Xlock B }\\ \text { 等待 } & \text { Write (B) } \\ \text { 等待 } & \text { Unlock B }\\ \text { Read (B) } & \\ \text { B:=B+2 } & \\ \text { Write(B) } \\ \text { Unlock B }\\ \end{array}
T1 Xlock A Read (A) A:=A-2 Write(A) Unlock A Xlock B 等待 等待 Read (B) B:=B+2 Write(B) Unlock B T2 Xlock B Read (B) B:=B-2 Write (B) Unlock B
🕘 2.4 封锁协议
- 在运用封锁方法,对数据对象加锁时,需要约定一些规则,如何时申请加锁、申请锁的类型、持锁时间、何时释放封锁等,我们称这些规则为封锁协议(Locking Protocol)。
- 对封锁方式规定不同的规则,就形成了各种不同的封锁协议,三级封锁协议分别在不同程度上解决了数据不一致性问题。
🕤 2.4.1 一级封锁协议
- 一级封锁协议是:事务T在修改数据A之前,必须先对其加X锁,直到事务结束才释放。事务结束包括正常结束(COMMIT)和非正常结束(ROLLBACK)。
- 一级封锁协议可防止“丢失修改”所产生的数据不一致性的问题,并保证事务T是可恢复的。
前面提到的“丢失更新”问题:
T
1
T
2
① Read:
R
=
5000
②
Read:
R
=
5000
③
R
=
R
−
1000
Write:
R
=
4000
④
R
=
R
−
300
Write:
R
=
4700
\begin{array}{|l|l|} \hline {\mathbf{T1}} & \mathbf{T2} \\ \hline \text { ① } \text {Read: } \mathbf{R}=\mathbf{5 0 0 0} \\ \text { ② } & \text {Read: } \mathbf{R}=\mathbf{5 0 0 0} \\ \text { ③ } \mathbf{R}=\mathbf{R}-\mathbf{1 0 0 0} \\ \text { Write: } \mathbf{R}=\mathbf{4 0 0 0} \\ \text { ④ } & \mathbf{R}=\mathbf{R}-300 \\ & \text {Write: } \mathbf{R}=\mathbf{4700} \\ \hline \end{array}
T1 ① Read: R=5000 ② ③ R=R−1000 Write: R=4000 ④ T2Read: R=5000R=R−300Write: R=4700
解决方案:
T
1
T
2
① Xlock R
Read:
R
=
5000
②
Xlock R
③
R
=
R
−
1000
等待
Write:
R
=
4000
等待
Unlock R
等待
④
获得Xlock R
Read:
R
=
4000
R
=
R
−
300
Write:
R
=
3700
Unlock R
\begin{array}{|l|l|} \hline {\mathbf{T1}} & \mathbf{T2} \\ \hline \text { ① } \text {Xlock R }\\ \text {Read: } \mathbf{R}=\mathbf{5 0 0 0} \\ \text { ② } & \text {Xlock R }\\ \text { ③ } \mathbf{R}=\mathbf{R}-\mathbf{1 0 0 0} & \text{等待 }\\ \text { Write: } \mathbf{R}=\mathbf{4 0 0 0} & \text{等待 }\\ \text { Unlock R } & \text{等待 }\\ \text { ④ } & \text{获得Xlock R } \\ & \text {Read: } \mathbf{R}=\mathbf{4 0 0 0} \\ & \mathbf{R}=\mathbf{R}-300 \\ & \text {Write: } \mathbf{R}=\mathbf{3700} \\ & \text {Unlock R } \\ \hline \end{array}
T1 ① Xlock R Read: R=5000 ② ③ R=R−1000 Write: R=4000 Unlock R ④ T2Xlock R 等待 等待 等待 获得Xlock R Read: R=4000R=R−300Write: R=3700Unlock R
在一级封锁协议中,如果仅仅是读数据而不对其进行修改,是不需要加锁的,所以它不能保证可重复读和不读“脏”数据。
🕤 2.4.2 二级封锁协议
- 二级封锁协议在一级封锁协议的基础上,加上了事务T在读取数据R之前必须先对其加S锁,读完后即可释放S锁。
- 二级封锁协议除防止了丢失修改,还可进一步防止读“脏”数据。
前面提到的“脏数据”问题:
T
1
T
2
① Read:
R
=
5000
②
R
=
R
−
1000
Write:
R
=
4000
③
Read:
R
=
4000
④ ROLLBACK
R恢复为5000
\begin{array}{|l|l|} \hline {\mathbf{T1}} & \mathbf{T2} \\ \hline \text { ① } \text {Read: } \mathbf{R}=\mathbf{5 0 0 0} \\ \text { ② } \mathbf{R}=\mathbf{R}-\mathbf{1 0 0 0} \\ \text { Write: } \mathbf{R}=\mathbf{4 0 0 0} \\ \text { ③ } & \text {Read: } \mathbf{R}=\mathbf{4 0 0 0} \\ \text { ④ } \text {ROLLBACK} \\ \text { R恢复为5000 } \\ \hline \end{array}
T1 ① Read: R=5000 ② R=R−1000 Write: R=4000 ③ ④ ROLLBACK R恢复为5000 T2Read: R=4000
解决方案:
T
1
T
2
① Xlock R
Read:
R
=
5000
②
R
=
R
−
1000
Write:
R
=
4000
③
Slock R
④ ROLLBACK
等待
R恢复为5000
等待
Unlock R
等待
⑤
获得Slock R
Read:
R
=
5000
Unlock R
\begin{array}{|l|l|} \hline {\mathbf{T1}} & \mathbf{T2} \\ \hline \text { ① } \text {Xlock R }\\ \text { Read: } \mathbf{R}=\mathbf{5 0 0 0} \\ \text { ② } \mathbf{R}=\mathbf{R}-\mathbf{1 0 0 0} \\ \text { Write: } \mathbf{R}=\mathbf{4 0 0 0} \\ \text { ③ } & \text {Slock R }\\ \text { ④ } \text {ROLLBACK} & \text{等待 }\\ \text { R恢复为5000 } & \text{等待 }\\ \text { Unlock R }& \text{等待 } \\ \text { ⑤ } & \text {获得Slock R } \\ & \text { Read: } \mathbf{R}=\mathbf{5 0 0 0} \\ & \text { Unlock R } \end{array}
T1 ① Xlock R Read: R=5000 ② R=R−1000 Write: R=4000 ③ ④ ROLLBACK R恢复为5000 Unlock R ⑤ T2Slock R 等待 等待 等待 获得Slock R Read: R=5000 Unlock R
在二级封锁协议中,对数据进行读写操作前加X锁,防止了丢失修改的问题,对数据进行读操作前加S锁,防止了读“脏数据”的问题,但由于读完数据后即可释放S锁,所以它不能保证可重复读。
🕤 2.4.3 三级封锁协议
- 三级封锁协议是:在一级封锁协议的基础上,加上事务T在读取数据R之前必须先对其加S锁,直到事务结束才释放。
- 三级封锁协议可进一步防止不可重复读的问题。
前面提到的“不可重复读”问题:
T
1
T
2
① Read:
R
=
5000
②
Read:
R
=
5000
③
R
=
R
−
300
Write:
R
=
4700
④ Read:
R
=
4700
\begin{array}{|l|l|} \hline {\mathbf{T1}} & \mathbf{T2} \\ \hline \text { ① } \text {Read: } \mathbf{R}=\mathbf{5 0 0 0} \\ \text { ② } & \text {Read: } \mathbf{R}=\mathbf{5 0 0 0} \\ \text { ③ } & \mathbf{R}=\mathbf{R}-\mathbf{3 0 0} \\ & \text {Write: } \mathbf{R}=\mathbf{4 7 0 0} \\ \text { ④ } \text {Read: } \mathbf{R}=\mathbf{4 7 0 0} \\ \hline \end{array}
T1 ① Read: R=5000 ② ③ ④ Read: R=4700T2Read: R=5000R=R−300Write: R=4700
解决方案:
T
1
T
2
① Slock R
Read:
R
=
5000
②
Slock R
③
等待
④ Read:
R
=
5000
等待
Unlock R
获得Xlock R
⑤
Read:
R
=
5000
R
=
R
−
300
Write:
R
=
4700
\begin{array}{|l|l|} \hline {\mathbf{T1}} & \mathbf{T2} \\ \hline \text { ① } \text {Slock R }\\ \text {Read: } \mathbf{R}=\mathbf{5 0 0 0} \\ \text { ② } & \text {Slock R }\\ \text { ③ } & \text {等待 }\\ \text { ④ } \text {Read: } \mathbf{R}=\mathbf{5 0 0 0} & \text {等待 }\\ \text {Unlock R } & \text {获得Xlock R } \\ \text { ⑤ } & \text {Read: } \mathbf{R}=\mathbf{5 0 0 0} \\ & \mathbf{R}=\mathbf{R}-\mathbf{3 0 0} \\ & \text {Write: } \mathbf{R}=\mathbf{4 7 0 0} \\ \hline \end{array}
T1 ① Slock R Read: R=5000 ② ③ ④ Read: R=5000Unlock R ⑤ T2Slock R 等待 等待 获得Xlock R Read: R=5000R=R−300Write: R=4700
X
锁
S
锁
一
致
性
保
证
\hspace{5.2cm}{\color{blue}X~~锁} \hspace{4.9cm}{\color{green}S~~锁} \hspace{4.8cm}{\color{red}一~致~性~保~证}
X 锁S 锁一 致 性 保 证
操
作
结
束
释
放
事
务
结
束
释
放
操
作
结
束
释
放
事
务
结
束
释
放
不
丢
失
读
改
不
读
“
脏
”
数
据
可
重
复
性
一级封锁协议
✓
✓
二级封锁协议
✓
✓
✓
✓
三级封锁协议
✓
✓
✓
✓
✓
\begin{array}{|c|c|c|c|c|c|c|c|} \hline & \begin{array}{c} {\color{blue}操作结束释放} \end{array} & \begin{array}{c} {\color{blue}事务结束释放} \end{array} & \begin{array}{c} {\color{green}操作结束释放} \end{array} & \begin{array}{c} {\color{green}事务结束释放} \end{array} & \begin{array}{c} {\color{red}不丢失读改} \end{array} & \begin{array}{c} {\color{red}不读“脏”数据} \end{array} & {\color{red}可重复性} \\ \hline \begin{array}{c} \text {一级封锁协议} \end{array} & & \checkmark & & & \checkmark & & \\ \hline \begin{array}{c} \text {二级封锁协议} \end{array} & & \checkmark & \checkmark & & \checkmark & \checkmark & \\ \hline \begin{array}{c} \text {三级封锁协议} \end{array} & & \checkmark & & \checkmark & \checkmark & \checkmark & \checkmark \\ \hline \end{array}
一级封锁协议二级封锁协议三级封锁协议操作结束释放事务结束释放✓✓✓操作结束释放✓事务结束释放✓不丢失读改✓✓✓不读“脏”数据✓✓可重复性✓
🕘 2.5 两段锁协议
- 三级封锁协议可以防止并发执行中出现的三类问题,但是,并不能保证并发执行一定是可串行化的。
- 两段封锁协议(Two-Phase Locking,简称2PL)是最常用的一种封锁协议,理论上证明使用两段封锁协议产生的是可串行化调度。
两段锁协议是将所有事务的加锁和解锁分成两个阶段:
(1)获得封锁,也称为扩展阶段,在该阶段,事务可以获得任何数据项上的任何类型的锁;
(2)释放封锁,也称为收缩阶段,在此阶段,事务可以释放任何数据项上的任何类型的锁,但一旦释放一个锁之后,事务不能再获得任何其他加锁。
例
两个事务T1,T2:
T1:lock(A) lock(B) lock(C) unlock(B) unlock(C) unlock(A)
T2:lock(A) unlock(A) lock(B) lock(C) unlock(C) unlock(B)
T1遵守两段锁协议,T2不遵守两段锁协议。
遵守两段锁协议的可串行化调度
例2:有两个事务T1和T2,所包含的操作如表所示。
T1
T2
Slock B
Slock A
Read (B)
Read (A)
Unlock B
Unlock A
Xlock A
Xlock B
Read (A)
Read (B)
A: =A+B
B: =A+B
Write (A)
Write (B)
Ulock A
Ulock B
\begin{array}{|c|c|} \hline \text { T1 } & \text { T2 } \\ \hline \text { Slock B } & \text { Slock A } \\ \hline \text { Read (B) } & \text { Read (A) } \\ \hline \text { Unlock B } & \text { Unlock A } \\ \hline \text { Xlock A } & \text { Xlock B } \\ \hline \text { Read (A) } & \text { Read (B) } \\ \hline \text { A: =A+B } & \text { B: =A+B } \\ \hline \text { Write (A) } & \text { Write (B) } \\ \hline \text { Ulock A } & \text { Ulock B } \\ \hline \end{array}
T1 Slock B Read (B) Unlock B Xlock A Read (A) A: =A+B Write (A) Ulock A T2 Slock A Read (A) Unlock A Xlock B Read (B) B: =A+B Write (B) Ulock B
假设有初值A=10,B=50,
按照T1→T2的串行结果是A=60,B=110,
按照T2→T1的方式串行的结果是A=70,B=60。
T1
T2
Slock B
Read (B)
XlockA
SlockA
等待
等待
Ulock B
等待
Read (A)
等待
A: =A+B
等待
Write (A)
等待
UlockA
Read (A)
Xlock B
Unlock A
Read (B)
B:
=
A
+
B
Write (B)
UlockB
\begin{array}{|l|l|} \hline \text { T1 } & \text { T2 } \\ \hline \text { Slock B } & \\ \hline \text { Read (B) } & \\ \hline \text { XlockA } & \text { SlockA } \\ \hline & \text { 等待 } \\ \hline & \text { 等待 } \\ \hline \text { Ulock B } & \text { 等待 } \\ \hline \text { Read (A) } & \text { 等待 } \\ \hline \text { A: =A+B } & \text { 等待 } \\ \hline \text { Write (A) } & \text { 等待 } \\ \hline \text { UlockA } & \text { Read (A) } \\ \hline & \text { Xlock B } \\ \hline & \text { Unlock A } \\ \hline & \text { Read (B) } \\ \hline & \text { B: }=A+B \\ \hline & \text { Write (B) } \\ \hline & \text { UlockB } \\ \hline \end{array}
T1 Slock B Read (B) XlockA Ulock B Read (A) A: =A+B Write (A) UlockA T2 SlockA 等待 等待 等待 等待 等待 等待 Read (A) Xlock B Unlock A Read (B) B: =A+B Write (B) UlockB
两个事务T1和T2均遵守两段锁协议,其执行结果为A=60,B=110,跟按照T1→T2顺序的串行执行的结果相同,因此对这两个事务的并发调度是可串行化的,是正确的。
- 事务遵守两段锁协议是可串行化调度的充分条件,而不是必要条件。
- 若并发事务都遵守两段锁协议,则对这些事务的任何并发调度策略都是可串行化的
- 若并发事务的一个调度是可串行化的,不一定所有事务都符合两段锁协议
遵守两段锁协议的事务可能发生死锁
T1
T2
Slock B
Read (B)
Slock A
Read (A)
Xlock A
等待
Xlock B
等待
等待
\begin{array}{|l|l|} \hline \text { T1 } & \text { T2 } \\ \hline \text { Slock B } & \\ \hline \text { Read (B) } & \\ \hline & \text { Slock A } \\ \hline & \text { Read (A) } \\ \hline \text { Xlock A } & \\ \hline \text { 等待 } & \text { Xlock B } \\ \hline \text { 等待 } & \text { 等待 } \\ \hline \end{array}
T1 Slock B Read (B) Xlock A 等待 等待 T2 Slock A Read (A) Xlock B 等待
🕘 2.6 活锁和死锁
🕤 2.6.1 相关概念
和操作系统一样,封锁的方法可能引起活锁和死锁。
-
如果事务T1封锁了数据R,事务T2又请求封锁R,于是T2等待。T3也请求封锁R,当T1释放了R上的封锁之后系统首先批准了T3的请求,T2仍然等待。然后T4又请求封锁R,当T3释放了R上的封锁之后系统又批准了T4的请求……T2在不断重复尝试获取封锁数据R,可能需永远等待,这就是活锁的情形。
-
活锁指的是事务没有被阻塞,导致一直尝试、失败、尝试、失败……,避免活锁最简单的方法是先来先服务的策略。
-
事务T1封锁了数据R1
-
T2封锁了数据R2
-
T1又请求封锁R2,因T2已封锁了R2,于是T1等待T2释放R2上的锁
-
接着T2又申请封锁R1,因T1已封锁了R1,T2也只能等待T1释放R1上的锁
-
这样T1在等待T2,而T2又在等待T1,T1和T2两个事务永远不能结束,形成死锁
T1 T2 lock R1 ⋅ ⋅ lock R2 ⋅ ⋅ lock R2 ⋅ 等待 lock R1 等待 等待 等待 等待 \begin{array}{|l|l|} \hline \text { T1 } & \text { T2 } \\ \hline \text { lock R1 } & ~~~~~~~ \cdot \\ \hline ~~~~~~~ \cdot & \text { lock R2 } \\ \hline ~~~~~~~ \cdot & ~~~~~~~ \cdot \\ \hline \text { lock R2 } & ~~~~~~~ \cdot \\ \hline \text { 等待 } & \text { lock R1 } \\ \hline \text { 等待 } & \text { 等待 } \\ \hline \text { 等待 } & \text { 等待 } \\ \hline \end{array} T1 lock R1 ⋅ ⋅ lock R2 等待 等待 等待 T2 ⋅ lock R2 ⋅ ⋅ lock R1 等待 等待
🕤 2.6.2 死锁预防
- 产生死锁的原因是两个或多个事务都已封锁了一些数据对象,然后又都请求对已为其他事务封锁的数据对象加锁,从而出现死等待。
- 预防死锁的发生就是要破坏产生死锁的条件
OS中预防死锁的方法
- 一次封锁法
要求每个事务必须一次将所有要使用的数据全部加锁,否则就不能继续执行
- 存在的问题
- 降低系统并发度
- 难于事先精确确定封锁对象
- 顺序封锁法
顺序封锁法是预先对数据对象规定一个封锁顺序,所有事务都按这个顺序实行封锁。
- 存在的问题
- 维护成本:数据库系统中封锁的数据对象极多,并且在不断地变化。
- 难以实现:很难事先确定每一个事务要封锁哪些对象。
结论:
- 在操作系统中广为采用的预防死锁的策略并不很适合数据库的特点
- DBMS在解决死锁的问题上更普遍采用的是诊断并解除死锁的方法,也可以修改封锁协议从而避免死锁。
🕤 2.6.3 死锁的检测
- 超时法
如果一个事务的等待时间超过了规定的时限,就认为发生了死锁
- 优点:实现简单
- 缺点
- 有可能误判死锁
- 时限若设置得太长,死锁发生后不能及时发现
- 事务等待图法
用事务等待图动态反映所有事务的等待情况
- 事务等待图是一个有向图G=(T,U)
- T为结点的集合,每个结点表示正运行的事务
- U为边的集合,每条边表示事务等待的情况
- 若T1等待T2,则T1,T2之间划一条有向边,从T1指向T2
- 图(a)中,事务T1等待T2,T2等待T1,产生了死锁
- 图(b)中,事务T1等待T2,T2等待T3,T3等待T4,T4又等待T1,产生了死锁
- 图(b)中,事务T3可能还等待T2,在大回路中又有小的回路
并发控制子系统周期性地(比如每隔数秒)生成事务等待图,检测事务。如果发现图中存在回路,则表示系统中出现了死锁。
🕤 2.6.4 死锁的处理
- 选择一个处理死锁代价最小的事务,将其撤消
- 释放此事务持有的所有的锁,使其它事务能继续运行下去
🕒 3. 数据库的恢复技术
故障是不可避免的
- 系统故障
- 人为故障
数据库的恢复
- 把数据库从错误状态恢复到某一已知的正确状态(亦称为一致状态或完整状态)
🕘 3.1 数据库故障分类
🕤 3.1.1 事务内部的故障
- 有的是预期的,在程序中可以预先估计到的,由事务程序加入判断和ROLLBACK语句进行处理。
- 有的是非预期的,事务内部更多的故障是非预期的,是不能由应用程序处理的。
- 运算溢出
- 并发事务发生死锁而被选中撤销该事务
- 违反了某些完整性限制等
以后,事务故障仅指这类非预期的故障
- 事务故障的恢复:撤消事务(UNDO)
🕤 3.1.2 系统故障
- 系统故障是指引起系统停止运转随之要求重新启动的任何事件。
- 原因:硬件故障(如CPU故障)、软件故障(如操作系统故障)、突然断电等。
- 特点:
- 整个系统的正常运行突然被破坏
- 所有正在运行的事务都非正常终止
- 不破坏数据库
- 内存中数据库缓冲区的信息全部丢失
发生系统故障时,事务未提交
- 恢复策略:强行撤消(
UNDO
)所有未完成事务
发生系统故障时,事务已提交,但缓冲区中的信息尚未完全写回到磁盘上。
- 恢复策略:重做(
REDO
)所有已提交的事务
🕤 3.1.3 介质故障
称为硬故障,指外存故障
- 磁盘损坏
- 磁头碰撞
- 瞬时强磁场干扰
(1)重装转储的备份副本(.mdf)到新的磁盘,使数据库恢复到转储时的一致状态;
(2)在日志(.ldf)中找出转储后所有已提交的事务;
(3)对这些已提交的事务进行REDO
处理,即子系统撤消所有未完成事务,对所有已提交的事务进行重做。
🕤 3.1.4 计算机病毒
计算机病毒是一种人为的故障或者破坏,病毒程序不同于其他程序,它可以繁殖并传播,并造成对计算机系统包括数据库系统的危害。计算机病毒的种类很多,不同病毒有不同特征。有的计算机病毒传播很快,一旦侵入系统就马上摧毁系统;有的病毒有较长潜伏期,计算机感染数天或数月后才开始发病;有的只对特定的某些程序或数据感兴趣。
🕘 3.2 数据库恢复的主要技术
恢复操作的基本原理:冗余
- 使用存储在另一个系统中的“冗余”数据以及事先建立起来的日志文件,重新构建数据库中已经被损坏的数据,或者修复已经不正确的数据。
恢复机制涉及的关键问题
- 如何建立冗余数据
- 数据转储
- 登记日志文件
- 如何利用这些冗余数据实施数据库恢复
🕤 3.2.1 数据转储技术
- 所谓数据转储,是指由DBA(数据库管理员)定期的将整个数据库中的内容复制到另一个存储设备或另一个磁盘上去,这些转储的副本称为后备副本或后援副本。
- 如何使用
- 数据库遭到破坏后可以将后备副本重新装入
- 重装后备副本只能将数据库恢复到最近转储时的状态
转储方法:
(1)从转储的运行状态,可分为静态转储与动态转储
(2)从转储的进行方式,可分为海量转储与增量转储
静态转储
- 在系统中无运行事务时进行的转储操作
- 转储开始时数据库处于一致性状态
- 转储期间不允许对数据库的任何存取、修改操作
- 得到的一定是一个数据一致性的副本
- 优点:实现简单,可保证副本与数据库的一致性
- 缺点:降低了数据库的可用性,效率较低
- 转储必须等待正运行的用户事务结束
- 新的事务必须等转储结束
动态转储
- 转储操作与用户事务并发进行
- 转储期间允许对数据库进行存取或修改
- 优点
- 不用等待正在运行的用户事务结束
- 不会影响新事务的运行
- 动态转储的缺点
- 不能保证副本中的数据正确有效
- 利用动态转储得到的副本进行故障恢复
- 需要把动态转储期间各事务对数据库的修改活动登记下来,建立日志文件
- 后备副本加上日志文件才能把数据库恢复到某一时刻的正确状态
海量转储:每次转储全部数据库
增量转储:只转储上次转储后更新过的数据
海量转储与增量转储比较:
- 从恢复角度看,使用海量转储得到的后备副本进行恢复往往更方便
- 但如果数据库很大,事务处理又十分频繁,则增量转储方式更实用、更有效
注:上述转储状态和转储方式可以两两组合
🕤 3.2.2 登记日志文件
日志文件定义:日志文件(log)是用来记录事务对数据库的更新操作的文件
日志文件的格式:
- 以记录为单位的日志文件
- 各个事务的开始标记(
BEGIN TRANSACTION
) - 各个事务的结束标记(
COMMIT
或ROLLBACK
) - 各个事务的所有更新操作
- 各个事务的开始标记(
具体来说,每个日志记录的格式为:
(【事务标识】(标明是哪个事务),【操作类型】(插入、删除或修改),【操作对象】(记录内部标识),【前像】(对插入操作而言,此项为空值),【后像】(对删除操作而言, 此项为空值))
举例说明日志文件记录,对于下面每次操作,在日志文件中写一个记录:
(1)事务T开始,日志记录为(T,start, , , )
(2)事务T修改对象A,日志记录为(T,update,A,前像,后像)
(3)事务T插入对象A,日志记录为(T,insert,A, ,后像)
(4)事务T删除对象A,日志记录为(T,delete,A,前像, )
(5)事务T提交,日志记录为(T,commit, , , )
(6)事务T回滚,日志记录为(T,rollback, , , )
- 以数据块为单位的日志文件
每个日志记录的格式为:
(【事务标识】(标明是哪个事务),【被更新的数据块】)
日志文件的作用:
(1)当数据库发生的是事务故障和系统故障时,直接根据日志文件对相应的数据库操作进行UNDO
和REDO
操作即可;
(2)当发生介质故障时,
- 如果采用的是动态转储方式,则将后备副本和日志文件结合起来才能有效恢复数据库;
- 如果采用的是静态转储方式,也可建立日志文件,二者结合完成数据库的恢复。
登记日志文件的基本原则:
(1)登记的次序必须严格按照并发事务执行的时间次序;
(2)必须先写日志文件,后写数据库,并且日志文件不能和数据库放在同一磁盘上,要经常把它复制到磁带上。
🕘 3.3 数据库的恢复策略
🕤 3.3.1 事务故障的恢复
事务故障:事务在运行至正常结束提交前被终止
恢复方法:应该撤消(UNDO
)该事务对数据库的一切更新
事务故障的恢复由系统自动完成,对用户是透明的,不需要用户干预
🕤 3.3.2 系统故障的恢复
系统故障造成数据库不一致状态的原因
- 未完成事务对数据库的更新已写入数据库
- 已提交事务对数据库的更新还留在缓冲区没来得及写入数据库
恢复方法:
Undo
故障发生时未完成的事务Redo
已完成的事务
系统故障的恢复由系统在重新启动时自动完成,不需要用户干预
🕤 3.3.3 介质故障的恢复
具体措施:
(1)检查磁盘的毁坏程度,必要时更换磁盘;
(2)然后修复系统(包括操作系统和DBMS),重新启动系统;
(3)重新装入最近的后备副本,使数据库恢复到最近一次转储时的一致性状态;
(4)重新装入有关的日志文件副本,对日志记录中转储点之后的已提交的事务进行REDO
操作,将数据库恢复到故障前某一时刻的一致状态。
🕘 3.4 SQL Server 2014的数据库备份和恢复
SQL Server 支持四种备份类型:
- 完全备份
- 差异备份
- 事务日志备份
- 文件和文件组备份。
这里主要介绍前3种备份方式。
🕤 3.4.1 完全备份
完全备份是将数据库中的全部信息进行备份,它是恢复的基线,可以与前面提到的海量转储联系起来。在进行完全备份时,不但备份数据库的数据文件、日志文件,还备份文件的存储位置信息以及数据库中的全部对象。完全备份需要消耗较多的时间和系统资源,一般可以几天或几周进行一次。恢复日志备份和差异备份时都依赖完全数据库备份。
例3:完全备份教学管理信息系统数据库
方法一:使用SQL语句
BACKUP DATABASE 教学管理信息系统
TO 教学管理信息系统_FULL
WITH INIT
方法二:使用对象资源管理器
🕤 3.4.2 差异备份
差异备份是备份从最近的完全备份之后数据库的全部变化内容。它以完全备份为基准点,备份完全备份之后变化了的数据文件、日志文件和其他被修改的内容。差异备份比完全备份需要的时间短。
例4:差异备份教学管理信息系统数据库。
方法一:使用SQL语句
BACKUP DATABASE 教学管理信息系统
TO 教学管理信息系统_DIFFER
WITH DIFFERENTIAL
提示:教学管理信息系统_DIFFER是备份设备。WITH DIFFERENTIAL参数选项为差异备份。
方法二:使用对象资源管理器
🕤 3.4.3 事务日志备份
事务日志备份是对上次备份(可以是完全备份、差异备份和日志备份)之后到当前备份时间所记录的日志内容进行备份。应该经常创建事务日志备份,减少丢失数据的危险。可以使用事务日志备份将数据库恢复到特定的即时点。
例5:事务日志备份教学管理信息系统数据库。
方法一:使用SQL语句
BACKUP LOG 教学管理信息系统
TO 教学管理信息系统_LOG
WITH INIT
提示:教学管理信息系统_LOG是备份设备。
方法二:使用对象资源管理器
注意:简单恢复模式下无法进行日志备份,更改恢复模式的方法:选定数据库—右键属性—选项----恢复模式更改
以上三种备份方式可以不同组合以符合各种应用需要,还需要适合的备份策略。
(1)完全备份策略
当对数据修改不是很频繁,而且允许一定量的数据丢失时,可以选择完全备份策略。
(2)完全备份加事务日志备份
如果用户不允许丢失太多的数据库,而且又不希望经常耗费过多时间和资源进行完全备份,这时可在两次完全备份中间进行多次事务日志备份,即完全备份加事务日志备份策略。
(3)完全备份加差异备份再加事务日志备份
进行一次完全备份所需时间比较长,如果用户希望将进行完全备份的时间间隔再加大一些,比如每周进行一次。我们可以采取第三种备份策略,即完全备份加差异备份和事务日志备份策略。
例6:从完全备份“教学管理信息系统_FULL”中恢复“教学管理信息系统_还原练习”数据库。
方法一:使用SQL语句
RESTORE DATABASE 教学管理信息系统
FROM 教学管理信息系统_FULL
WITH RECOVERY
方法二:使用对象资源管理器
例7:从差异备份“教学管理信息系统_DIFFER”中恢复“教学管理信息系统”数据库。
分析:从差异备份中恢复,先要进行完全备份恢复,再从差异备份恢复,所以需要两步操作。
方法一:使用SQL语句
①先从完全备份恢复,使用NORECOVERY选项。
RESTORE DATABASE 教学管理信息系统
FROM 教学管理信息系统_FULL
WITH NORECOVERY
②再从差异备份中恢复,使用RECOVERY选项。
RESTORE DATABASE 教学管理信息系统
FROM 教学管理信息系统_FULL
WITH RECOVERY
提示:NORECOVERY
指出在执行数据库恢复后不回滚未提交的事务。RECOVERY
与NORECOVERY
刚好相反,要求在执行数据库恢复后回滚未提交的事务。
方法二:使用对象资源管理器
例8:从日志备份“教学管理信息系统_LOG”中恢复“教学管理信息系统”数据库。
恢复日志备份的时候,SQL Server只恢复事务日志中所记录的数据更改。恢复步骤是首先恢复完全数据库备份;如果存在差异备份,则再恢复差异备份;最后恢复日志备份。
方法一:使用SQL语句
①先从完全备份恢复,使用NORECOVERY
选项。
RESTORE DATABASE 教学管理信息系统
FROM 教学管理信息系统_FULL
WITH NORECOVERY
②再从差异备份中恢复,使用NORECOVERY
选项。
RESTORE DATABASE 教学管理信息系统
FROM 教学管理信息系统_ DIFFER
WITH NORECOVERY
③最后使用日志备份恢复,使用RECOVERY
选项。
RESTORE DATABASE 教学管理信息系统
FROM 教学管理信息系统_ LOG
WITH RECOVERY
方法二:使用对象资源管理器
🕘 3.5 数据库的复制
🕤 3.5.1 快照复制
快照复制是完全按照数据和数据库对象出现时的状态来复制和分发它们的过程。快照复制分发特定时刻的数据,并不需要连续地监视数据更新,因为已发布数据的变化不被增量地传播到订阅服务器,而是周期性的被一次复制。快照复制是复制那些经常改变的数据的最佳方法,同时也适合于那些不需要最新数据和低滞后时间的场合。在进行同步时,整个快照被创建并发往订阅服务器。
🕤 3.5.2 事务复制
使用事务复制,初始快照数据将被传播到订阅服务器,因此该订阅服务器就具有了一个所谓的初始负载,这是可以开始工作的内容。当发布服务器上发生数据修改时,这些单独的事务会被及时捕获并复制到订阅服务器。并保留事务边界,当所有的改变都被传播后,所有订阅服务器将具有与传播服务器相同的值。
事务复制适用于以下场合:
(1)增量改变需要在发生时传播到订阅服务器。
(2)事务需要坚持其ACID(原子、一致性、隔离和持久性)属性。
(3)订阅服务器频繁地以可靠的方式连接到发布服务器上。
🕤 3.5.3 合并复制
任意计算机上的更新将在稍后复制到另一台计算机上。合并复制是从发布服务器向订阅服务器分发数据的过程,允许发布服务器和订阅服务器在连接或非连接状态下进行更新,然后在站点连接状态下合并这些更新。
合并复制包括默认和定制冲突解决两种选择,用户可以在配置合并复制的同时定义它。在发生冲突时,合并代理程序将调用一个解决程序,该程序将判断哪些数据应被接受和传播到其他站点。合并复制适用于以下场合:
(1)多个订阅服务器需要在不同时间更新数据并将数据改变传播到发布服务器和其他订阅服务器。
(2)订阅服务器需要接收数据并脱机更改数据,然后与发布服务器及其他订阅服务器同步这些改变。
(3) 当数据在多个站点上进行更新时,可能会导致一些冲突。这主要因为数据首先被过滤到分区中,然后才发布到不同的订阅服务器那里,或者是因为应用程序使用方式的原因。但是,如果发生了冲突,ACID属性的冲突是可以接受的。
🕒 4. 课后习题
-
【单选题】DBMS的恢复子系统,保证了事务__________的实现。
A、原子性
B、一致性
C、隔离性
D、持久性 -
【单选题】事务是数据库执行的基本工作单位,如果一个事务执行成功,则全部更新提交;如果一个事务执行失败,则已做过的更新被恢复原状,好像整个事务从未有过这些更新,这就保持数据库处于( )状态。
A、安全性
B、一致性
C、完整性
D、可靠性 -
【填空题】如果数据库中只包含成功事务提交的结果,就说数据库处于__________状态。
-
【单选题】在第一个事务以S封锁方式读数据A时,第二个事务对数据A的读方式会遭到失败的是( )。
A、实现X封锁的读
B、实现S封锁的读
C、不加封锁的读
D、实现共享型封锁的读 -
【多选题】设T1和T2两个事务,它们对数据A的并发操作如下图所示(其中SLOCK A表示对数据A上S锁,UNLOCK A表示对数据A解锁,COMMIT表示提交操作)。对这个并发操作,下面5个评价中()和()两条评价是正确的。
T 1 T 2 ① 请求 SLOCK A 读 A = 18 ② 请求 SLOCK A 读 A = 18 ③ A = A + 10 写回 A = 28 COMMIT UNLOCK A 写回 A = 18 ④ COMMIT UNLOCK A \begin{array}{|l|l|} \hline T_{1} & T_{2} \\ \hline \text { ① 请求 } & \\ \text { SLOCK A } & \\ \text { 读 } \mathrm{A}=18 & \\ \text { ②} & \text { 请求 } \\ & \text { SLOCK A } \\ & \text { 读 } \mathrm{A}=18 \\ \text { ③ } \mathrm{A}=\mathrm{A}+10 & \\ \text { 写回 } \mathrm{A}=28 & \\ \text { COMMIT } & \\ \text { UNLOCK A } & \text { 写回 } \mathrm{A}=18 \\ \text { ④ } & \text { COMMIT } \\ & \text { UNLOCK } \mathrm{A} \\ \hline \end{array} T1 ① 请求 SLOCK A 读 A=18 ② ③ A=A+10 写回 A=28 COMMIT UNLOCK A ④ T2 请求 SLOCK A 读 A=18 写回 A=18 COMMIT UNLOCK A
A、该操作不能重复读
B、该操作丢失修改
C、该操作符合完整性要求
D、该操作的第(1)步中,事务T1应申请X锁
E、该操作的第(2)步中,事务T2不可能得到对A的锁 -
【填空题】如果多个事务依次执行,则称事务是( )执行;如果利用分时的方法,同时处理多个事务,则称事务是( )执行。
-
【判断题】采用封锁技术时与封锁协议无关。
-
【判断题】在第一个事务以S锁方式读数据R时,第二个事务可以进行对数据R加S锁并写数据的操作。
-
【单选题】数据库恢复的基础是利用转储的冗余数据。这些转储的冗余数据包括_____。
A、数据字典、应用程序、审计档案、数据库后备副本
B、数据字典、应用程序、日志文件、审计档案
C、日志文件、数据库后备副本
D、数据字典、应用程序、数据库后备副本 -
【单选题】关于具有检查点的恢复技术,下列说法不正确的是()。
A、检查点最大限度地减少数据库完全恢复时所必须执行的日志部分
B、使用检查点方法可以改善恢复效率
C、无论事务在检查点之前还是之后提交,都需要执行REDO操作
D、恢复子系统可以定期地建立检查点,也可以按照某种规则建立检查点 -
【单选题】用于数据库恢复的重要文件是( )
A、数据库文件
B、索引文件
C、日志文件
D、备注文件 -
【单选题】后援副本的用途是( )。
A、安全性保障
B、一致性控制
C、故障后的恢复
D、数据的转储 -
【单选题】“所有事务都是两段式”与“事务的并发调度是可串行化”两者之间的关系是( )。
A、同时成立与不成立
B、没有必然的联系
C、前者蕴涵后者
D、后者蕴涵前者 -
【单选题】写一个修改到数据库中,与写一个表示这个修改的运行记录到日志文件中是两个不同的操作,对这两个操作的顺序安排应该是( )。
A、前者先做
B、后者先做
C、由程序员在程序中做安排
D、哪一个先做由系统决定
答案:1.D 2.B 3.一致性 4.A 5.BD 6.串行、并行 7.× 8.× 9.C 10.C 11.C 12.C 13.C 14.B
OK,以上就是本期知识点“事务并发控制和恢复技术”的知识啦~~ ,感谢友友们的阅读。后续还会继续更新,欢迎持续关注哟📌~
💫如果有错误❌,欢迎批评指正呀👀~让我们一起相互进步🚀
🎉如果觉得收获满满,可以点点赞👍支持一下哟~