项目使用的是mysql数据库,使用的是mysql默认的repeatable read事务级别, 今天需要讨论的是,用户消费业务账户金额扣除的并发性问题。
消费业务代码肯定是用spring的事务管理的。
一开始的代码是这样的,里面加了一些非sql的东西,帮助理解下业务。
begin;
set @value=select value from test where id=1
//xxxx@p金额的各种校验处理,比如大于零等等
set @newvalue=@value-x //x是消费金额
update test set value=@newvalue where id=1 and value-1>=0;
commit;
看起来毫无问题。先查询账户金额,然后java代码处理扣减后的金额得到newvalue,最后更新newvalue到数据库。
现在问题来了,在并发情况下,两个session同时执行这段代码,假设同时读到@value=1,然后消费1,同时计算出newvalue=0。即时mysql的update是自带排它锁阻塞的,那么两个session还是各自跑完了,但是用户余额却只扣除了1,而不是2!
为了解决这个并发问题,一开始的想法就是加锁,又由于服务器是多个tomcat的,因此sych同步显然不合适,于是使用了redis来作为集群的并发锁,控制消费业务代码,在第一步获取账户余额value的时候就进行了代码锁定,直到消费业务完成才释放锁。
这么做,其实只是保证了数据的安全,但是却大大降低了并发效率,把本