最近开发一个小程序,遇到一个需要实现分布式事务管理的业务需求。用户可以在使用小程序的同时查看景点,如果想去的话可以标记景点或者城市,那么需要统计一个地方被标记的人数,记录用户是否标记了一个地方想去。使用两张表来存储数据,一个地方表记录一个地方被标记的次数,一个用户意向表记录一个用户是否标记一个地方是想要的。由于可能有多个用户同时标记一个位置,每个用户在前端点击按钮后,后台收到一个请求,从数据库中查询标记某个城市的人数,加1,然后更新到数据库。从数据库中查询标记的人数,加1,然后更新到数据库中。在这个过程中,数据库数据必须加锁,一次只能有一个进程处理。否则,数据将不同步。我用RedLock做分布式锁管理,用spring注解事务管理。在实现过程中,遇到了以下两个深刻的问题:1.分布式锁共享和spring注解事务引起的问题2.事务提交前锁超时问题使用分布式锁RedLock和spring事务实现初始实现代码如下:publicmarkScenicSpot(){//设置锁为destIdRLocklock=redisson.getLock("Afanti_markScenicSpot_updateCountwantAndCountbeenLock_"+ID);//尝试获取锁longlockTimeOut=30;//持有锁超时时间**booleansuccess=lock.tryLock(5,lockTimeOut,TimeUnit.SECONDS);**if(success){try{//业务逻辑实现}catch(Exceptione){throwe;}finally{//释放锁**lock.unlock();**}}else{log.error("获取锁失败!更新失败!");抛出新的BizException(ErrorCodeEnum.PROCESS_DATA_ERROR);}}问题:高并发是锁不生效1、spring注解事务@Transactional和分布式锁不能一起使用,因为@Transactional通过方法是否抛出异常来判断事务是回滚还是提交,以及此时该方法已经结束。但是我们必须在方法结束前释放锁,所以释放锁后,此时还没有提交。由于锁已经释放,其他进程可以获取到锁并从数据库中查询位置标记的个数,但是此时之前的进程还没有提交数据。这个过程查到的数据不是最新的数据。我花了很长时间来调查这个问题,因为从锁的释放到事务的提交只有几毫秒的时间。我一直认为这么短的时间不可能是这里的问题。我心存疑虑,但我又放弃了。耗时很短(我实际测试时这个过程只需要几毫秒),但是在高并发的情况下还是会出现问题。方案一:由于不能使用注解事务,我改成手动事务管理,添加如下代码。publicmarkScenicSpot(){//设置锁为destIdRLocklock=redisson.getLock("Afanti_markScenicSpot_updateCountwantAndCountbeenLock_"+ID);//尝试获取锁longlockTimeOut=30;//持有锁超时booleansuccess=lock.tryLock(5,lockTimeOut,TimeUnit.SECONDS);如果(成功){**DefaultTransactionDefinitiondef=newDefaultTransactionDefinition();def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);//事务隔离级别TransactionStatusstatus=transactionManager.getTransaction(def);//获取事务状态**try{//业务逻辑实现//...**//提交事务transactionManager.commit(status);**}catch(Exceptione){**//回滚事务transactionManager.rollback(status);**}finally{//释放锁lock.unlock();}}else{log.error("获取锁失败!更新失败!");抛出新的BizException(ErrorCodeEnum.PROCESS_DATA_ERROR);}}问题:Locktimeout事情异常1.Locktimeout问题经过手工事务管理,同步问题解决。但是还有一个问题,锁超时了但是事务还没有提交。由于此时当前进程锁超时但没有commit,所以其他进程可以获取到锁并从数据库中查询到目标标记个数,但不是更新后的数据,获取到的数据是错误的。方案二:对于锁超时的情况,只需要在当前进程提交前加一个判断,判断是否超时,如果超时则抛出异常退出。添加如下代码:publicmarkScenicSpot(){//设置锁为destIdRLocklock=redisson.getLock("Afanti_markScenicSpot_updateCountwantAndCountbeenLock_"+ID);//尝试获取锁longlockTimeOut=30;//持锁超时时间booleansuccess=lock.tryLock(5,lockTimeOut,TimeUnit.SECONDS);**//获取锁定时间longgetLockTime=System.currentTimeMillis();**if(success){//事务管理DefaultTransactionDefinitiondef=newDefaultTransactionDefinition();def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);//事务隔离级别TransactionStatusstatus=transactionManager.getTransaction(def);//获取事务状态try{//业务逻辑实现//...//提交事务判断锁是否超时**if(System.currentTimeMillis()-getLockTime
