事务是一系列的数据库操作,是数据库应用程序的基本逻辑单元,也是恢复和并发控制的基本单位。
事务处理技术主要包括数据库恢复技术和并发控制技术。本篇博文主要总结下并发控制技术。
事务:是用户定义的一个数据库操作序列,这些操作要么全做,要么全不做,是一个不可分割的工作单位。例如,在关系数据库中,一个事务可以是一条SQL语句、一组SQL语句或整个程序。一般来说,一个程序中包含多个事务。
ACID,是指在可靠数据库管理系统(DBMS)中,事务(transaction)所应该具有的四个特性:
A:原子性(Atomicity): 事务是一个或多个行为捆绑在一起组成一个单独的工作单元,事务中的动作要不都发生,要不都不发生。
C:一致性(Consistent): 即在事务开始之前和结束之后,数据库的完整性约束没有被破坏。
数据库层面:在一个事务执行前和执行后,数据会符合你设置的约束(例如unique约束,foreign key约束,check约束等)和触发器设置.由数据库进行保证.
业务层面:保持业务的一致性.需要由开发人员进行保证.
I:隔离性(Isolation):指的是在并发环境中,事务之间互相影响的程度(即并发事务间数据的可见程度).当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间.由并发事务所做的修改必须与任何其他并发事务所做的修改隔离.事务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看到中间状态的数据.。
D:持续性(Durability): 也叫永久性,指一旦事务完成,事务的结果应该持久化,用来保证即使系统崩溃也不会破坏事务的结果。
保证事务ACID特性是事务管理的重要任务。事务ACID特性可能遭到破坏的因素有:
(1)多个事务并行运行时,不同事务的操作交叉执行。
(2)事务在运行过程中被强行停止。
并发操作带来的数据不一致性包括丢失修改、不可重复读和读“脏”数据。
No.1 丢失修改(或叫丢失更新)
如果两个事务都要更新数据库一个字段X,X=100;
事务A | 事务B |
读取X=100 | 读取X=100 |
写入x=X+100 | 写入x=X+200 |
事务结束x=200 | 事务结束x=300 |
最后x=300 |
两个不同的事务同时获得了相同的数据,又都对这个数据进行了修改,那么先提交的事务的更新就会被后提交事务的更新覆盖掉,叫做丢失更新。
No.2 不可重复读
一个事务执行期间,在自己没有更新数据库的情况下,同一个查询操作在执行一次或多次的情况下,结果应该是一致的。
事务A | 事务B |
读取X=100 | 读取X=100 |
读取X=100 | 写入x=X+100 |
事务结束, x=200 | |
读取X=200 (此时,在同一个事务A中,读取的X值发生了变化!) |
|
事务结束 | |
这种情况事务A多次读取数据出现不一致的结果。
摘自王珊老师的《数据库系统概论》(第5版)的解释:不可重复读包括三种情况(后两种情况也称为幻影现象或幻读):
(1):事务T1读取某一数据之后,事务T2对其进行了修改,当事务T1再次读该数据时,得到与前一次不同的值.如上所示。
(2):事务T1按一定条件从数据库中读取了某些数据记录后,事务T2删除了其中部分记录,当T1再次按照相同条件读取数据时,发现某些记录神秘的消失了。
(3):事务T1按一定条件从数据库中读取了某些数据记录后,事务T2插入了一些记录,当T1再次按照相同条件读取数据时,发现多了一些记录。
No.3 脏读(读脏数据,未提交读)
事务A | 事务B |
写入x=X+100 (x=200) | |
读取X=200 (读取了事务B未提交的数据) | |
事务回滚x=100 | |
事务结束x=100 | |
事务结束 |
事务读取了未提交的数据,如图 事务B可能出现未提交或者提交不成功的情况而进行回滚,这就导致事务A读取了错误的数据,也叫脏数据。
并发控制的主要技术有封锁(locking)、时间戳(timestamp)、乐观控制法(optimistic sheduler)和多版本并发控制(multi-version concurrency control,MVCC)等。
下面主要讲封锁方法。
封锁是实现并发控制的一个非常重要的技术。基本的封锁类型有两种:排他锁(X锁)和共享锁(S锁)。
排它锁又称写锁,若事务T对数据对象A加上X锁,则只允许T读取和修改A,其他任何事务都不能再对A加任何类型的锁直到T释放A上的锁为止。
共享锁又称读锁,若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事物只能再对A加S锁,而不能加X锁。
在运用 排他锁 和 共享锁 对数据对象加锁时,还需要约定一些规则,例如何时申请 排他锁 或 共享锁、持锁时间、何时释放等。称这些规则为封锁协议(Locking Protocol)。对封锁方式规定不同的规则,就形成了各种不同的封锁协议。不同的封锁协议对应不同的隔离级别。
在标准SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同:
A. 未授权读取(Read uncommited):允许脏读取,但不允许丢失修改。
对应一级封锁协议:一级封锁协议是:事务T在修改数据R之前必须先对其加X锁,直到事务结束才释放。事务结束包括正常结束(COMMIT)和非正常结束(ROLLBACK)。
B. 授权读取(Read Committed):允许不可重复读取,但不允许脏读取和丢失修改。这可以通过“瞬间共享读锁”和“排他写锁”实现。
对应二级封锁协议:二级封锁协议是:一级封锁协议加上事务T在读取数据R之前必须先对其加S锁,读完后即可释放S锁(瞬间S锁)。
C. 可重复读取(Repeatable Read):禁止不可重复读取和脏读取和丢失修改,但是有时可能出现幻影数据。这可以通过“共享读锁”和“排他写锁”实现。
对应三级封锁协议:三级封锁协议是:一级封锁协议加上事务T在读取数据R之前必须先对其加S锁,直到事务结束才释放。
D. 序列化(Serializable):提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。
四级封锁协议是对三级封锁协议的增强,其实现机制也最为简单,直接对事务中所读取或者更改的数据所在的表加表锁,也就是说,其他事务不能读写该表中的任何数据。这样所有的脏读,不可重复读,幻读,都得以避免!