已复制
全屏展示
复制代码

Redis内存爆满飙升


· 3 min read

线上场景

场景1

某 Redis 内存马上就要满了,经过排查你发现有大量的数据已经不使用了,当你删除了这些不再使用的数据后,你发现 Redis 的内存并没有空闲出来,这是什么原因呢?

场景2

我们每天都会往某 Redis 写入固定数量的 key,比如 500 万个 key: profile:${dt}_${uid},即每天写的 key 都不同,写数据时设置 24 小时过期。

在每天都写入 500 万个key的情况下,预期内存占用应该保持某个值不变,因为我每天在写入新的,每天旧的也在过期,但实际情况却不是这样的,观察 grafana 的图发现,每天的内存占用并不是固定的,而是在不断上升的。

这是因为 Redis 对于过期 key 的处理有惰性删除、定时删除这两种策略配合完成数据的清理。

清理策略

Redis 对于过期 key 的处理有惰性删除和定时删除两种策略:

  • 惰性删除:   当读/写一个已经过期的 key 时,会触发惰性删除策略,判断 key 是否过期,如果过期了直接删除掉这个 key
  • 定时删除:   由于惰性删除策略无法保证冷数据被及时删掉,所以 Redis 会定期(默认每100ms) 主动淘汰一批已过期的 key,这里的一批只是部分过期key,为什么只删除一部分呢?想一想假如 redis 存了几十万个 key ,每隔100ms就遍历所有的设置过期时间的 key 的话,就会给 CPU 带来很大的负载,所以可能会出现部分 key 已经过期但还没有被清理掉的情况,导致内存并没有被释放

查看内存指标

127.0.0.1:6379> info memory
# Memory
used_memory:26460978584
used_memory_human:24.64G
used_memory_rss:27559759872
used_memory_rss_human:25.67G
used_memory_peak:26700582832
used_memory_peak_human:24.87G
used_memory_peak_perc:99.10%
...
maxmemory:34359738368
maxmemory_human:32.00G
maxmemory_policy:volatile-lru
mem_fragmentation_ratio:1.04    # 大于1 小于 1.5 可以认为是合理的
...

解决方法

方法1

修改定期删除过期 key 的时间间隔,默认是 1 秒检测 10次,可以增加该值,但不宜修改太大

redis-cli 
127.0.0.1:6379> 

# 看到当前redis-server 默认值是10  
127.0.0.1:6379> config get hz
1) "hz"
2) "10"

# 将hz设置为50,然后观察段时间看看
# 注意hz的设置值可以以10为步长逐步增加,但是一般不要超过100
127.0.0.1:6379> config set hz 50
1) "hz"
2) "50"

方法2

其实这也不算好的方法,只是一个取巧的方法:手动 scan 指定的 key 也可以清理过期的 key 占用的内存空间

pip install redis
  • scan_redis.py
import redis

redis = redis.Redis(host='127.0.0.1', port=6379, db=0)
keys_num = 0
next_pos = 0

while True:
    return_pos, keys = redis.scan(next_pos, "popularity*", 1000)
    if len(keys) > 0:
        # redis.delete(*keys)
        keys_num += len(keys)
    next_pos = return_pos
    if next_pos == 0:
        break

print('scanned keys: '+ str(keys_num))
🔗

文章推荐