事务并发(乐观锁)

一、 事务4个特性ACID

  • 原子性(Atomic),事务必须是原子工作单元;
  • 一致性(Consistent),事务在完成时,必须使所有的数据都保持一致状态。
  • 隔离性(Insulation),由事务并发所作的修改必须与任何其它并发事务所作的修改隔离。
  • 持久性(Duration),事务完成之后,它对于系统的影响是永久性的。

二、事务并发

  • 通常为了获得更好的运行性能,各种数据库都允许多个事务同时运行,这就是事务并发。

三、 隔离机制

  • 当并发的事务访问或修改数据库中相同的数据(同一行同一列)时,通常需要采取必要的隔离机制。
  • 解决并发问题的途径是什么?
    • 答案是:采取有效的隔离机制。
  • 怎样实现事务的隔离呢?隔离机制的实现必须使用锁

四、事务并发带来的问题

  • 以下事务都是发生在毫秒级的时间差
  • JPA只能处理第一、二类丢失更新,其他3种必须由数据库自己处理

1.第一类丢失更新:(在秒杀场景会出现问题)

  • 库存是1件
  • 当事务A和事务B同时修改某行的值,
  • 1.事务A将数值改为0并提交,购买了一件
  • 2.事务B将数值改为0并提交,也购买了一件。这时数据的值为0,事务A所做的更新将会丢失。(相当于就卖出去2件商品)
  • 解决办法:对行加锁,只允许并发一个更新事务。(JPA中的悲观锁,乐观锁)

2.脏读

  • 1.张三的原工资为4000, 财务人员将张三的工资改为了8000(但未提交事务)
  • 2.张三读取自己的工资 ,发现自己的工资变为了8000,欢天喜地!(在缓存中读取)
  • 3.而财务发现操作有误,回滚了事务,张三的工资又变为了4000 像这样,张三记取的工资数8000是一个脏数据。
  • 解决办法:如果在第一个事务提交前,任何其他事务不可读取其修改过的值,则可以避免该问题。

3.虚读(幻读)

  • 目前工资为4000的员工有10人。
  • 1.事务1,读取所有工资为4000的员工。
  • 2.这时事务2向employee表插入了一条员工记录,工资也为4000
  • 3.事务1再次读取所有工资为4000的员工共读取到了11条记录,
  • 解决办法:如果在操作事务完成数据处理之前,任何其他事务都不可以添加新数据,则可避免该问题。

4.不可重复读

  • 在一个事务中前后两次读取的结果并不致,导致了不可重复读。
  • 1.在事务1中,Mary 读取了自己的工资为1000,操作并没有完成
  • 2.在事务2中,这时财务人员修改了Mary的工资为2000,并提交了事务.
  • 3.在事务1中,Mary 再次读取自己的工资时,工资变为了2000
  • 解决办法:如果只有在修改事务完全提交之后才可以读取数据,则可以避免该问题。

5.第二类丢失更新

  • 多个事务同时读取相同数据,并完成各自的事务提交,导致最后一个事务提交会覆盖前面所有事务对数据的改变

五、数据库事务隔离级别(使用数据库默认的隔离级别就可以了)

SERIALIZABLE隔离级别最高,效率最低
一般都不修改隔离级别,如果要改,问dba(数据库管理员)
在这里插入图片描述

六、悲观锁

  • 悲观锁(处理的是同一张表的同一行同一列记录),用户体验非常差

  • 如果使用了悲观锁(加了一个行锁),如果事务没有被释放,就会造成其他事务处于等待
    不用,不用哦!!!!

  • 使用数据库提供的锁机制实现悲观锁。
    如果数据库不支持设置的锁机制,JPA会使用该数据库提供的合适的锁机制来完成,而不会报错。

  • 使用entityManager.find(class,id,LockModeType);加悲观锁,相当于发送SELECT … FOR UPDATE(加了一个行锁)

  • 使用entityManager.lock(object,LockModeType);加悲观锁,相当于发送SELECT id FROM … FOR UPDATE(加了一个行锁)

七、乐观锁(处理的还是同一张表的同一行同一列记录)

在这里插入图片描述

1.Version方式(整数,存储空间小)

// 添加一个私有字段Integer version,不由程序员维护,由JPA自己维护
  @Version
  private Integer version;

2.秒杀场景:库存预设1个

真正的秒杀肯定是一件一件的商品进行购买

3.事务1,事务2交替运行

// 事务操作流程:先查询,获取库存,在购买,再更新
// 事务1,事务2交替运行
@Test
public void update2() throws Exception {
  try {
    EntityManager entityManager2 = JPAUtils.getEntityManager();
    EntityManager entityManager = JPAUtils.getEntityManager();
    entityManager2.getTransaction().begin();
    entityManager.getTransaction().begin();
    Product product = entityManager.find(Product.class, 1L);
    Product product2 = entityManager2.find(Product.class, 1L);
    product2.setNum(product2.getNum() - 1);
    entityManager2.merge(product2);

    product.setNum(product.getNum() - 1);
    entityManager.merge(product);

    entityManager.getTransaction().commit();
    entityManager.close();

    entityManager2.getTransaction().commit();
    entityManager2.close();
  } catch (StaleObjectStateException e) {// 乐观锁异常
    EntityManager entityManager3 = JPAUtils.getEntityManager();
    Product product3 = entityManager3.find(Product.class, 1L);
    System.out.println("库存已经改变,最新库存:" + product3.getNum());
    entityManager3.close();
  }
}

如果出现乐观锁异常,捕获StaleObjectStateException异常

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Spring Boot中使用Redis实现乐观锁处理并发情况,可以通过使用Redis的命令来实现。 乐观锁是一种乐观思想,基于数据版本的控制来实现并发控制。在Redis中,可以使用WATCH命令来实现乐观锁并发控制。 在业务代码中,可以首先使用WATCH命令监视需要控制并发的数据的版本号。然后在确认无并发情况后,使用MULTI命令开启事务,执行需要的操作,并在最后通过EXEC命令提交事务。 Redis在执行事务期间,如果被监视的数据的版本号发生了变化,那么会放弃执行事务,并返回nil给客户端。这样就能确保在事务执行期间,数据不会被其他客户端修改。 下面是一个示例代码: ``` @Autowired private RedisTemplate redisTemplate; public void optimisticLock(String key) { boolean success = false; while (!success) { redisTemplate.watch(key); // 监视key String value = (String) redisTemplate.opsForValue().get(key); // 进行业务逻辑判断 if (businessLogic(value)) { redisTemplate.multi(); // 开启事务 // 执行需要的操作 redisTemplate.opsForValue().set(key, newValue); List<Object> results = redisTemplate.exec(); // 提交事务 if (results != null) { success = true; // 事务执行成功 } } else { redisTemplate.unwatch(); // 放弃监视 break; } } } ``` 通过使用Redis的WATCH命令和事务操作,可以实现乐观锁并发控制,确保在执行事务期间数据不会被其他客户端修改,从而解决并发问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值