数据并发与事务隔离

一、数据并发带来的各种情况 

①脏读:事务A读到事务B尚未提交的数据,并基于这个数据进行后续操作

②不可重复读:事务A读取数据后,被事务B修改或删除,事务A再次读取时前后两次读取的数据不一致

③幻像读:事务A读取数据后,事务B新增了数据,事务A再次读取是前后两次读取的数据不一致


不可重复读和幻想读的区别:

幻象读和不可重复读是两个容易混淆的概念,前者是指读到了其它已经提交事务的新增数据,而后者是指读到了已经提交事务的更改数据(更改或删除),为了避免这两种情况,采取的对策是不同的,防止读取到更改数据,只需要对操作的数据添加行级锁,阻止操作中的数据发生变化,而防止读取到新增数据,则往往需要添加表级锁——将整个表锁定,防止新增数据(Oracle使用多版本数据的方式实现)。

④第一类更新丢失:事务A和事务B同时访问同一个数据,事务B先提交修改,事务A回滚操作。导致事务B的修改丢失

⑤第二类更新丢失:事务A和事务B同时访问同一个数据,事务B先提交修改,事务A再提交。导致事务B的修改被覆盖

第一类更新丢失是很严重的操作,如果控制不当,可能导致在一个长时间的大型事务中,所有的操作都被回滚。所以所有的数据库都不支持这种并发情况。

二、数据库的锁机制

从锁的作用范围来分,可以分为:行级锁和表级锁
从锁的排他性来分,可以分为:共享锁和独占锁(排他锁),
其中共享锁允许共享,但阻止独占锁。独占锁不但不允许共享锁,且不允许其它独占锁


于是组合起来就有:

①行共享锁:允许多个会话共享锁定的行数据,但不允许对这些行的独占锁。例如select ...for update

②行独占锁:对行进行独占,不允许其它的共享锁(表,行)、独占锁(表,行)和表共享行独占锁。例如insert,update

③表共享锁:允许多个会话共享表数据,但不允许其它的独占锁(表,行)、表共享行独占锁。可以实现表级事务一致性

④表独占锁:对表进行独占,不允许其它的共享锁(表,行)、读占锁(表,行)和表共享行独占锁。达到序列化操作级别

⑤表共享行独占锁:允许多个会话共享表数据,但同一时刻只能有一个行独占锁。可以达到数据共享同时防止脏读、不可重复读、幻像读

表共享锁定可以让会话具有对表事务级一致性访问,因为其它会话在你提交或者回溯该事务并释放对该表的锁定之前不能更改这个被锁定的表(因为要修改表的记录,就必须获得行独占锁,但是共享锁会阻止独占锁的获取,这样原来其它正在读取表记录的事务就不会出现脏读、不可重复读、幻像读的情况了);

表共享行独占锁与其不同则是多了一个行独占,这样效率更高。

 

三、事务隔离级别

尽管数据库为用户提供了锁的DML操作方式,但直接使用锁管理是非常麻烦的,因此数据库为用户提供了自动锁机制。只要用户指定会话的事务隔离级别,数据库就会分析事务中的SQL语句,然后自动为事务操作的数据资源添加上适合的锁。此外数据库还会维护这些锁,当一个资源上的锁数目太多时,自动进行锁升级以提高系统的运行性能,而这一过程对用户来说完全是透明的

下面是ANSI ISO92定义的4个事务隔离级别以及对应的对数据并发的处理

READ COMMITITED:不允许读取未提交的数据,但可以读取已提交的数据。所以可能出现不可重复读、和幻像读(读的过程依然可以被修改、增加、删除)

REPEATABLE READ:通过行锁定,在读的数据不允许其它进程修改。确保已读取的数据不被修改、删除(不可重复读)但无法阻止其它进程写入新数据,所以不能确保读取到新的数据(幻像读)
SERIALIZABLE:通过表锁定,彻底禁止读取期间其它进程的修改、删除(屏蔽不可重复读)和增加(屏蔽幻像读)
但是不管是那种隔离级别,对第一类丢失更新都是不能接收的 
解决方案:为了避免上面出现的几种情况,在标准SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同。 
  ● 未授权读取,也称为读未提交(Read Uncommitted):允许脏读取,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个数据则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。 
  ● 授权读取,也称为读提交(Read Committed):允许不可重复读取,但不允许脏读取。这可以通过“瞬间共享读锁”和“排他写锁”实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。 
  ● 可重复读取(Repeatable Read):禁止不可重复读取和脏读取,但是有时可能出现幻影数据。这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。 
  ● 序列化(Serializable):提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。 
  隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed,它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、虚读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。 

 

posted @ 2010-10-29 17:45  StarWang  阅读(326)  评论(0编辑  收藏  举报