1、背景
我们知道很多电商都会有秒杀的业务,如果只有一台服务器承载不了同一时间千万用户的业务,所以需要多台服务器,单台业务中我们只操作一个数据库,对数据的库存做想要的删减和增加用户的秒杀信息。解决并发的场景使用的是synchronized锁,但是分布式集群的情况下,这个锁起不到作用,并且会出现以下超卖的情况所以需要依赖于redis的分布式锁。
2、简单使用之setnx
在redis中有一个命令是setnx,含义是:如果key存在则返回0 如果key存在则增加后返回0,符合我们并发增加锁和删除锁的场景。
(1)使用的是setnx 增加锁
stringRedisTemplate.opsForValue().setIfPresent("lock:" + name,threadId, timeoutSec, TimeUnit.SECONDS);
(2)删除锁
stringRedisTemplate.delete("lock:" + name);
问题:
上述存在的问题是会存在误删锁的情况,如果两个线程,其中一个线程执行业务时间比较长,超过了其过期时间,则redis自动就会释放锁。另一个线程请求过来又可以获取锁了,第一个线程进入释放锁的逻辑,导致后续线程存在并发的情况了。
3、判断是否是自己线程则释放锁
上述情况的问题原因是由于没有在释放锁的时候,判断此线程是否是当前线程,如果是,则释放锁。为了做到同步,则使用lua脚本的方式来释放锁。以下是lua脚本,其中argv[1]表示当前线程的名称
if(redis.call('get', KEYS[1]) == ARGV[1])then return redis.call('del', KEYS[1]) end return 0
4、redission由来
上述操作,redis也帮我们做到了,他提供了redisson工具这个内部内置了lua脚本,解决了之前锁的问题和限制,具体是哪些问题呢?
1、上述误删的问题
2、可重入锁 如果使用上述锁 是不能重入的 重入的含义是一个有锁的方法中 进入另一个需要锁的方法
3、可重试锁 是在等待一段时间内,看是否能获取锁,使用的是信号量和PubSub的功能来实现等待,唤醒。获取锁失败的重试机制
4、超时续约,redisson内部有过期释放机制默认值是30s,当releasetime=-1时才会有这个功能,当时间过期后会重置超时时间。
总结:
我们实际开发过程中直接使用的就是redisson。但是知其然要知其所以然。希望上述文章能帮助大家更好理解。