数据库并发事务控制一:综述
原文:http://www.2cto.com/database/201501/373684.html
并发控制是DBMS的关键技术
对数据库的操作都是在事务中进行的。
事务是指一组相互依赖的操作行为。事务中的操作是不可分割的工作单元,由一组在业务逻辑上相互依赖的SQL语句组成,有ACID特征。
Atomic(原子性):事务中包含的操作被看做一个逻辑单元,这个逻辑单元中的操作要么全部成功,要么全部失败。
Consistency(一致性):只有合法的数据可以被写入数据库,否则事务应该将其回滚到最初状态。
Isolation(隔离性):事务允许多个用户对同一个数据进行并发访问,而不破坏数据的正确性和完整性。同时,并行事务的修改必须与其他并行事务的修改相互独立。
Durability(持久性):事务结束后,事务处理的结果必须能够得到固化。
数据库中有多个事务同时存在,就是事务并发,此时就不能保证事务隔离性,SQL-92定义了事务隔离级别,描述了给定事务的行为对其它并发执行事务的暴露程度,或者说是一个事务必须与其它事务进行隔离的程度。隔离级别由低到高为:
Read Uncommitted,Read Committed,Repeatable Read, Serializable
隔离级别越高,越能保证数据的完整性和一致性,但对并发性能的影响也越大。
并发事务操作相同的数据时会出现冲突,不能完全保证数据的完整性和一致性,此时可能会出现以下问题:
Lost update(第一类丢失更新):撤销一个事务时,把其他事务已提交的更新数据覆盖。
Dirty Reads(脏读):一个事务读到另一事务未提交的更新数据。
Phantom Reads(虚读):一个事务读到另一事务已提交的新插入的数据。
Non-repeatable Reads(不可重复读):一个事务读到另一事务已提交的更新数据。
Second lost updates problem(第二类丢失更新):这是不可重复读中的特例,一个事务覆盖另一事务已提交的更新数据。
事务隔离级别和可能出现的问题
保证并发事务准确性的关键是让冲突的相关事务调度可串行化。理论上,某一事务执行时禁止其他事务执行的调度策略一定是可串行化的调度,这是最简单的调度策略,但实际上是不可行的,因为它使用户不能充分共享数据库资源。目前DBMS普遍采用封锁方法来保证事务正确性;即保证并行操作调度的可串行性。除此之外还有其他一些方法,如时标方法、乐观方法等。
悲观并发控制: 锁定系统阻止用户以影响其它用户的方式修改数据。如果用户执行的操作导致应用了某个锁,则直到这个锁的所有者释放该锁,其它用户才能执行与该锁冲突的操作。该方法主要用在数据争夺激烈的环境中,以及出现并发冲突时用锁保护数据的成本比回滚事务的成本低的环境中,因此称该方法为悲观并发控制。
乐观并发控制: 在乐观并发控制中,用户读数据时不锁定数据。在执行更新时,系统进行检查,查看另一个用户读过数据后是否更改了数据。如果另一个用户更新了数据,将产生一个错误。一般情况下,接收错误信息的用户将回滚事务并重新开始。该方法主要用在数据争夺少的环境内,以及偶尔回滚事务的成本超过读数据时锁定数据的成本的环境内,因此称该方法为乐观并发控制。
时标并发控制: 时标和封锁技术之间的基本区别是封锁是使一组事务的并发执行(即交叉执行)同步,使用它等价于这些事务的某一串行操作;时标法也是使用一组事务的交叉执行同步,但是使它等价于这些事务的一个特定的串行执行,即由时标的时序所确定的一个执行。如果发生冲突,是通过撤销并重新启动一个事务解决的。事务重新启动,则赋予新的时标。
单纯加锁协议解决并发控制问题会有读写冲突和写写冲突,对并发事务性能影响较大。因此实践中postgresql,mysql innodb,oracle,sql server(可设置,默认关闭),informix等数据库还使用了多版本并发控制(MVCC),MVCC可以避免读写冲突,这样使用锁搞定写写冲突的相关事务串行化操作就可以了。