数据库事务

 

数据库的事务隔离级别:

1.Read Commited 可读已提交
   2.Read Uncommited 可读未提交
   3.Repeatable Read 可重复读
   4.Serializable 串行化

sqlserver默认的事务隔离级别是:读可提交。

 

各种并发问题

1. 第一类丢失更新(lost update): 在完全未隔离事务的情况下,两个事物更新同一条数据资源,某一事物异常终止,回滚造成第一个完成的更新也同时丢失。

  事务1 事务2
1 开启事务  
2   开启事务
3 取出数据 age=20  
4   取出数据 age=20
5 更新数据 age=30  
6   更新数据 age=35,并提交事务
7 回滚事务  

 

 

在T1时刻开启了事务1,T2时刻开启了事务2,在T3时刻事务1从数据库中取出了id="402881e535194b8f0135194b91310001"的数据,T4时刻事务2取出了同一条数据,T5时刻事务1将age字段值更新为30,T6时刻事务2更新age为35并提交了数据,但是T7事务1回滚了事务age最后的值依然为20,事务2的更新丢失了,这种情况就叫做"第一类丢失更新(lost update)"。

2. 脏读(dirty read):如果第二个事务查询到第一个事务还未提交的更新数据,形成脏读。

  事务1 事务2
1 开启事务  
2   开启事务
3 取出数据 age=20  
4    
5 更新 age=30  
6   读取数据 age=30(脏读)
7    

 

在T1时刻开启了事务1,T2时刻开启了事务2,在T3时刻事务1从数据库中取出了id="402881e535194b8f0135194b91310001"的数据,在T5时刻事务1将age的值更新为30,但是事务还未提交,T6时刻事务2读取同一条记录,获得age的值为30,但是事务1还未提交,若在T7时刻事务1回滚了事务2的数据就是错误的数据(脏数据),这种情况叫做" 脏读(dirty read)"。

3. 虚读(phantom read):一个事务执行两次查询,第二次结果集包含第一次中没有或者某些行已被删除,造成两次结果不一致,只是另一个事务在这两次查询中间插入或者删除了数据造成的。

  事务1 事务2
1 开启事务  
2   开启事务
3 查询数据,有一条记录  
4   插入一条记录
5    
6   提交事务
7 查询数据,有2条记录  

 

在T1时刻开启了事务1,T2时刻开启了事务2,T3时刻事务1从数据库中查询所有记录,记录总共有一条,T4时刻事务2向数据库中插入一条记录,T6时刻事务2提交事务。T7事务1再次查询数据数据时,记录变成两条了。这种情况是"虚读(phantom read)"。

4. 不可重复读(unrepeated read):一个事务两次读取同一行数据,结果得到不同状态结果,如中间正好另一个事务更新了该数据,两次结果相异,不可信任。

  事务1 事务2
1 开启事务  
2   开启事务
3 查询数据,age=20  
4   查询数据,age=20
5   更新数据,age=30
6   提交事务
7 查询数据,age=30  
8    
9    

 

在T1时刻开启了事务1,T2时刻开启了事务2,在T3时刻事务1从数据库中取出了id="402881e535194b8f0135194b91310001"的数据,此时age=20,T4时刻事务2查询同一条数据,T5事务2更新数据age=30,T6时刻事务2提交事务,T7事务1查询同一条数据,发现数据与第一次不一致。这种情况就是"不可重复读(unrepeated read)"。

5. 第二类丢失更新(second lost updates):是不可重复读的特殊情况,如果两个事务都读取同一行,然后两个都进行写操作,并提交,第一个事务所做的改变就会丢失。

  事务1 事务2
1 开启事务  
2   开启事务
3 更新数据,age=25  
4    
5   更新数据,age=30
6 提交事务  
7   提交事务

 

在T1时刻开启了事务1,T2时刻开启了事务2,T3时刻事务1更新数据age=25,T5时刻事务2更新数据age=30,T6时刻提交事务,T7时刻事务2提交事务,把事务1的更新覆盖了。这种情况就是"第二类丢失更新(second lost updates)"。

 

数据库事务隔离级别

隔离级别 是否出现第一类丢失更新 是否出现脏读 是否出现虚读 是否出现不可重复读 是否出现第二类丢失更新
Serializable
Repeatable Read
Read Commited
Read Uncommited

 

数据库的事务隔离级别越高,越能解决并发中可能出现的问题。但是处理并发的能力就越弱。

也就是说,如果数据库事务采用serializable级别的隔离级别,就不会发生并发的问题。但处理数据的能力非常弱。大部分数据库都采用的是read commited隔离级别。

那怎么来解决并发中可能出现的问题呢?需要用到数据库锁。悲观锁和乐观锁。

悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。

一个典型的悲观锁的调用:select * from users where name=’leo’ for update.

  取款事务 存款事务
1 开始事务  
2   开始事务
3 select * from accounts where id=1 for update;
查询结果显示存款余额为1000元,这条记录被锁定
 
4   select * from accounts where id=1 for update;执行该语句时,事务停下来等待取款事务解除对这条记录的锁定。
5 取出100元。把存款余额改为900元。  
6 提交事务  
7   事务回复运行,查询结果显示存款余额为900元。这条记录被锁定。
8   汇入100元,把存款余额改为1000元。
9   提交事务

 

使用乐观锁时,可以在表中添加一个字段:version。

但读数据时,连同version一同读出。在update时,将version+1,然后与数据库中的version比较。如果大于数据库中的版本,执行update。如果等于或小于数据库中的version,那么就是脏数据。不执行。

posted @ 2014-04-24 14:38  Leo_Lei  阅读(326)  评论(0编辑  收藏  举报