本文共 1360 字,大约阅读时间需要 4 分钟。
主要是原因:
1、MySQL的事务隔离机制为:重复读(主因)
2、分布式锁的释放在事务提交前(这个并发高时,时间差也是出现问题的源点)
排查代码(分布式锁+事务下面代码不是很合理)如下:
发现事务service中,获得redis分布式锁后,进行了数据库查询,根据数据查询出的结果进行了限制,同时事务提交前的finally中进行了提前释放分布式redis锁。
1、先说第一个分布式锁中进行数据库查询根据返回count进行处理的问题:
查看数据的事务隔离级别,SELECT @@tx_isolation;发现系统中是repeatable read(重复读),MySQL默认。
重复读:是事务开始时,便会创建快照(时间点 row trx_id,如果事务中有多个表,表查询的快照点都是事务中操作第一张表的时间点的快照),在同一个事务中查询同样的语句,返回的结果一致。MySQL是采用MCVV视图机制实现重复读
InnoDB 在实现 MVCC(数据库的多版本并发控制) 时用到的一致性读视图,即 consistent read view,用于支持 RC(Read Committed,读提交)和 RR(Repeatable Read,可重复读)隔离级别的实现。
更多的重复读以及回滚,可以参考
出现没控制住的原因进程图如下:
事务A开启时,另外一个事务B未结束,当B释放锁提交事务,将坑位占用剩余0,此时事务A中恰好拿到锁,但是查询数据库时,坑位还是事务开始时的剩余1个(可重读的隔离级别下,B事务的提交数据对A不可见),所以导致1个坑位2个人占的情况。
解决办法,将隔离级别改为:读提交。
2、分布式锁在事务提交前进行释放的问题:
事务是在service方法中,将释放锁的代码上提到controller层进行处理,这样就是事务提交后再释放锁。
3.redis中setIfAbsent、exprie两个方法是两行代码,这样会出现设置的key长久存在问题,需要放同一个事务中,不过这个问题在redis2.1版本后,是一行代码解决。
4.更多的分布式锁的使用可以参看另外一篇文章分布式锁的使用:
------------------------------------------------------------------------------------
数据库改为读重复的隔离级别后,线上又出现了并发控制不住的情况又出现了,分析还是分布式锁释放后,另外一个线程立即拿到了锁,进行查询,但是mysql事务提交处理时间(特别开启binlog的情况,是2次确认提交的),如果抢购时需要处理的事务也多,耗时会长一些。
之前分布式锁释放到controller,延迟1秒释放锁,这个不通用,也会出现时间错差的问题。
解决办法,将控制数量的值放到redis中,数据的增加放到分布式锁中,校验以及增加数据都是在分布式锁中处理,这样可以保证锁释放时,控制数量值立即变化。这样就是业务需要修改的代码多一些,还是需要提前做好设计。控制并发还是需要redis分布式锁内处理完数量或者mysql乐观锁。
修改办法:校验处取值redis,分布式锁释放前,增加redis中数量的值
牵扯到预约的逆向,需要控制变量减少数量的业务代码,需要处理多处,。。。
转载地址:http://fwodi.baihongyu.com/