学习内容来自于腾讯课堂 图灵老师
stringRedisTemplate.opsForValue().get(‘key’) 通过key获取redis库存数量时,在高并发场景下会出现同一个库存被多次获取的情况。而synchronized锁是进程锁,只针对同一个jvm情况下,也就是在分布式,或者说相同war部署了多个Tomcat的时候,synchronized锁并不能解决以上库存被多次获取的情况。这里有个疑问就是redis是线程安全的,或者说他是单线程的,怎么会出现库存被多少获取呢,因为在业务代码上,一般首先我们是先通过stringRedisTemplate.opsForValue().get(‘key’)获取值,再通过stringRedisTemplate.opsForValue().set(‘key’,value)修改原先的值,而在高并发的时候,获取redis的key值时,同一个库存会被多次获取。
此时,通过stringRedisTemplate.opsForValue().setIfAbsent(‘key’,value)(注:这是springBoot集成Redis的,以上stringRedisTemplate都是。)实现redis分布式锁,想当于redis的setnx方法。方法含义分别如下:
redis命令
stringRedisTemplate命令
,这里又因为redis是线程安全的,所以在设置stringRedisTemplate.opsForValue().setIfAbsent (‘key’,value)时,高并发场景下已队列的形式在等待,假如key不存在的话,返回true,我们使业务往下继续执行,假如key存在的话,则结束这块业务。
下面新的问题来了,当业务块发生异常时,要释放库存,我们需要在finally中关闭锁,如下
另一个场景便是我们要考虑,当服务器宕机或者说当运维重启服务器时,此时刚好创建的redis分布式锁,但是下面的业务没有实现的情况下,需要释放锁(注:其他业务涉及数据库,可开启事务),如下,我们需要给redis库对应的值通过stringRedisTemplate.opsForValue().setIfAbsent (‘key’,value,time,timeType)设置失效时间
再一个场景是,一般上我们设置分布式锁的key,是通过由前端的传来的商品主键ID,作为key值,在高并发场景下,必然会出现相同的key值,而我们设置key值的有效时间不足,代码块业务还未走完,比如我们设置了失效时间为10秒,但是代码最终走到finally中时花了15秒时间,此时第二个线程相同的key也许才刚刚创建,就被第一个线程中的finally中stringRedisTemplate.delete(‘key’)方法清理了。这样导致第二个线程或者其他线程的分布锁失效,所以我们要给对应的分步锁添加唯一value值。如下
另Redisson实现方式