Redis入门到入土-章节五-Redission

211 阅读5分钟

什么是Redisssion

Redisssion是一个基于Redis的Python库,用于实现分布式锁和其他高级锁机制。它在Redis的原生锁功能的基础上提供了更高级别的功能,如可重入锁、公平锁、读写锁等。Redisssion旨在解决在分布式环境下的并发控制问题,它能够保证多个进程或线程之间的互斥访问和同步操作。 说白了就是一个基于Redis的一个分布式方案解决框架

Redisssion使用Redis作为后端存储,通过利用Redis的原子操作和内存存储特性,实现了高效、安全的分布式锁机制。它支持对锁进行配置和管理,并提供了超时机制、可重入性、可公平性等特性,使得开发人员可以灵活地控制和应用锁策略。

Redisssion提供了简单易用的API,可以方便地在Python项目中使用。它与Python中常用的线程和进程模型兼容,并支持在分布式环境中使用,例如在分布式任务调度、分布式缓存管理、分布式资源控制等场景中都可以使用Redisssion来实现分布式锁。

可重入锁

指同一个线程能多次获取同一个锁,在多次获取锁时先去判断HashMap结构中该锁是否为当前线程的,如果是,给当前线程锁计数加一。在多次获取锁后释放锁时,不能直接删除锁,而是减少可重入锁的计数次数。在释放锁时,当重入次数为0时才能真正删除锁

执行流程

这里结合秒杀功能一人一单展开讲

image.png

判断锁是否存在:

image.png

根据以上理论知识,可以得出秒杀功能优化方案:使用lua脚本完成加锁解锁操作,保证在进行业务操作时加锁和解锁的原子性,防止过程中出现不可控因素,导致库存数量异常

lua加锁:

local key = KEYS[1];
local threadId = ARGV[1];
local releaseTime = ARGV[2];
-- 锁不存在,设置锁
if(redis.call('exists',key,threadId) == 0) then
    -- 设置锁和计数
    redis.call('hset',key,threadId,'1');
    -- 设置过期时间
    redis.call('expire',key,releaseTime);
	return 1;
end;
-- 锁存在,判断锁是否为自己的
if(redis.call('hexists',key,threaId) == 1) then
    -- 计数+1
    redis.call('hincrby',key,thread,'1');
    -- 重置时间
    redis.call('expire',key,releaseTime);
    return 1;
end;
-- 获取的锁不是自己的
return 0;

lua释放锁

local key = KEYS[1];
local threadId = ARGV[2];
local releaseTime = ARGV[3];
-- 不是自己的线程锁了
if(redis.call('hexists',key,threadId) == 0) then
    reutn nil;
end;
-- 是自己的计数-1
local count = redis.call('hincrby',key,threadId,-1);
-- 计数是否为0了
if(count > 0) then 
    redis.call('expire',key,releaseTime);
    return nil;
else
    redis.call('DEL',key)
end;

Redission的可重入锁基于hash结构实现,记录线程id和重入次数,获取锁时先判断锁是否存在,存在重入次数+1,释放锁时-1,直到重入次数减为0,才真正释放锁

重试机制

Redisssion提供了一个可靠的重试机制,用于处理在分布式环境下可能出现的锁竞争和网络故障等问题。重试机制可以确保在锁竞争失败或Redis连接中断时进行重新尝试,以提高系统的可靠性和稳定性。

在Redisssion中,重试机制主要通过以下两个方面来实现:

  1. 自动重试: 当Redis连接断开或发生其他网络故障时,Redisssion会自动尝试重新连接,并继续执行后续的操作。它会根据配置的重试次数和重试间隔进行尝试,直到连接成功或达到最大重试次数为止。
  2. 锁竞争重试: 当获取分布式锁时发生竞争失败,Redisssion会根据配置的重试次数和重试间隔进行重试,直到成功获取锁或达到最大重试次数为止。这个重试机制可以防止由于锁竞争导致的并发问题。

超时机制

在源码中,包括一个"看门狗"(Watch Dog)的机制,"看门狗"是一种监控机制,用于检测并处理长时间未释放的锁。

在调用lock方法后,都会调用以下方法org.redisson.RedissonLock#tryAcquireAsync

image.png

其中this.scheduleExpirationRenewal(thredId)为到期续订,也就是看门狗的具体实现方法,想要进入下方内容,需要保证leaseTime==-1,即锁的过期时间,当调用lock方法时传入超时时间限制,那么就不会开启看门狗

lock.lock(); // 开启
lock.lock(5000,TimeUnit.SECONDS); // 不开启

想要进入续期,需要保证ttlRemaining==null,而这个参数是加锁成功后返回的值

org.redisson.RedissonLock#renewExpiration

image.png

超时续期这里,当任务每 internalLockLeaseTime/3 ms后执行一次,其中internalLockLeaseTime的默认时间为30000,所以任务每10s执行一次

核心业务

image.png

这里getLockaName(threadId)中可拿到一些自定义的参数,如可自定义internalLockLeaseTime的值,来指定看门狗多久续期一次

到这里重试机制中的部分实现大概清楚了,无非在后台开启定时任务线程,每隔一段时间对锁续命,延长锁时间。那这样的话这个数据是否会永不过期呢?理论上是这样,但是在实际业务中,存在于Redis中的数据即使不会过期,也会有相关人员进行维护,或者产生bug,陷入死循环