事务
事务的特性
所谓事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。
Atomicity(原子性)
原子性是指事务是一个不可再分割的工作单位,事务中的操作要么都发生,要么都不发生。
Consistency(一致性)
一致性是指在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。这是说数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性。
Isolation(隔离性)
多个事务并发访问时,事务之间是隔离的,一个事务不应该影响其它事务运行效果。
这指的是在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。事务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看到中间状态的数据。
Durability(持久性)
持久性,意味着在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。
并发控制
并发控制的主要方法是封锁(Locking)。
读写异常
数据库是要被广大客户所共享访问的,那么在数据库操作过程中很可能出现以下几种不确定情况:
丢失修改:两个事务T1,T2读入同一数据并修改,T2提交的结果被T1破坏了,导致T1的修改丢失。(订票系统)
不可重复读:事务T1读取数据后,事务T2执行更新操作,使T1无法再次读取结果。
读脏数据:事务T1修改某个数据并写回磁盘,事务T2读取同一数据,但T1由于某种原因撤销了,这时T1修改过的数据恢复原来的值,T2读取的数据就与数据库中的数据不一致。
幻读:事务在操作过程中进行两次查询,第二次查询结果包含了第一次查询中未出现的数据(这里并不要求两次查询SQL语句相同)这是因为在两次查询过程中有另外一个事务插入数据造成的。
封锁
封锁是实现并发控制的一个非常重要的技术,所谓 封锁就是事务T在对某个数据对象例如表、记录等操作之前,先向系统发出请求,对其加锁。数据库系统提供两种锁:
排他锁(写锁):若事务T对数据对象A加写锁,则只允许T读取和修改A,其他事务都不能再对A加任何类型的锁,直到T释放A上的锁为止。
共享锁(读锁):若事务T对数据对象A加读锁,则只允许T可以读取但不能修改A,其他事务只能再对A加读锁,而不能加写锁,直到T释放A上的读锁为止。
事务隔离级别
为了避免上面出现几种情况在标准SQL规范中定义了4个事务隔离级别,不同隔离级别对事务处理不同 。
未提交读(Read Uncommitted)
未提交读(READ UNCOMMITTED)是最低的隔离级别。允许脏读(dirty reads),但不允许更新丢失,事务可以看到其他事务“尚未提交”的修改。
提交读(Read Committed)
允许不可重复读取,但不允许脏读取。这可以通过“瞬间共享读锁”和“排他写锁”实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。
可重复读(Repeatable Read)
禁止不可重复读取和脏读取,但是有时可能出现幻读数据。这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。
可序列化(Serializable)
最高的隔离级别,它要求事务序列化执行,事务只能一个接着一个地执行,不能并发执行。仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。
封锁协议
使用读锁和写锁时,需要约定一定的规则。比如:何时申请、持续时间、何时释放等。这些规则被称为 封锁协议。针对不同的事务隔离级别,有不同的封锁协议。
- 一级封锁协议:事务T在修改数据R之前必须先对其加写锁,直到事务结束才释放。一级封锁协议防止了丢失修改,但不能保证可重复读和不读脏数据。
- 二级封锁协议:在一级封锁协议的基础上增加事务T在读数据R前必须加读锁,读完就可以释放。二级封锁协议进一步防止读脏数据,但不能保证可重复读。
- 三级封锁协议:一级封锁协议的基础上增加事务T在读数据R前必须加读锁,直到事务结束才释放。三阶封锁协议除了防止丢失修改和读脏数据外,进一步防止了不可重复读。
- 四级封锁协议:四级封锁协议是对三级封锁协议的增强,其实现机制也最为简单,直接对事务中所读取或者更改的数据所在的表加表锁,也就是说,其他事务不能 读写 该表中的任何数据。
并行调度
调度是一个或多个事务的重要操作按时间排序的一个序列。如果一个调度的动作首先是一个事务的所有动作,然后是另一个事务的所有动作,以此类推,而没有动作的混合,那么我们说这一调度是串行的。
事务的正确性原则告诉我们,每个串行调度都将保持数据库状态的一致性。 通常,不管数据库初态怎样,一个调度对数据库状态的影响都和某个串行调度相同,我们就说这个调度是可串行化的。
可串行性是并行调度正确性的唯一准则,两段锁(简称2PL)协议是为保证并行调度可串行性而提供的封锁协议。两段锁协议规定:在对任何数据进行读、写操作之前,事务道首先要获得对该数据的封锁,而且在释放一个封锁之生,事务不再获得任何其他封锁。
所谓“两段”锁的含义是:事务分为两个阶段,第一阶段是获得封锁,也称为扩展阶段,第二阶段是释放封锁,也称为收缩阶段。
使用事务
在MySQL中使用START TRANSACTION
或 BEGIN
开启事务,提交事务使用COMMIT
,ROLLBACK
用来放弃事务。MySQL默认设置了事务的自动提交,即一条SQL语句就是一个事务。
总结
事务的(ACID)特性是由关系数据库管理系统(RDBMS,数据库系统)来实现的。数据库管理系统采用日志来保证事务的原子性、一致性和持久性。日志记录了事务对数据库所做的更新,如果某个事务在执行过程中发生错误,就可以根据日志,撤销事务对数据库已做的更新,使数据库退回到执行事务前的初始状态。
数据库管理系统采用锁机制来实现事务的隔离性。当多个事务同时更新数据库中相同的数据时,只允许持有锁的事务能更新该数据,其他事务必须等待,直到前一个事务释放了锁,其他事务才有机会更新该数据。