JPA @Lock(value = LockModeType.PESSIMISTIC_WRITE) 悲观锁防坑

前提:

由于业务需要在entity Product已经实现了@version的乐观锁的基础上再加上了悲观锁的控制


@Lock(value = LockModeType.PESSIMISTIC_WRITE)

Product findByType(String type);


详情描述:

但是当进行多请求的并发测试的时候发现程序第一个抢占findByType的请求能正常上锁,其它并发请求也正常进入等待,可是当第一个请求修改product并且提交事务释放锁的时候,其它等待请求准备上锁的时候却抛出了乐观锁的错误,为什么会出现这种错误呢?

解答:

JPA 提供的@Lock 不是直接对findByType生成的对应查询语句后面加for update上锁,而是按照下面的顺序来上锁的:

  1. 执行查询语句: select * from product where type = ? 查询结果(id, type, version): [1001, ‘NEW’, 1]
  2. 通过查询出来的id, version在执行以下查询 select * from product where product id = 1001 and version = 1
  3. 如果步骤2查询有数据返回的话就生成下面的for update的查询SQL上锁, 如果没有就抛出乐观锁异常。 select * from product where product id = 1001 and version = 1 for update

根据@Lock的上述执行原理,当触发多请求并发的时候,所有请求都同步执行了上述1的查询操作,当执行步骤2,3的时候先上锁的请求修改了数据和版本后,后续请求在旧的version就查询不出来数据就抛出了乐观锁异常了。

建议: 一般情况下尽量避免使用悲观锁,对系统开销很大,如果不得已需要用到的话还是直接写for update的SQL直接上锁。

发表评论

邮箱地址不会被公开。 必填项已用*标注