标签:
在事务的隔离级别内容中,能够了解到两个不同的事务在并发的时候可能会发生数据的影响。细心的话可以发现事务隔离级别章节中,脏读、不可重复读、幻读三个问题都是由事务A对数据进行修改、增加,事务B总是在做读操作。如果两事务都在对数据进行修改则会导致另外的问题:丢失更新。这是本博文所要叙述的主题,同时引出并发事务对数据修改的解决方案:锁机制。
丢失更新就是两个不同的事务(或者Java程序线程)在某一时刻对同一数据进行读取后,先后进行修改。导致第一次操作数据丢失。可以用图表表示如下:
时间点 | 事务A | 事务B |
1 | start transcaction; | |
2 | start transcaction; | |
3 | update t_customer ts set ts.name=‘张三‘ where ts.id=‘10‘; | |
4 | update t_customer t set t.name=‘李四‘,t.age=20 where ts.id=‘10‘ | |
5 | commit; | |
6 | commit; |
假如原来t_customer表内id为10的行,是一条{id:10,name:"王五",age:15} 的数据,经过事务A修改后变成{id:10,name:"张三",age:15}。事务B提交后,该数据变成了{id:10,name:"李四",age:20}。由事务A所执行的操作在事务B的提交后,数据被冲掉了。这个现象就叫做丢失更新。
备注:事实上Mysql数据库会在事务里面默认添加写锁,上面的现象是没法重现的。了解即可。丢失更新就是由并发修改数据造成的。如下图所示:
(1) 不使用事务。[最不可能的方法]
(2)使用数据库锁机制防止丢失更新
解决丢失更新的方法有好几个,先来了解下数据库里面的"锁"。从数据库功能上面来看,数据库设计上分为两种锁:读锁(共享锁)和写锁(排它锁)。
数据库在设计这两种锁的时候,这两种锁间的关系如下:读锁与读锁可以共存,读锁与写锁互斥,写锁与写锁互斥。(这种设计跟Java线程锁机制是一样的)。
使用数据库添加读锁和写锁的方法很简单 , 但需要注意的是锁必须在事务内进行声明。在事务外声明的锁将不具备效应。
为表添加读锁的方法:select * from t_account lock in share mode; (读锁与他人共享读操作,很容易导致死锁。)
为表添加写锁的方法:select * from t_account for update;
在JDBC这块,事务一般通过sql脚本去控制处理。因此可以在sql脚本处添加上锁去进行控制。(后面会整理通过ORM框架:hibernate对事务进行控制)
解决丢失更新,主要使用两种方法:
1. 使用排它锁。经过上面基于数据库锁的介绍可知,丢失更新可以使用写锁(排它锁)进行控制。因为排它锁添加到某个表的时候,事务未经提交,其他的事务根本没法获取修改权,因此排它锁可以用来控制丢失更新。需要说明的是有时候,当知道某一行会发生并发修改的时候,可以把锁定的范围缩小。例如使用select * from t_account t wheret.id=‘1‘ for update; 这样能够比较好地把控上锁的粒度,这种基于行级上锁的方法叫"行级锁"。
2. 使用乐观锁。
乐观锁的原理是:认为事务不一定会产生丢失更新,让事务进行并发修改,不对事务进行锁定。发现并发修改某行数据时,乐观锁抛出异常。让用户解决。可以通过给数据表添加自增的version字段或时间戳timestamp。进行数据修改时,数据库会检测version字段或者时间戳是否与原来的一致。若不一致,抛出异常。
时间点 | 事务A | 事务B | version值 |
1 | |||
1 | 提出修改 :update t_account t set t.money=t.money+100 where t.name =‘a‘ | 提出修改 :update t_account t set t.money=t.money+100 where t.name =‘a‘ | 1 |
2 | commit; | 校验事务A与version值,version字段值都为"1",通过提交内容,version字段值更新为2 | |
3 | commit; |
校验事务B与version值,事务B提交前的version字段值为1,但当前version值为2,禁止事务B提交.抛出异常让用户处理 |
时间戳与version的判断都一样,时间戳记录的是当前时间。提交前数据库会校验提交前的时间戳是否与当前的一致。若一致则更新时间戳。
以上为本次对数据库并发更新的总结,后面补充在ORM框架下如何上锁。
标签:
原文地址:http://www.cnblogs.com/doucheyard/p/5662171.html