事务管理是对于一系列数据库操作进行管理。在多个事务并发执行的数据库系统中,如果对共享数据的更新不进行控制,就会产生数据的不一致性,导致数据库存储数据错误。运行中的数据库系统很容易受到来自多方面的干扰和破坏。如软、硬件系统故障,合法用户的误操作,非法入侵等等。
数据库的保护就是要排除和防止各种对数据库的干扰破坏,确保数据安全、可靠,以及在数据库已经遭到破坏后如何尽快地恢复正常。数据库的保护是通过对数据库的恢复、安全性控制、完整性控制和并发控制四个方面来实现的。
1. 事务的基本概念
事务(Transaction)是一系列的数据库操作,是数据库应用程序的基本逻辑单位,即应用程序对数据库的操作都应该以事务的方式进行。
事务是一个操作序列,这些操作“要么都做,要么都不做”,是数据库环境中不可分割的逻辑工作单位。事务和程序是两个不同的概念,一般一个程序可包含多个事务。
事务通常由数据库操纵语言或其他高级语言(如SQL、COBOL、C、C++、Java等)书写的用户程序来实现。一个事务由应用程序的一组操作序列组成,它以 BEGIN TRANSACTION 语句开始,以 END TRANSACTION 结束语句。
事务定义的语句如下:
(1)BEGIN TRANSACTION:事务开始。
(2)END TRANSACTION:事务结束。
(3)COMMIT:事务提交。该操作表示事务成功地结束,它将通知事务管理器该事务的所有更新操作现在可以被提交或永久地保留。
(4)ROLLBACK:事务回滚。该操作表示事务非成功地结束,它将通知事务管理器出故障了,数据库可能处于不一致状态,该事务的所有更新操作必须回滚或撤销。
示例1. 银行转账业务,对“从账户A转入账户B金额x元”业务,站在顾客角度来看,转账是一次单独操作;而站在数据库系统的角度它至少是由两个操作组成的,第一步从账户A减去x元,第二步给账户B加上x元。下面是银行转账事务的伪代码:
BEGIN TRANSACTION read(A); /*读账户A的金额*/ A=A-x; IF (A<0) THEN print("金额不足,不能转账"); ROLLBACK; /*撤销该事务,回到事务执行前的状态*/ ELSE write(A); /*写入账户A的金额*/ read(B); B=B+x; write(B); COMMIT; /*提交事务*/ ENDIF; END TRANSACTION
SQL 标准规定当一条 SQL 语句被执行,就隐式地开始了一个事务,SQL中的 Commit work 和 Rollback work 语句之一会结束一个事务。
(1)Commit work:提交当前事务。这意味着将该事务所做的更新在数据库中永久保存。一旦事务被提交后,一个新的事务自动开始。
(2)Rollback work:回滚当前事务。这意味着将撤销该事务对数据库的更新。这样,数据库恢复到该事务执行第一条语句之前的状态。
若事务已执行了 Commit work,就不能用 Rollback work 来撤销。数据库系统能保证在发生诸如某条 SQL 语句错误、断电、系统崩溃的情况下,若事务还没有执行 Commit work,则所造成的影响将被回滚。对断电、系统崩溃的情况,回滚是在系统重新启动时进行。
事务具有四个特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),简称ACID特性。
原子性:事务的所有操作在数据库中要么全做要么全都不做。如银行转账中的两个操作必须做为一个单位来处理,不能只执行部分操作。
一致性:一个事务独立执行的结果,将保持数据的一致性,即数据不会因为事务的执行而遭受破坏。
隔离性:一个事务的执行不能被其他事务干扰。并发事务在执行过程中可能会对同一数据进行操作,这些事务的操作应该不会相互干扰,是相互隔离的。如事务执行中数据不一致性状态出现时不能让其他事务读取到不一致的数据。
持久性:一个事务一旦提交,它对数据库的改变必须是永久的,即便系统出现故障时也是如此。
如果不出现故障,那么所有事务都能执行完成。一旦在执行过程中发生故障,不能执行完成的事务称为中止事务;将中止事务对数据库的更新撤销称为事务回滚;成功执行完成的事务称为已提交事务。
中止的事务是可以回滚的,通过回滚恢复数据库,保持数据库的一致性,这是 DBMS 的责任。已提交的事务是不能回滚的,必须由程序员或 DBA 手工执行一个“补偿事务”才能撤销提交的事务对数据库的影响。
事务一旦提交,就不能中止它,而要撤销已提交事务所造成影响的唯一方法是执行一个补偿事务(Compensating Transaction)。
事务的五种状态:
活动状态:事务的初始状态,事务执行时处于这个状态。
部分提交状态:当操作序列的最后一条语句自动执行后,事务处于部分提交状态。这时,事务虽然已经完全执行,但由于实际输出可能还临时驻留在内存中,在事务成功完成前仍有可能出现硬件故障,事务仍有可能不得不中止。因此,部分提交状态并不等于事务成功执行。
- 失败状态:由于硬件或逻辑等错误,使得事务不能继续正常执行,事务就进入了失败状态。处于失败状态的事务必须进行回滚(ROLLBACK)。这样,事务就进入了中止状态。
- 中止状态:事务回滚并且数据库恢复到事务开始执行前的状态。
- 提交状态:当事务成功完成后,称事务处于提交状态。只有事务处于提交状态后,才能说事务己经提交。
事务的状态转换:
事务进入中止状态后,系统一般有如下两种选择:
- 重启事务。当事务中止的原因是软、硬件错误而不是事务内部逻辑错误时,一般采用重启事务的方法。重启事务可以被看成一个新事务。
- 杀死事务。这样做通常是因为事务中止的原因是事务内部的逻辑错误,或者是输入错误,也可能是所需数据在数据库中没找到等原因。
2. 数据库的并发控制
并发操作,是指在多用户共享的系统中,许多用户可能同时对同一数据进行操作。并发操作带来的问题是数据的不一致性,主要有三类:丢失修改、不可重复读和读脏数据。其主要原因是事务的并发操作破坏了事务的隔离性。DBMS 的并发控制子系统负责协调并发事务的执行,保证数据库的完整性不受破坏,避免用户得到不正确的数据。
2.1 事务调度
1) 串行调度
串行调度(serial schedule)是指多个事务依次串行执行,且只有当一个事务的所有操作都执行完后才执行另一个事务的所有操作。
假设用A和B表示账号A和账号B的存款数量;A、B的初值为10000和20000。如果这两个事务串行执行,可以有两种调度方案。
2 )并发调度
并发调度(concurrent schedule):利用分时的方法同时处理多个事务。
对于N个事务进行并发调度,情况会变得复杂得多,它的调度方案远大于N!个,而且并发调度的结果有可能是错误的。
(a)最终结果A+B与原来一致,则这个并发调度是正确的。
(b)最终A+B结果为32000$\ne$30000,这个调度是错误的。
3)可恢复调度
T1 -> T2:A=50.5,B=252.5,A+B=303
T2 -> T1:A=51,B=252,A+B=303
(2) 若上述两个事务的一个并发调度顺序如下图所示,请问调度是否正确,为什么?
调度不正确,因为根据A、B 的初值,按照给定的调度,获得执行结果为:A=50.5、B=252,与任何一个串行执行的结果都不同,故为错误的调度,事实上会造成储户的无端损失。
2.2 并发操作带来的问题
并发操作带来的数据不一致性有三类:丢失修改、不可重复读和读脏数据
。
1)丢失修改
2.3 并发调度的可串行性
数据库系统必须控制事务的并发执行以保证数据库处于一致性状态。
1)可串行化的调度
多个事务的并发执行是正确的,当且仅当其结果与某一次序串行地执行它们时的结果相同,称这种调度策略是可串行化的调度(serializability schedule)。
可串行性是并发事务正确性的准则,按这个准则规定,一个给定的并发调度,当且仅当它是可串行化的才认为是正确调度。
2)冲突可串行化
事务管理控制(下):https://developer.aliyun.com/article/1529561