事务隔离级别的几个问题

在数据库的处理过程中常会遇到一些并发的情况,因而会导致这样或那样的问题。而数据库的事务隔离级别就是用来应对这些问题,针对不同的场景使数据的并发操作和读取满足一定规则。

 

通常我们会遇到以下几种常见问题

  • 数据丢失(Lost Updates)

数据丢失发生在两个进程同时操作同一条数据时,比如Process A 更新内容为“icecream”,Process B更新内容为“milk”,再次查询的时候,Process A更新的数据就可能丢失了。因为两个进程操作的是同一条数据。

  • 脏读(Dirty Reads)

脏读发生在读取数据时,读取的数据不是正确的。比如Process A做了一次update操作,更新了价格为110,但是还没有commit事务,Process B读取了这条数据的价格,但是之后Process A把这条事务rollback了。使价格变回了原来的100,那么刚才Process B就发生了脏读。

  • 不可重复读(Non-repeatable Reads)

不可重复读发生在一个Process读取相同的数据时(比如相同的Where条件),两次读取的数据不一致。这种情况发生在Process A在第一和第二次读取数据之间,Process B把Process A读取的数据改变了。因而Process A第二次读取数据时,数据的值就会前后不一致了。

  • 幻读(Phantoms)

幻读指你两次相同的查询可能得到的结果数不同,因而叫幻读。比如Process A的一个事务内做一个select查询操作(带上where条件),第一次select返回数据100条。Process B之后更新了和Process A之前select语句相同的where条件的数据,当Process A再次做跟第一次相同的select时发现数据之后109条了,此时Process A的事务才完成。原因就是Process B的更新可能使数据从一个Date Page移动到另外的Date Page(数据存放的底层结构)。

因而也就有了几种事务隔离级别的产生。在讲事务隔离级别之前,我们还要引入下不同的并发模型。按类型通常分两种:乐观并发(Optimistic Concurrency)和悲观并发(Pessimistic Concurrency)

  • 悲观并发(Optimistic Concurrency)

悲观并发指在数据读取时对数据加锁,如果其他Process想要对改数据做更新必须等到读取的锁释放才能做更新。如果对数据做更新时也会加锁,如果其他Process想要对数据读取和更新都必须要等到该锁释放。

  • 乐观并发(Pessimistic Concurrency)

乐观并发指在对数据读取时,不会影响其他Process对相同数据做更新。在对数据做更新时,也不会影响其他Process对数据的读取。这种方式的实现呢,主要是考duplicate一条相同的数据使他们的版本号不同,然后读取和更新分别在不同版本的数据上,在合适的时候commit和去掉旧的数据。

哎呀妈呀,急死俺了,终于到中心点了。SQL Server 2008支持5种事务隔离级别,分别是Read Uncommitted, Read Committed,Repeatable Read,Snapshot,Serializable。

  • Read Uncommitted

这种事务隔离级别允许你发生除了数据丢失之外的其他问题。因为通常情况下数据读取会使用锁(共享锁Share Lock),就是说读一条数据会对该数据加个锁,导致其他Process不能更新该条数据做更新,但是可以读取。而写锁通常会使用排他锁(Exclusive Lock),就是说一条数据在被更新时,其他Process能读取但不能更新这条数据。这种事务隔离级别通常可以用在对数据的准确性上要求不是很高的地方(比如市场趋势报告等),或者对数据库插的吞吐量有要求的地方。因为维护对数据的锁需要一定的开销,因而会导致数据处理的能力降低。

  • Read Committed

这种事务隔离级别也是通常数据库默认的数据隔离级别。允许Process能读取已被commit的数据。它还分Read Committed(Locking)和Read Committed(Snapshot). Read Committed(Locking)就是使用上面提到的悲观并发模型,Read Committed(Snapshot)就是使用上面提到的乐观并发模型。

  • Repeatable Read

在这种事务隔离级别下,不允许发生不可重复读(Non-repeatable Read)的情况。但是允许幻读。意思就是说不允许读取的数据列的内容发生改变,但是允许读取的数据在Data Page的位置发生改变(就是除了查询的数据列之外的数据是可以被改变的)。相应的开销包括共享锁(Share Lock)和排他锁(Exclusive Lock).

  • Snapshot

Snapshot的事务隔离级别下,事务的处理和Read Committed(Snapshot)的情况和方式基本是一致的。同时Snapshot又和Serializable的方式又有些共同的地方。不过在处理并发的情况下会有些特别的地方。
比如以下两个事务同时执行:
Process A (Transaction A)

USE pubs
SET TRANSACTION ISOLATION LEVEL SNAPSHOT
DECLARE @price money
BEGIN TRAN
SELECT @price = price FROM titles where title_id = ‘BU1032’
UPDATE titles SET price = @price where title_id = ‘PS7777’
COMMIT TRAN

Process B (Transaction B)

USE pubs
SET TRANSACTION ISOLATION LEVEL SNAPSHOT
DECLARE @price money
BEGIN TRAN
SELECT @price = price FROM titles where title_id = ‘PS7777’
UPDATE titles SET price = @price where title_id = ‘BU1032’
COMMIT TRAN 

在两条同时发生的事务中,最后’PS7777’和’BU1032’的值会发生交换。我个人的理解是,数据库会分别使两条事务的运行环境分离。然后两个线程使用cpu时间片轮转的方式,分别切换着执行。因而产生的数据结果就使两条数据的值进行了交换。

  • Serializable

是要求最高的事务分离级别,之前我们提到的问题它都不允许发生。相应的开销也是很大的。除了对读取和操作的数据加锁之外,还会对读取的或更新的where条件的数据进行加锁(即使数据不存在,使用key-range lock)。操作相同where条件的数据,事务执行的顺序按照Process获得锁的先后来执行。

posted @ 2014-03-04 09:14  visionwang  阅读(480)  评论(0编辑  收藏  举报