乐观锁 悲观锁 及 幻读(虚读)情境处理

乐观锁:冲突检测

悲观锁:冲突避免

 

乐观锁是在 这条记录保存到数据库中时进行版本检测,也就是说这条记录 进行的操作是 update 时,乐观锁才会产生效果

 

 幻读(虚读)三种情况:

1、人员表的身份证字段在做唯一检查的时候满足条件,但在做后续 save操作完成之前,该表新增了一条相同身份证的记录,导致数据库中出现2条身份证相同的记录。

解决:

是否可以通过 悲观锁的 读锁 来解决呢?

//PO中
@Table(indexes = {
        @Index(name="IDX_BookShelf_Owner_Name",columnList = "owner",unique = true),
        @Index(name="IDX_BookShelf_Owner_Name",columnList = "name",unique = true),
})


//Repository中
@Lock(LockModeType.PESSIMISTIC_READ)
public long countByNameAndOwner(String name,String owner);

 悲观锁很容易造成死锁。

我们思考下这种场景:

事务A,第一步 检查身份证是否有11111的记录,第二步 如果没有则将身份证修改为22222。

事务B,第一步 检查身份证是否有22222的记录,第二步 如果没有则将身份证修改为11111。

事务A执行检查时,会为所有身份证是否有11111的记录加上读锁,该读锁同时也会阻止 所有通过新增或修改方式让身份证变为11111的写操作。

事务B执行检查时,会为所有身份证是否有22222的记录加上读锁,该读锁同时也会阻止 所有通过新增或修改方式让身份证变为22222的写操作。

如果事务A执行完检查,事务B也执行完检查,则 任何将身份证变为11111 或 22222 的操作都会被阻止。

事务A的第二步在等事务B释放读锁,事务B的第二步在等事务A释放读锁,造成死锁。

 

那这种幻读究竟怎么处理好呢?

我的做法是 加 唯一索引,然后捕获 commit失败时候的异常,处理后返回给前端。

 

2、某人银行余额500,存100块。此时从数据库中取出余额记录为500,系统计算500+100=600。

要将600存入数据库前,这个人同时又从银行取走了500,照道理数据库中余额应该是100,但实际保存到数据库的余额是600。

解决:

  • 如果通过悲观锁的写锁来处理,处理起来方便,但它会阻止任何 带了读锁的select语句 访问该记录。
  • 如果通过乐观锁来处理,处理起来会麻烦些,但效率方面会好一点。

延伸:根据业务确定 你的系统中有多少重要字段需要 进行这种保护。绝大多数情况下,要么这个字段在业务场景下不太可能有这么大的并发,要么这个字段本身没有这么重要

 

3、银行系统有一张负债表和一张发放贷款表,负债高于100W的人不允许发放贷款。某个人向银行贷款,检查负债表时,负债没高于100W,银行系统准备往贷款表更新数据了。

但这时候,此人的贷款突然激增超过100W,结果银行系统还是给这个人发放了贷款。

解决:

  • 这种情况能用乐观锁处理吗? 不能!因为最后只会往贷款表中更新数据,比对的是贷款表中的记录version,负债表根本不会去比对,所以乐观锁不能发现这个问题。
  • 悲观锁可以处理这个问题。在查负债表的时候,加上读锁,防止负债表该记录被修改,就可以解决这个问题。

延伸:这个问题和第2个问题的不同之处在于,第2个问题是要update的那条记录本身被修改导致的问题,而这个问题是其他表的记录被修改导致的问题。

 

 

posted on 2018-03-04 20:19  HB1  阅读(316)  评论(0)    收藏  举报

导航