并发事务之丢失更新

7 并发事务问题之丢失更新

  丢失更新:一个事务的更新被另一个事务的更新覆盖了;


时间点


事务1


事务2


t1?


开始事务

?

t2?

?
开始事务


t3?


查询pid=p1的记录结果为[pid=p1,pname=zhangSan,age=23,sex=male]

?

t4

?
查询pid=p1的记录结果为[pid=p1,pname=zhangSan,age=23,sex=male]


t5?


修改age=24,其它保留原值,即:

update person set pname=‘zhangSan‘,

age=24,sex=‘male‘ where pid=‘p1‘;

?

t6?


提交事务

?

t7?

?
修改sex=female,其它保留原值

update person set pname=‘zhangSan‘,

age=23,sex=‘female‘ where pid=‘p1‘;


t8

?
提交事务

?

事务2覆盖了事务1的更新操作。结果为:[pid=p1,pname=zhangSan,age=23,sex=female]。因为事务2没有在事务1的基础上进行更新,而是在自己的查询基础上进行更新。

?


public
class Demo1 {

????private
static Connection getConnection() throws Exception {

????????String driverClassName = "com.mysql.jdbc.Driver";

????????String url = "jdbc:mysql://localhost:3306/day12?useUnicode=true&characterEncoding=utf8";

????????String username = "root";

????????String password = "123";

?

????????Class.forName(driverClassName);

????????return DriverManager.getConnection(url, username, password);

????}

?

????public Person load(Connection con, String pid) throws Exception {

????????String sql = "select * from t_person where pid=?";

????????PreparedStatement pstmt = con.prepareStatement(sql);

????????pstmt.setString(1, pid);

????????ResultSet rs = pstmt.executeQuery();

????????if (rs.next()) {

????????????return
new Person(rs.getString(1), rs.getString(2), rs.getInt(3),

????????????????????rs.getString(4));

????????}

????????return
null;

????}

????

????public
void update(Connection con, Person p) throws Exception {

????????String sql = "update t_person set pname=?, age=?, gender=? where pid=?";

????????PreparedStatement pstmt = con.prepareStatement(sql);

????????pstmt.setString(1, p.getPname());

????????pstmt.setInt(2, p.getAge());

????????pstmt.setString(3, p.getGender());

????????pstmt.setString(4, p.getPid());

????????

????????pstmt.executeUpdate();

????}

????

????@Test

????public
void fun1() throws Exception {

????????Connection con = getConnection();

????????con.setAutoCommit(false);

????????
?

//[pid=p1,pname=zs,age=24,gender=male]

????????Person p = load(con, "p1");

????????p.setAge(42);//断点

????????update(con, p);

????????

????????con.commit();

????}

????@Test

????public
void fun2() throws Exception {

????????Connection con = getConnection();

????????con.setAutoCommit(false);

//[pid=p1,pname=zs,age=24,gender=male]

????????Person p = load(con, "p1");

????????p.setGender("female");//断点

????????update(con, p);

????????

????????con.commit();

????}

}

?

处理丢失更新:

  • 悲观锁:在查询时给事务上排他锁,这可以让另一个事务在查询时等待前一个事务解锁后才能执行;
  • 乐观锁:给表添加一个字段,表示版本,例如添加version字段,比较查询到的version与当前vesion是否相同;

?

7.1 悲观锁解决丢失更新

只需要修改上面代码的load()方法中select语句即可:

select * from t_person where pid=? for update

?


????public Person load(Connection con, String pid) throws Exception {

????????String sql = "select * from t_person where pid=? for update";

????????PreparedStatement pstmt = con.prepareStatement(sql);

????????pstmt.setString(1, pid);

????????ResultSet rs = pstmt.executeQuery();

????????if (rs.next()) {

????????????return
new Person(rs.getString(1), rs.getString(2), rs.getInt(3),

????????????????????rs.getString(4));

????????}

????????return
null;

????}

?

悲观锁:悲观的思想,认为丢失更新问题总会出现,在select语句中添加for update为事务添加排他锁,这会让其他事务等待当前事务结束后才能访问。当然,其他事物的select语句中也要加上for update语句才会等待;

悲观锁的性能低!

?

7.2 乐观锁

乐观锁与数据库锁机制无关;

我们需要修改t_person表,为其添加一个字段表示当前记录的版本。例如给t_person表添加version字段,默认值为1。

当事务查询记录时得到version=1,再执行update时需要比较当前version的值是否与查询到的version相同,决定update是否执行成功。如果update成功,还要把version的值加1。


????public
void update(Connection con, Person p) throws Exception {

????????String sql = "update t_person set pname=?, age=?, gender=?, version=version+1 where pid=? and version=?";

????????PreparedStatement pstmt = con.prepareStatement(sql);

????????pstmt.setString(1, p.getPname());

????????pstmt.setInt(2, p.getAge());

????????pstmt.setString(3, p.getGender());

????????pstmt.setString(4, p.getPid());

????????pstmt.setInt(5, p.getVersion());

????????

????????pstmt.executeUpdate();

????}

?

  • 事务1:查询时得到version=1;
  • 事务2:查询时得到version=1;
  • 事务1:执行update时因为version没有改变,所以update执行成功,update不只修改了age=42,还修改了version=2;
  • 事务2:执行update语句时version已经为2,而查询时的version为1,所以update执行失败;

?

乐观锁:与数据库锁机制无关,乐观的思想,认为丢失更新不是总出现;通过给表添加版本字段来决定update操作是否成功。即查询时和更新时的版本必须一致!

时间: 2024-08-02 15:32:48

