账户脏读问题及解决方法,超详细账户脏读最近上线,使用redission锁定账户,在锁定后,读取账户amount,计算账户金额的加减,然后将计算出的金额存入数据库。1.粗略代码@OverridepublicHandleBalanceResulthandleBalanceAndGiven(longaccountId,BigDecimalhandleBalance,BigIntegerhandleGiven){try{RLocklock=redissonClient.getLock(LOCK_ACCOUNT+accountId);if(lock.tryLock(3*1000,3*1000,TimeUnit.MILLISECONDS)){账户账户=accountRepository.findOne(accountId);account.setBalance(account.getBalance.add(handleBalance));//积分account.setGiven(account.getGiven.add(handleGiven));帐户.保存(帐户);返回trans(account)}catch(InterruptedExceptione){e.printStackTrace();thrownewAccountException("账户操作失败");}finally{if(lock.isHeldByCurrentThread()){lock.unlock();}}}2.问题分析问题产生的原因是账户操作过程中使用了交易。由于事务传播机制,事务是在执行完其他方法后提交的。如果去掉交易的话,我们的业务系统中的用户是很多的。时不时总会出现这样的问题。3、验证上面的代码如果去掉事务功能是没有问题的,但是去掉事务之后,考虑回滚的问题就比较麻烦了。相比之下,不使用分布式锁也可以使用事务,所以不需要考虑事务回滚的问题。当事务中有更新某条记录的特性时,会锁住一行,其他事务无法进入,只能在事务执行完成后执行。下面是验证方法。创建账户表,插入一些数据SETNAMESutf8mb4;SETFOREIGN_KEY_CHECKS=0;----------------------------表结构对于帐户------------------------------如果存在`account`则删除表;创建表`account`(`id`int(11)NOTNULLAUTO_INCREMENT,`name`varchar(255)CHARACTERSETutf8COLLATEutf8_general_ciNULLDEFAULTNULL,`balance`int(11)NULLDEFAULTNULL,PRIMARYKEY(`id`)使用BTREE)ENGINE=InnoDBAUTO_INCREMENT=CHARACTERSET=utf8COLLATE=utf8_general_ciROW_FORMAT=Dynamic;--------------------------------账户记录-------------------------------INSERTINTO`account`VALUES(1,'zhangsan',100);INSERTINTO`account`VALUES(2,'lisi',200);INSERTINTO`account`VALUES(3,'wangwu',300);SETFOREIGN_KEY_CHECKS=1;在Navicat上打开两个窗口,代码如下#Window1startTRANSACTION;updateaccountsetbalance=200whereid=1#window2startTRANSACTION;updateaccountsetbalance=300whereid=1窗口1执行完成后,窗口2会等待窗口1的事务执行完成,然后在窗口1执行COMMIT;或回滚;回滚后,可以执行窗口2。注意where的条件是加锁的,是加到条件的索引中的。如果条件没有索引,则对整个表进行索引。4.解决验证问题交易有锁机制。在事务中,where条件的索引被锁定,只有在其他事务完成后,才能执行相同的where条件事务。然后就可以使用版本号机制了。每执行一条sql语句,都会有一个版本的sql语句。执行一次,就会有另一个版本。当用以前的版本更新另一个sql语句时,更新会失败。例如下面的语句#IamatransactionstartTRANSACTION;从ID=1的账户中选择余额、版本;#这里是1001updateaccountsetbalance=200whereid=1andversion=1;#执行其他的,时间比较短长一点,5s左右,反正足够完成下面的执行#------COMMIT;#我是另一个窗口的交易startTRANSACTION;selectbalance,versionfromaccountwhereid=1;#上面一行加锁更新加锁,读取时正常读取,这里是1001##{version}是上面语句得到的版本,因为上面没有加锁,所以得到的version=1,之前执行下面的语句updateaccountsetbalance=300whereid=1andversion=#{version};#这个地方会被锁定COMMIT;具体代码,注意这是一个User实体,更新user中的信息,与上面sqlUserDaoimportcn.amoqi.springbootjpagradle.entity.User;importorg.springframework.data.jpa.repository.JpaRepository无关;导入org.springframework.data.jpa.repository.Modifying;导入org.springframework.data.jpa.repository.Query;导入org.springframework.stereotype.Repository;导入org.springframework.transaction.annotation.Transactional;导入java。数学.BigDecimal;导入portjava.util.List;@RepositorypublicinterfaceUserDaoextendsJpaRepository
