REDIS_04Redis高并发分布式锁
2023-04-26 23:44:15 6 举报
AI智能生成
Redis高并发分布式锁
作者其他创作
大纲/内容
场景
秒杀活动中,用户在购买前判断库存是否充足,然后购买成功后会减掉库存
conn = redis.StrictRedis(host='localhost', port=6379, db=0)
stock = int(conn.get('stock'))
f stock > 0:
real_stock = stock - 1
stock = int(conn.get('stock'))
f stock > 0:
real_stock = stock - 1
单机场景
在单机环境下运行,一个执行完了下一个再执行,这样不会有问题
并发场景
并发环境下,在未加锁的情况下多线程查出来的库存量是一样的,后面继续执行,到最后会发现有两个请求购买成功了,但是库存只减了1,这样就会出现超卖的问题
分布式锁
setnx
使用方法
setnx key value
将 key 的值设为 value,若给定的 key 已经存在,则 SETNX 不做任何动作
EXISTS job # job这个key不存在
返回(integer) 0
SETNX job "programmer" # 设置job
(integer) 1
SETNX job "programmer2" # 重新设置job,失败
(integer) 0
GET job # job还是第一次设置的值
"programmer"
使用思路
根据setnx的结果来判断是否是获取到锁,是就执行减库存的操作,不是则返回网络错误
lock_key = 'lock_key'
res = conn.setnx(lock_key, 'lock')
if not res:
return '网络错误'
# ... 减库存逻辑
conn.delete(lock_key)
return msg
res = conn.setnx(lock_key, 'lock')
if not res:
return '网络错误'
# ... 减库存逻辑
conn.delete(lock_key)
return msg
当在执行删除key的操作前,程序异常退出了,后面的请求永远不会执行到减库存的逻辑
try…finally来解决
lock_key = 'lock_key'
res = conn.setnx(lock_key, 'lock')
if not res:
return '网络错误'
try:
# ... 减库存逻辑
finally:
conn.delete(lock_key)
res = conn.setnx(lock_key, 'lock')
if not res:
return '网络错误'
try:
# ... 减库存逻辑
finally:
conn.delete(lock_key)
除了程序异常退出,还可能机器宕机,这样异常就捕获不到了,这是就需要给key设置一个过期时间
等到过期时间到后key就会被redis销毁掉,不会影响其他的请求
lock_key = 'lock_key'
res = conn.setnx(lock_key, 'lock')
conn.expire(lock_key, 10)
if not res:
return '网络错误'
try:
# ... 减库存逻辑
finally:
conn.delete(lock_key)
res = conn.setnx(lock_key, 'lock')
conn.expire(lock_key, 10)
if not res:
return '网络错误'
try:
# ... 减库存逻辑
finally:
conn.delete(lock_key)
设置过期时间的代码和设置锁值的代码应该使用原子性来执行
conn.set(lock_key, 'lock', nx=True, ex=10)
考虑在加了超时的情况下,业务还没执行完,但锁超时间已到
子主题
当前请求1的处理库存逻辑需要12秒,还没执行完,锁就失效了
请求2获得了锁后,开始处理逻辑,请求2执行了2秒后,请求1处理完毕,删除了锁,但是这个锁是请求2的锁
请求3,不等到请求2释放锁,在第12秒时就可以获得锁
然后请求2会会再把请求3的锁提前释放掉
针对这种问题,我们需要对判断要删除的锁是不是自己创建的
redisson
使用方法
redisson提供了不同类型redis部署的连接方案
获得redisson锁
//拿到锁对象
RLock redissonLock=redisson.getLock(couponKey);
RLock redissonLock=redisson.getLock(couponKey);
加锁(同时实现加锁,看门狗锁续命的功能)
//拿到锁对象
RLock redissonLock=redisson.getLock(couponKey);
RLock redissonLock=redisson.getLock(couponKey);
释放锁
//解锁
redissonLock.unlock();
redissonLock.unlock();
使用思路
考虑到业务时间比超时时间长,考虑可以给锁续命的问题
让后面的线程无法删除前面线程
每个线程都设置一个唯一ID(UUID)。在删除锁的时候,判断一下是不是自己线程的ID,这样就无法删除其他线程的锁了
锁续命
如果业务代码在无异常,并且仍然在执行的情况下,我们加一个可以自动将锁时间延长的功能
释放锁如何保持原子性
释放锁的时候,如果出现异常续命任务还会继续执行
0 条评论
下一页