分布式锁与 Spring 事物管理顺序问题

这个是网上的网友提问,我给他推理并解决了他的问题。故此记录一下 。

他在使用zookeeper的临时顺序节点分布式锁来修改mysql中的数据值,此功能类似于售票机制,高并发下,要求售票数量保持一致。不可以数量多或少。

使用zookeeper的临时顺序节点分布式锁的时候,他锁住service层时,执行完整个service方法时,锁释放,但此时方式上标注的spring 管理的声明式事物却还未提交。这样的场景在并发情况下会导致数据问题。

理所当然他的数据库中数据会出现异常情况。100个线程同时执行1次一次拿1张,100张票应该为剩余0,而他的结果却并不是0,没有达到预期值。

原理是:第一个线程执行update方法结束之后,但由于事物还未提交完成。分布式锁却已经先一步释放,下一个线程执行此方法时 select 获取的还是update之前的脏读数据。导致数据出现问题。因为spring声明式事物@Transactional(rollbackFor = Exception.class) 是基于aop,方法执行完成之后的再执行的环绕通知,但环绕通知提交事务时,zookeeper的锁却已经释放。这个先后顺序差异导致下一个线程读到脏数据,数据处理产生错误。

所以要修改 spring事物和分布式锁释放的先后顺序。先提交事务之后,再释放掉分布式锁或直接锁controller层就可以避免此问题。

这里再多说一句,分布式锁不能保证数据完整性,只能保证当前业务层service方法的唯一调用。

如果其他service方法中有mapper也在处理这一条数据,那么数据将还是会出现问题。

所以理应给表中添加乐观锁字段来保证数据的完整性。乐观锁可以很好的保持数据的完整性,并且并发性能高于他使用的service加锁。