基于redis的分布式锁的实现

it2025-12-05  15

问题研究

如今互联网越来越发达,应用不可能是单服务部署,而是集群分布式,及一个应用部署成多个微服务,通过nginx负载的形式去实现分布式。但是就会出现分布式下高并发场景出现的bug,例如一个商品在促销情况下发生超出我们设定的商品数量的bug。如何去解决这样的分布式问题呢?答案就是分布式锁,因为java自带的锁只能解决当前这个节点上锁的问题。

互联网厂商常用的分布式锁通过redis来实现

setnx 这个命令的意思就是当这个key不存在的时候才能创建,否则是不能赋值的。

下面的代码使用redisTemplate

public class ConsumerController{ @Autowired SpringRedisTemplate springRedisTemplate; public Object buyGoods(){ //加锁操作 boolean result= springRedisTemplate.opsForValue.setIfAbsent("lockKey","value"); if(result){ //这里是对商品的购买,对商品的剩余库存进行删减 }else{ return 一个提示信息 } //用户操作完成释放锁 springRedisTemplate.delete("lockKey"); } }

上述代码存在的问题:

当用户的购买操作中出现了异常,例如redis 超时等异常,这时候锁就不能被释放出来,导致后续的购买操作都不能正常购买

通过finnally去解决上述问题

public class ConsumerController{ @Autowired SpringRedisTemplate springRedisTemplate; public Object buyGoods(){ try{ //加锁操作 boolean result= springRedisTemplate.opsForValue.setIfAbsent("lockKey","value"); if(result){ //这里是对商品的购买,对商品的剩余库存进行删减 }else{ return 一个提示信息 } }finally{ //如果发生异常就直接释放锁 springRedisTemplate.delete("lockKey"); } } }

上述代码存在的问题:

如果一个用户购买过程中,服务器发生了宕机,finally并没有执行,redis库中还有这把锁, 那还是会造成用户不能购买的bug

通过设置锁的有效时间来规避上述问题

public class ConsumerController{ @Autowired SpringRedisTemplate springRedisTemplate; public Object buyGoods(){ try{ //加锁操作 boolean result= springRedisTemplate .opsForValue .setIfAbsent("lockKey","value",10,TimeUnit.SECOND); if(result){ //这里是对商品的购买,对商品的剩余库存进行删减 }else{ return 一个提示信息 } }finally{ //如果发生异常就直接释放锁 springRedisTemplate.delete("lockKey"); } } }

上述代码存在的问题

当用户在锁的有效时间内并没有完成操作,这时候锁已经释放,但是锁已经被别的线程拿到,这时候就会出现超出商品库存的bug

解决方法就是在锁里设置一个当前线程的唯一标识,然后开一个子线程设置定时任务监控锁是否存在,存在就延长锁的有效期,直到用户购买完成释放锁

public class ConsumerController{ @Autowired SpringRedisTemplate springRedisTemplate; public Object buyGoods(){ String uid=UUID.randomUUID().toString(); try{ //加锁操作 boolean result= springRedisTemplate .opsForValue .setIfAbsent("lockKey",uid,10,TimeUnit.SECOND); if(result){ //这里是对商品的购买,对商品的剩余库存进行删减 }else{ return 一个提示信息 } }finally{ if(uid.equals(springRedisTemplate .opsForValue .getValue("lockKey"))){ //如果发生异常就直接释放锁 springRedisTemplate.delete("lockKey"); }else{ return 提示信息 } } } }

上述代码又一个简单实现,那就是通过redission框架来实现

入口类中加入 @Bean public Redisson redisson(){ Config config =new Config(); config .useSingleServer() .setAddress("redis://ip+端口") .setDatabase(0); return (Redisson)Redisson.create(config); } public class ConsumerController{ @Autowired Redisson redisson; public Object buyGoods(){ Rlock redissonLock=redisson.getLock("lockKey") try{ //加锁 redissonLock.lock(); //购买商品操作 }finally{ //释放锁 redissonLock.unlock(); } } }
最新回复(0)