前提:
由于业务需要在entity Product已经实现了@version的乐观锁的基础上再加上了悲观锁的控制
@Lock(value = LockModeType.PESSIMISTIC_WRITE)
Product findByType(String type);
详情描述:
但是当进行多请求的并发测试的时候发现程序第一个抢占findByType的请求能正常上锁,其它并发请求也正常进入等待,可是当第一个请求修改product并且提交事务释放锁的时候,其它等待请求准备上锁的时候却抛出了乐观锁的错误,为什么会出现这种错误呢?
解答:
JPA 提供的@Lock 不是直接对findByType生成的对应查询语句后面加for update上锁,而是按照下面的顺序来上锁的:
- 执行查询语句: select * from product where type = ? 查询结果(id, type, version): [1001, 'NEW', 1]
- 通过查询出来的id, version在执行以下查询 select * from product where product id = 1001 and version = 1
- 如果步骤2查询有数据返回的话就生成下面的for update的查询SQL上锁, 如果没有就抛出乐观锁异常。 select * from product where product id = 1001 and version = 1 for update
根据@Lock的上述执行原理,当触发多请求并发的时候,所有请求都同步执行了上述1的查询操作,当执行步骤2,3的时候先上锁的请求修改了数据和版本后,后续请求在旧的version就查询不出来数据就抛出了乐观锁异常了。
建议: 一般情况下尽量避免使用悲观锁,对系统开销很大,如果不得已需要用到的话还是直接写for update的SQL直接上锁。