redis 设置expire=0和直接调用delete删除key的区别

背景

问题起源于生产上应用redis做的一个临时缓存的场景,调用方首先调用某个容器的生成接口,该接口会生成临时缓存的数据放到redis中,然后调用方在调用获取数据内容的接口从任何一台容器中获取放置在redis中的临时数据。

还原问题场景

由于临时数据仅仅只是为了在第二步获取数据内容时从任一个容器都可以访问到,所以临时数据需要删除,因此应用设计了一个单独线程来清理临时redis数据的内容,一开始清理逻辑时判断临时数据放到redis的时间是否已经超过了3S,如果是,那么直接删除,后来,有同事反馈说,我们不能这么删除,因为我们没法保证临时数据在3s内肯定调用方已经调用完获取数据的接口获取到数据,所以保险起见,我们用expire设置过期时间为1s,这样相当于临时数据至少存活4S,更充分的保证调用方已经调用获取数据接口获取到了数据,于是我们的应用这样上线了,但是刚上线没多久,Redis就告警内存不足,于是就有了这次关于expire设置过期时间的工作原理的记录

redis.expire工作原理

expire设置一个很短的过期时间 和 直接delete相比有什么不一样呢? delete能及时回收redis的内存,那么expire为什么不能及时回收内存呢?这里就要讲到了redis是如何清理过期数据的,首先redis会使用惰性删除和定时清理的策略删除过期的key

惰性删除
所谓的惰性删除策略是指当访问redis的某个过期的redis的key时,redis会直接删除过期的key,并回收过期key所占据的内存

定时删除策略
Redis服务器默认每隔100ms过期检查一次数据库,检查的间隔可以通过Redis.conf进行配置,每次过期检查都会执行以下操作
1.首先从所有设置了过期时间的的key集合中随机抽取20个候选的key,判断key是否过期,如果过期,那么就进行删除并且回收内存,并记录下20个候选key中总共有多少个key过期了
2.如果步骤一中过期的key的数量/20个候选key的数量的占比大于25%,那么继续循环删除,否则等待下一轮的循环检查
不过为了防止有大量的key要删除导致的redis卡死,每一轮循环Redis设置了最长的删除流程的上限不超过25ms,超过则等待下一轮检查

总结

从上述Redis对过期时间的key的清理策略可知,当短时间内有大量的key过期,并且很多是大key时,redis服务器是来不及删除这么多的过期key的,如果此时新的key不断的生成,而过期的key来不及及时删除,此时就可能导致redis内存不足. 那回到一开始的问题,为什么你使用delete直接删除不会导致redis内存问题呢?首先一方面是delete删除可以直接回收内存,而且由于是客户端来进行删除,只有在删除之后才会再次申请新的redis的key,所以相当于也变相的把新key的生成速度降低了,这两者综合导致redis内存并没有被打爆

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 好的,下面是一个示例代码: ```python import redis # 连接到 Redis 服务器 r = redis.Redis(host='localhost', port=6379, password='123456') # 设置锁的超时时间 timeout = 1000 # 尝试获取锁,如果锁被其他客户端获取,则此操作会失败 status = r.setnx('lock_key', 'lock_value') # 如果获取成功,则设置锁的超时时间 if status: r.expire('lock_key', timeout) # 如果获取失败,则轮询等待,直到获取到锁为止 while not status: time.sleep(0.1) status = r.setnx('lock_key', 'lock_value') if status: r.expire('lock_key', timeout) # 执行具体的业务逻辑 # ... # 释放锁 r.delete('lock_key') ``` 上面的代码使用了 Redis 的 SETNX 命令来尝试获取锁,如果锁没有被其他客户端获取,则返回 1,表示获取成功。否则,返回 0,表示获取失败。 然后,使用 RedisEXPIRE 命令来设置锁的超时时间,以避免死锁的情况。 最后,使用 RedisDELETE 命令来释放锁。 注意,上面的代码只是一个示例,实际应用中还需要考虑更多的细节,比如锁的重入、锁的自动续期等。 ### 回答2: Redis分布式锁是一种通过Redis的原子操作来实现多个进程或线程之间的互斥访问。下面是一个简单的Redis分布式锁的代码实现示例: ```python import redis import time class RedisDistributedLock: def __init__(self, redis_host, redis_port, lock_key, acquire_timeout=10, lock_expire=30): self.redis = redis.Redis(host=redis_host, port=redis_port) self.lock_key = lock_key self.acquire_timeout = acquire_timeout self.lock_expire = lock_expire def acquire(self): end_time = time.time() + self.acquire_timeout while time.time() <= end_time: if self.redis.set(self.lock_key, 'locked', nx=True, ex=self.lock_expire): return True time.sleep(0.001) return False def release(self): self.redis.delete(self.lock_key) ``` 使用示例: ```python # 假设Redis运行在localhost的6379端口上 redis_host = 'localhost' redis_port = 6379 # 分布式锁的键名 lock_key = 'my-distributed-lock' # 创建Redis分布式锁对象 lock = RedisDistributedLock(redis_host, redis_port, lock_key) # 尝试获取分布式锁 if lock.acquire(): try: # 获得锁之后执行需要互斥的代码 print("执行互斥代码") finally: # 释放锁 lock.release() else: print("无法获取分布式锁") ``` 以上代码中,`RedisDistributedLock`是一个封装了Redis分布式锁操作的类。在`acquire`方法中,使用`redis.set`命令设置键值对,通过`nx=True`参数实现了只有当键不存在时才进行设置,从而实现了互斥访问。在`release`方法中,使用`redis.delete`命令删除键,释放锁。 使用时,首先创建`RedisDistributedLock`对象,然后调用`acquire`方法尝试获取分布式锁,如果成功获取到锁则执行需要互斥的代码,最后调用`release`方法释放锁。如果获取锁失败,则执行其他逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值