Redis内存使用率增长的一些解决思路

可以把Redis存储想象成一个有进有出的蓄水池

哪些情况会是Redis服务的内存使用率不断升高呢

1、将大量新写入Redis的key的TTL设置为-1,永远不过期,也就相当于一直往容量一定的蓄水池中加水但是不往外面排水,这时内存使用率一直升高是很显然的。

2、大量新写入Redis的eky的TTL时间设置的有点长,相当于这样的场景:蓄水池的水龙头快速往水池中加水,但是排水口从蓄水池中排水的速度远小于加水的速度,那么此时Redis的内存使用率也会一直升高!

一些建议

1、建议把项目中设置Redis的key的地方写在一起,方便知道线上代码写入Redis的key的格式有哪些(因为Redis不像关系型数据库那样有很多很好的可视化工具支持查看表结构、表字段等信息~)

2、对于一些非固定的数据,不建议将TTL值设置为-1,比如用户的一些常用信息我们会缓存到redis中,这样确实降低了读取数据库的IO,提高了效率,但是互联网的用户流动量比较大,有可能存入到Redis中的用户信息,这个用户到后面不玩我们的产品了,他也没有注销~ 这时候我们再redis中存储的其实就是一条永久的无用的脏数据了!!!—— 实际上建议存24小时或者其他时间,每天只存一些当天活跃或者7天活跃的用户~~ 这样效果会更好 —— 网站的session一般都会存7天。

3、Redis的TTL,设置的要合理一些~ 不要太长,这样排水口的水流出的会很慢,如果进水口进入的水量增加的话,也会使Redis的内存使用率爆增!

问题描述

线上Redis监控发现某一段时间redis的内存使用率在一直升高,当时的时间点是升高的时间:

经过线上处理,首先是停止Redis内存继续往上升~然后可以去掉一些无用的key~最终使内存使用率降下来。

记得根据实际情况解决问题 ******

❗️但是,提醒一下:需要先评估一下,删除这些key,对于业务有没有特别大的影响!如果影响很小,是一个很边缘的需求,可以这么搞。 

必须的key不要删除

如果删除这些key,对于业务的影响很大的话(比如用户每日的任务等,TTL到当天的24点,删除的话用户任务白做了!),这时候就不要去想删除key去解决内存使用率增高的问题了!

建议:适当的去调整一下新的进入Redis中这些key的TTL值~ 或者 升级一下Redis机器~现在的云服务器基本都支持平滑升级,并且升级还是很快的,可以将损失降到很低~

非必要的key可以批量删除

我们自己的业务中,删除这些key,对于正常业务没有任何影响!

❗️先在业务代码中,取消往Redis中写入这些key的逻辑 ~~ 保证没有新的key进入Redis,然后再删除 ———— 删除完,等Redis的内存下来以后,可以再加回来相关的逻辑,但是TTL不要设置的太大了!!!

使用一个demo来说明一下解决思路 ***

在本地docker搭建一下redis服务,使用db1,里面有1个key

 

写一个python脚本往里面分别插入3种不同的key

import random
from datetime import date

import redis


today_str = date.today().strftime("%Y-%m-%d")
print(">>> ", today_str, type(today_str))

# redis key 的格式
key1 = "redis_demo:test1:{}:{}"  # redis_demo:test1:(2021-12-25):(随机字符串)
key2 = "redis_demo:test2:{}:{}"  # redis_demo:test1:(2021-12-25):(随机字符串)
key3 = "redis_demo:test3:{}:{}"  # redis_demo:test1:(2021-12-25):(随机字符串)

# redis链接
# 注意这里我用的是 db1 ———— 没有用Redis集群,用的是一个Redis实例
r = redis.Redis(host="127.0.0.1", port=6379, db=1)


# 往Redis中模拟插入1w个key1、key2、key3类型的字符串
for i in range(10000):
    curr_key1 = key1.format(today_str, str(random.randint(0,1000000)))
    curr_key2 = key2.format(today_str, str(random.randint(0,1000000)))
    curr_key3 = key3.format(today_str, str(random.randint(0,1000000)))
    curr_value = str(random.randint(0,10000))

    # 都在10000秒后失效
    r.set(curr_key1, curr_value, 10000)
    r.set(curr_key2, curr_value, 10000)
    r.set(curr_key3, curr_value, 10000)

看看db1中的dbsize

如果线上key比较多千万不能使用 keys * 相关命令!会阻塞Redis服务!

如果线上key比较多千万不能使用keys *相关命令!会阻塞Redis服务!

使用scan命令找key

scan 0 match redis_demo:test1* 

但是找出来以后,不可能一个一个执行 del 命令删除~~ 太浪费时间了。

可以在终端执行 redis-cli 相关命令。

使用redis-cli命令将所有key输出到文件中然后写脚本del相关的key ***

选择db1,然后将match到的key输出到文件中:

redis-cli -h 127.0.0.1 -p 6379 -n 1 --scan --pattern "redis_demo:test1*" > redis_demo的test1的key.txt

也可以看看文件有多少行:

cat redis_demo的test1的key.txt |wc -l

—— 也就是有9962个key,里面都是完整的redis的key,后面可以写一个脚本,一行一行从文件中读取这些key,然后在redis中执行del操作删除key!

使用redis-cli配合xargs命令使用命令删除scan命令获取到的key ***

注意:需要选择 db1  —— 实际上可以同时删除1000条数据~~

redis-cli -h 127.0.0.1 -p 6379 -n 1 --scan --pattern "redis_demo:test1*" |xargs -L 10 redis-cli -h 127.0.0.1 -p 6379 -n 1  del

把test2与test3也一起删掉,可以同时删1000条数据

最后再进去看看只剩下一开始的那个key了

参考文章

Redis在命令行中操作指定数据库下的key

redis cli命令 ***

redis scan 优雅的批量删除

深入理解Redis的scan命令

Redis scan命令解析——替代keys命令可以在生产环境使用实现遍历

Redis Scan 原理解析与踩坑 

~~~

posted on 2021-12-25 19:05  江湖乄夜雨  阅读(1606)  评论(0编辑  收藏  举报