您的位置:首页 > 数据库

数据库的事务隔离级别分析

2009-04-10 16:40 141 查看
 
在数据库系统中,隔离
是定义一个操作对数据所做的改变如何/何时对其它的并行
操作可见。

 

隔离级别

 

数据库系统有四个隔离级别。对数据库使用何种隔离级别要审慎分析,因为

1. 维护一个最高的隔离级别虽然会防止数据的出错,但是却导致了并行度的损失,以及导致死锁出现的可能性增加。

2. 然而,降低隔离级别,却会引起一些难以发现的bug。

 

在不同的并行控制模式下,对各隔离级别的实现是不同的。

 

SERIALIZABLE(序列化)


 

所有的数据库事务都完全隔离,就如系统中所有的事务都顺序执行一样,一个接着一个。当然,数据库可以同时执行多个事务,只要顺序执行的“错觉”可以维护(即同时执行的多个事务不会互相干扰,或者多个事务的同时执行能保证结果的正确)。

 

a. 基于锁的并行控制。(当执行一个带有范围的where子句的查询时,需要范围锁定)

b. 无锁并行控制。不需要锁,但是当系统检测到并行的事务违反了序列化“错觉”,它会迫使那个事务回退,所以应用程序需要重新启动那个事务。

 

幻读,脏读,不可重复读等问题都不会发生。

 

REPEATABLE READ(可重复读)



 

所有查询到的数据记录都不会被改变。即会在所有获取的数据上加上读锁,但是不会加上范围锁。

 

可能发生的问题:当执行一个范围查询时,可能会发生幻读。

 

READ COMMITTED(提交读)


 

一个查询所获取的数据可以被其他事务修改。

 

读锁:在所读取的数据上添加读锁,但会立即释放。

写锁:不会释放,直到事务完结。

 

可能发生的问题:不可重复读。

 

READ UNCOMMITTED(未提交读)


 

一个事务可以看见另一个事务所做的未提交修改。

 

可能发生的问题:脏读。

 

总结:

 

可以看到,对于前三种隔离级别,其实他们的不同是通过对获取的数据实行不同类型的共享机制来实现,比如添加范围锁、读锁或者立即释放读锁等来实现,但跟排他锁无关。

 

隔离级别添加的共享锁
序列化范围锁
可重复读读锁
提交读立即释放读锁
 

而对于第四种隔离级别:未提交读,一个事务可以看到另一个事务未提交的数据。

 

问题


 

我们看到,当执行不同的隔离级别时,可能会发生各种各样不同的问题。下面对它们进行总结并举例说明。

 

幻读

 

幻读发生在当两个完全相同的查询执行时,第二次查询所返回的结果集跟第一个查询不相同。

 

发生的情况:没有范围锁。

 

例子:

 

事务1事务2
SELECT

* FROM

users
WHERE

age BETWEEN

10

AND

30

;

INSERT

INTO

users VALUES

(

3

, 'Bob'

, 27

)

;

SELECT

* FROM

users WHERE

age BETWEEN

10

AND

30

;

 

 

如何避免:实行序列化隔离模式,在任何一个低级别的隔离中都可能会发生。

 

不可重复读



在基于锁的并行控制方法中,如果在执行select时不添加读锁,就会发生不可重复读问题。

在多版本并行控制机制中,当一个遇到提交冲突的事务需要回退但却被释放时,会发生不可重复读问题。

 

事务1事务2
SELECT

* FROM

users WHERE

id = 1

;

UPDATE

users SET

age = 21

WHERE

id = 1

;
COMMIT; /* in multiversion concurrency*/
control, or lock-based READ COMMITTED *

SELECT

* FROM

users WHERE

id = 1

;

COMMIT; /* lock-based REPEATABLE READ */

 

在上面这个例子中,事务2提交成功,它所做的修改已经可见。然而,事务1已经读取了一个其它的值。在序列化和可重复读的隔离级别中,数据库管理系统会返回旧值,即在被事务2修改之前的值。在提交读和未提交读隔离级别下,可能会返回被更新的值,这就是“不可重复读”。

 

有两个策略可以防止这个问题的发生:

1. 推迟事务2的执行,直至事务1提交或者回退。这种策略在使用锁时应用。

2. 而在多版本并行控制中,事务2可以被先提交。而事务1,继续执行在旧版本的数据上。当事务1终于尝试提交时,数据库会检验它的结果是否和事务1、事务2顺序执行时一样。如果是,则事务1提交成功。如果不是,事务1会被回退。

 

脏读



脏读发生在一个事务A读取了被另一个事务B修改,但是还未提交的数据。假如B回退,则事务A读取的是无效的数据。这跟不可重复读类似,但是第二个事务不需要执行提交。 

 

事务1事务2
SELECT

* FROM

users WHERE

id = 1

;

UPDATE

users SET

age = 21

WHERE

id = 1

SELECT

FROM

users WHERE

id = 1

;

COMMIT; /* lock-based DIRTY READ */

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息