一、背景
redis数据库购买的是阿里云的云数据库,配置是4G集群版,8分片。(也就是说每个分片的内存为500M)
官方示意图见下:
- 8个数据节点
二、某个分片的内存剧增
分片db-4在剧增前,内存使用率为50%几:
分片db-4,内存使用率剧增最高值为93%多,严重超标,触发报警:
进一步查看内存值变化趋势:
总结:可以看到,总共500MB内存的分片被消耗殆尽,下面需要进一步查看是什么原因导致的。
三、查看redis 大key
使用CloudDBA的离线全量Key分析功能:
这里有两个信息:
- 首先,直接告知了我们该分片有两个大key,它们正是内存剧增的罪魁祸首。(后文需要对其进行治理)
- 其次,db为0和2各有两个相同的key(db=2是灰度服务在使用,其key占据的内存远小于前者)
当然这里的key也都设置了有效期,特别是大key,其有效期为3天。但是因为我们单个分片的内存大小才500MB,一下子被分配到了2个大key,所以内存吃紧。
换句话说,整个应用可能产生的大key数(因为3天的有效期)同时存在4个,分配至8个集群分片,而不是像现在这样两个大key同时分配到某一个集群分片,那样的话,内存紧张会相对缓解一些。
四、治理redis大key
问题既然知道了, 根治的办法就是修改程序,不把当天的所有数据存储到一个key里。
本质还是数据量过多导致大key的。
但是,如果你不想修改程序,引起发版,这里采用的是一个临时解决方案。
- 定期删除大key
结合实际业务:大key保存的数据非业务数据,而是统计收据。所以,其重要性大大降低,如果不想发版解决,就定期删除它们,及时解决redis内存紧张的燃眉之急。
下面是具体的实现,编写一个shell脚本,把最近3天的key删掉。
#redis 连接信息
REDIS_HOST="r-bp34bc2001cc3a39.redis.rds.aliyuncs.com"
REDIS_PORT="6379"
REDIS_PASSWORD="你的密码"
# 定义一个数组,大前天、前天、昨天和今天
declare -a DATE_ARRAY=($(date -d "3 days ago" +%m-%d) $(date -d "2 days ago" +%m-%d) $(date -d "1 days ago" +%m-%d) $(date +%m-%d))
# 遍历数组中的每一个日期
for DATE in "${DATE_ARRAY[@]}"; do
# 构建key
KEY="channel_entiretyRecordActiveUserNum:$DATE"
# 检查key是否存在,存在则删除
if redis-cli -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD EXISTS $KEY; then
redis-cli -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD DEL $KEY
fi
done
连接地址见下截图:
删除大key后,内存降低至30%几。
内存使用量也减少到165MB,宽裕多了。
五、总结
站在运维的角度看,redis大key的危害可真大,警示我们在编程实现的时候,更多地需要考虑什么样的数据才适合存入redis。
其实这种统计收据,完全可以采用打印日志的方式,异步采集即可。
实在不行,也应该在程序里异步保存至mysql等数据库,万不可使用一个key来存储所有。
应用程序存入是没啥障碍,可要读取这么一个大key,消耗的内存可想而知。。。