删除策略

# 过期数据

我们在之前 通用命令 中也讲过数据过期,主要是利用 ttl 命令来完成的。
返回值:

  • XX:具有时效性的数据
  • -1:永久有效的数据
  • -2:已经过期/被删除/未定义的数据

但实际上过期数据不会立马被删除,只是用不了了,它的删除时机取决于我们定义的删除策略。

# 时效性数据存储结构

一个 hash 的结构

expires:
  - value1的地址: 过期时间
  - value2的地址: 过期时间
  - ...
1
2
3
4

我们定义删除策略,在内存占用与CPU占用中寻找平衡,找到合适的时机将其删除。

# 删除策略

# 定时删除

创建定时器,当 key 设置有过期时间到达时立刻执行删除操作。
这样做节约了内存,但是 CPU 要一直处于忙碌状态比较花费性能。

# 惰性删除

到时间了先不删,等下次访问它时若该 key 已过期就先删除它,然后返回 key 不存在的消息。
这样做节约 CPU 性能,但会出现长期占用内存的数据。

# 定期删除

配置文件中存在 server.hz 表示一秒钟执行 serverCron() 的次数。
serverCron() 是一种服务端轮询策略,它配合 server.hz定期查询库中的 keys 看它们是否过期,若过期的话删除。
具体执行过程如下:

serverCron(){ databasesCron() { activeExpireCycle(); } }
其中 activeExpireCycle() 对每一个 expires[*](* 号数据库) 逐一检测,每次执行 250ms/server.hz
对某个 expires[*] 检测时随机W=W=ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP(来自于配置文件) 个 key 进行检测,对于当前检测的 key:

  • 如果 key 超时,删除 key
  • 如果一次删除的 key 的数量 >W25>\;W*25% 则继续做这个 expires[*]
  • 如果一次删除的 key 的数量 W25\le\;W*25% 则检查下一个 expires[*] ,[0,15][0,15] 循环

需要注意的是在 Redis 源码设计中,对这种删除策略是一种全局递进的,也就是说如果第一次删除到 expires[5] 了话,那么第二次会接着上一次的删除进度 expires[5] 继续往下走。
用于保存进度的全局变量是 current_db
而由于 [0,15][0,15] 循环的特性,不用担心会删除到终点。

这样周期性抽查删除过期数据,且可以根据CPU性能设置频率。
对CPU友好,且解决了长期无法访问的冷数据的清除问题。

# 逐出算法

发生于内存不足以支撑新数据的到来时。
Redis 反复执行逐出算法多次,若多次执行算法依旧未能满足内存需要,返回 OOM 指令失败,否则将新数据成功插入 Redis。

配置属性:

  • maxmemory:Redis 最大可使用的物理内存
    • 0:不做限制
    • *:*B
    • *MB: *MB
  • maxmemory-samples:进行逐出算法时,每次选取的待删除的数据个数
  • maxmemory-policy:发生内存不足时,对挑选的数据进行逐出的策略
    • 检测易失数据(从 server.db[i].expires 中挑选)
      • volatile-lru:逐出最久未使用的数据
      • volatile-lfu:逐出最近最少使用次数的数据
      • volatile-ttl:逐出将要过期的数据
      • volatile-random:逐出随机选择的数据
    • 检测全库数据(从 server.db[i].dict 中挑选)
      • allkeys-lru:逐出最久未使用的数据
      • allkeys-lfu:逐出最近最少使用次数的数据
      • allkeys-random:逐出随机选择的数据
    • no-enviction:禁止数据逐出(Redis4.0中的默认策略)

具体如何配置最合适的,可以利用 info 指令查看缓存 hitmiss 的次数来调优。

Last Updated: 10/27/2023, 3:31:40 PM