redis应用系列四:redis内存满了该怎么办?

Q:dis是内存性数据库,随着业务数据的增长,内存会越来越大,甚至发生溢出情况,如果再不加服务器的情况下,redis内部是怎们处理的呢?

A:这就是redis回收机制

说明

  • redis回收机制由过期策略(针对时间)和淘汰策略(针对空间)两部分组成
  • 回收机制由服务内部自动完成,对于开发人员只需要配置相关参数即可

过期策略

  • 通俗来讲,过期策略是对已经过期的key进行删除的操作

    但是对于机器而言,根本不清楚在什么时候删除,删除什么数据,那redis服务内部是怎么实现对已经过期的数据的删除呢?通常来说有下面三种思路:

流程图参考

Laravel

1.定时删除

  • 创建带有TTL的key时候创建一个定时器,到期后定时删除

    这种方式就是通过创建定时器(闹钟)进行删除,这种方式数据即时性最高的
    但是,创建定时器和运行定时任务对性能消耗较大(对单线程的redis来说不可取)

2.惰性删除

  • 对于已经到期key不会立马删除,
  • 在请求过期key的时候进行判断是否过期,如果过期才会删除该key,并返回空数据
  • redis内部采用了该机制
    • 这种处理方式在高并发场景中会随着时间维度增加数据量会出现剧增,有可能造成内存溢出,导致服务异常,数据的丢失(怎么办?需要结合下面的淘汰策略);
    • 但是这种处理方式在业务量不是很大的应用中是恰到好处,就像一个很稳定的胖子,不急不慢

3.定期删除

  • redis创建带过期时间key的时候将该key放入两个字典

  • 一个字典用于存所有key,一个字典存储带有过期时间的key

  • redis服务会定期扫描该过期字典key,对于过期的key进行删除

  • 定期时间可以通过配置文件中hz进行设置

    hz=10 # 默认10,代表1s中10次,即100ms进行一次扫描
    # 该参数范围为1-500
    # 该参数越大,数据实时性越高,性能消耗较大
  • 但是这个过期key并不是全部扫描(全部扫描对性能损耗较大),而是采用贪心策略

    贪心策略

    1.redis循环对每个库进行随机取出20个key
    2.对取出的key进行过期时间判断,如果过期则删除
    3.如果取出的20个key有超过25%过期,则继续执行1,2两部操作

    那么问题来了,如果表中大量数据过期,会一直执行过期1,2步为了清除过期数据操作,这样的话在高并发场景会出现服务卡顿(无响应),这显然并不明智,
    该怎么做了?加入超时时间,redis内部其实采用快慢两种清除方式:

    1.快模式清除:超时时间固定为1ms,并且2s中内只能执行一次
    2.慢模式清除:超时时间为CPU的25%时间

总结

从以上得知,redis内部为了平衡性能和内存空间,采用了惰性删除和定期删除策略,
而这两种其实都是一种概率性删除,不会保证每个已经过期的key会及时的清理掉,这样的话如果在高并发或者大业务量的场景中会出现超出最大内存 max_memory限制而没法存储有效的key(默认情况会出现报错),影响业务。这种情况称之为 内存溢出,这种情况我们开发人员如何解决?选择正确 淘汰策略maxmemory-policy

存在问题

  • 内存溢出

淘汰策略

  • 淘汰策略触发的前提下是设置最大内存max_memory并且内存已经达到最大内存
  • 淘汰策略默认配置为不淘汰noeviction
  • noeviction(不淘汰)
    • 默认策略
    • 当内存不足以容纳新写入数据时,新写入操作会报错
    • 此方案实际当中没人用
  • allkeys-lru(最近最少使用key)
    • 当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 Key,推荐使用
  • allkeys-random(随机淘汰)
    • 当内存不足以容纳新写入数据时,在键空间中,随机移除某个 Key。
    • 应该也没人用吧,你不删最少使用 Key,去随机删
  • volatile-lru(有过期时间的最近最少使用)
    • 当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 Key。
    • 这种情况一般是把 Redis 既当缓存,又做持久化存储的时候才用。不推荐
  • volatile-random(有过期时间的随机淘汰)
    • 当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 Key。
    • 依然不推荐
  • volatile-ttl(有过期时间的淘汰更早过期的)
    • 当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 Key 优先移除。如果没有对应的键,则回退到noeviction策略
    • 不推荐

六种内存淘汰策略使用场景

  • allkeys-lru :如果我们的应用对缓存的访问符合幂律分布,也就是存在相对热点数据,或者我们不太清楚我们应用的缓存访问分布状况,我们可以选择 allkeys-lru策略
  • allkeys-random :如果我们的应用对于缓存key的访问概率相等,则可以使用这个策略。
  • volatile-ttl:这种策略使得我们可以向Redis提示哪些key更适合被eviction
  • volatile-lru策略和volatile-random策略适合我们将一个Redis实例既应用于缓存和又应用于持久化存储的时候,然而我们也可以通过使用两个Redis实例来达到相同的效果,值得一提的是将key设置过期时间实际上会消耗更多的内存,因此我们建议使用allkeys-lru策略从而更有效率的使用内存

    注意:频繁执行回收内存成本很高,主要包括查找可回收键和删除键的开销,如果当前redis有从节点(主从架构),回收内存操作对应的删除命令会同步到从节点,导致写放大的问题(主从架构当中)

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 2

值得一提的是将 key 设置过期时间实际上会消耗更多的内存

如果所有key都不设置过期时间,而是等内存达到max_memory再执行淘汰,那么每次有数据入库都要执行删除操作,是不是很耗CPU

2年前 评论
笨小孩 (楼主) 2年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!