并发事务之丢失更新的相关文章

并发事务的丢失更新及数据锁机制

在事务的隔离级别内容中,能够了解到两个不同的事务在并发的时候可能会发生数据的影响.细心的话可以发现事务隔离级别章节中,脏读.不可重复读.幻读三个问题都是由事务A对数据进行修改.增加,事务B总是在做读操作.如果两事务都在对数据进行修改则会导致另外的问题:丢失更新.这是本博文所要叙述的主题,同时引出并发事务对数据修改的解决方案:锁机制. 1.丢失更新的定义及产生原因. 丢失更新就是两个不同的事务(或者Java程序线程)在某一时刻对同一数据进行读取后,先后进行修改.导致第一次操作数据丢失.可以用图表表

数据库并发事务控制 一:综述

并发控制是DBMS的关键技术 对数据库的操作都是在事务中进行的. 事务是指一组相互依赖的操作行为.事务中的操作是不可分割的工作单元,由一组在业务逻辑上相互依赖的SQL语句组成,有ACID特征. Atomic(原子性):事务中包含的操作被看做一个逻辑单元,这个逻辑单元中的操作要么全部成功,要么全部失败. Consistency(一致性):只有合法的数据可以被写入数据库,否则事务应该将其回滚到最初状态. Isolation(隔离性):事务允许多个用户对同一个数据进行并发访问,而不破坏数据的正确性和完

SqlServer 并发事务(一):事务隔离级别

--查了当前数据库是事务隔离级别 DBCC USEROPTIONS 表初始内容: SELECT * FROM Test  Wherename='kk' id  name   info 1   kk      NULL [测试一:丢失更新] --事务1 begin tran select * from dbo.Test(nolock) where name = 'kk' waitfor delay '00:00:05' update T set info = 'A更改' from Test T(n

Spring 并发事务的探究

前言 在目前的软件架构中,不仅存在单独的数据库操作(一条SQL以内,还存在逻辑性的一组操作.而互联网软件系统最少不了的就是对共享资源的操作.比如热闹的集市,抢购的人群对同见商品的抢购由一位售货员来处理,这样虽然能保证买卖的正确进行,但是牺牲了效率,饱和的销售过程并不能高效处理所有的购买请求,最后打烊了部分顾客悻悻而归.而电脑的发明是让人类解放于这种低效的工作中,提高销售性能,比如抢购系统,秒杀系统等.而这种销售过程必然包含了检查库存.秒杀排队.校对商品信息.下单等一系列的组合操作,而一个交易过程

SqlServer 并发事务(二):锁粒度和锁模式

锁粒度: 资源 格式 说明 DATABASE 不适用 resource_database_id 列中已提供数据库 ID. FILE <file_id> 此资源所表示的文件 ID. Object <object_id> 此资源所表示的对象 ID. 此对象可以是sys.objects 中列出的任何对象,不仅仅是表. PAGE <file_id>:<page_in_file> HoBt ID.此值与 sys.partitions.hobt_id 相对应. PAGE

如何利用hibernate3解决数据库丢失更新问题?

首先我们要明白什么叫丢失更新. 比如数据库有一个person表,里面有一条这样的数据 "5 zhangsan shenzhen"; 现在有两个事务A.B同时查找了这一条记录: A事务修改记录为"5 zhangsan beijing"; 提交事务 B事务修改记录为"5 lisi shenzhen"; 这时候B事务再去提交或者回滚数据就会出现覆盖A事务已经修改的内容,这种情况就叫做丢失更新. hibernate3中的解决方法:利用悲观锁.乐观锁 悲观

day18 12.丢失更新介绍与悲观锁

共享锁在一条记录上是可以加多个的,共享嘛.排它锁的意思是指这条记录上如果有任何其他的锁我排它锁是加不上的,有了排它锁其他锁也是加不上的,唯一的.比如说现在我的记录上没锁,加了排它锁其他人使用不了,我这排它锁没完事.如果这条记录上已经有共享锁了,再加排它锁加不了,只能在上面加一种锁. A事务在用共享锁,B事务用共享锁可以访问;A事务如果想更换共享锁为排它锁,得等到B事务提交之后才可以更换(一条记录上了一种锁之后不能再上别的锁,除非事务之间的交互结束).如果A事务用的是共享锁,没有B事务使用共享锁那

并发事务 可能导致的问题

并发事务可能引起的事情: 1.脏读: 对于两个事务T1 和T2 , T1 读取了已经被T2 更新但还没有被提交的字段.之后,若T2 进行回滚,T1读取的内容就是临时且无效的 2.不可重复读: 对于两个事务T1 和T2 , T1 读取了一个字段,然后T2 更新了该字段.之后,T1再次读取同一个字段,值就不同了 3.幻读: 对于两个事务T1,T2,T1从表中都去了一个字段,然后T2在该表中插入了一些新的行,之后,如果T1再次读取同一个表,就会多出几行 ------------------------

“丢失更新” 两种情况分析!

第一类丢失更新 A事务撤销时,把已经提交的B事务的更新数据覆盖了.这种错误可能造成很严重的问题,通过下面的账户取款转账就可以看出来: 时间 取款事务A 转账事务B T1 开始事务 T2 开始事务 T3 查询账户余额为1000元 T4 查询账户余额为1000元 T5 汇入100元把余额改为1100元 T6 提交事务 T7 取出100元把余额改为900元 T8 撤销事务 T9 余额恢复为1000 元(丢失更新) A事务在撤销时,"不小心"将B事务已经转入账户的金额给抹去了. 第二类丢失更新