什么是 bigkey
在Redis中,一个字符串最大512MB,一个二级数据结构(例如hash、list、set、zset)可以存储大约40亿个(2^32-1)个元素,但实际上中如果下面两种情况,我就会认为它是bigkey。
字符串类型:它的big体现在单个value值很大,一般认为超过10KB就是bigkey。
非字符串类型:哈希、列表、集合、有序集合,它们的big体现在元素个数太多。
危害
1.内存空间不均匀
这样会不利于集群对内存的统一管理,存在丢失数据的隐患。
2.超时阻塞
由于Redis单线程的特性,操作bigkey的通常比较耗时,也就意味着阻塞Redis可能性越大,这样会造成客户端阻塞或者引起故障切换,它们通常出现在慢查询中。
3.网络拥塞
bigkey也就意味着每次获取要产生的网络流量较大,假设一个bigkey为1MB,客户端每秒访问量为1000,那么每秒产生1000MB的流量,对于普通的千兆网卡(按照字节算是128MB/s)的服务器来说简直是灭顶之灾,而且一般服务器会采用单机多实例的方式来部署,也就是说一个bigkey可能会对其他实例造成影响,其后果不堪设想。
4.过期删除
有个bigkey,它安分守己(只执行简单的命令,例如hget、lpop、zscore等),但它设置了过期时间,当它过期后,会被删除,如果没有使用Redis 4.0的过期异步删除(lazyfree-lazy-expire yes),就会存在阻塞Redis的可能性,而且这个过期删除不会从主节点的慢查询发现(因为这个删除不是客户端产生的,是内部循环事件,可以从latency命令中获取或者从slave节点慢查询发现)。
5.迁移困难
当需要对bigkey进行迁移(例如Redis cluster的迁移slot),实际上是通过migrate命令来完成的,migrate实际上是通过dump + restore + del三个命令组合成原子命令完成,如果是bigkey,可能会使迁移失败,而且较慢的migrate会阻塞Redis。
怎么产生的
一般来说,bigkey的产生都是由于程序设计不当,或者对于数据规模预料不清楚造成的,来看几个:
(1) 社交类:粉丝列表,如果某些明星或者大v不精心设计下,必是bigkey。
(2) 统计类:例如按天存储某项功能或者网站的用户集合,除非没几个人用,否则必是bigkey。
(3) 缓存类:将数据从数据库load出来序列化放到Redis里,这个方式非常常用,但有两个地方需要注意:
第一,是不是有必要把所有字段都缓存
第二,有没有相关关联的数据
建议
string 控制在 10kb 以内,hash、list、set、zset元素个数不要超过5000。反之,则称之为「bigkey」。
如何发现呢
既然知道有这些危害了,那该怎么避免呢?
可以使用 redis-cli 原生命令 redis-cli --bigkeys
这个命令是使用「scan」进行取样处理的,所有并不用太担心会阻塞 redis。统计完的结果如下
[01.35%] Biggest string found so far 'AAA:SHOPPING:FlightInfoCacheService:getFlightScheduleInfoMap:CTU PEK 2022 07 09' with 3535 bytes
[02.22%] Biggest string found so far 'AAA:ORDER:JOURNEY_INFO:88342190' with 9226 bytes
[08.10%] Biggest string found so far 'AAA:BAGGAGE:CONGIG:G5:V' with 28249 bytes
[26.00%] Biggest string found so far 'AAA:FS:QUERY:PKX:SZX:20220710' with 35530 bytes
[29.30%] Biggest hash found so far 'AAA:FRM:SZX:HAK' with 173 fields
[30.39%] Biggest hash found so far 'AAA:FRM:CGO:HAK' with 195 fields
[40.85%] Biggest string found so far 'AAA:FS:QUERY:CAN:CKG:20220711' with 47711 bytes
[51.40%] Biggest hash found so far 'AAA:FRM:LGA:CMH' with 233 fields
[61.67%] Biggest string found so far 'AAA:ORDER:JOURNEY_INFO:90388812' with 184500 bytes
[63.14%] Biggest hash found so far 'AAA:FRM:CJU:GMP' with 318 fields
从这个结果可以得出,它会统计出每一种数据结构 top 1 的「bigkey」。同时,也可以增加一个 --i 阈值,让它定时执行一次统计。例如下面的命令,表示100毫秒执行一次
redis-cli -h $REDIS_IP -p $REDIS_PORT -n 0 --i 0.1 --bigkeys
附:自己写的简易代码 可以批处理操作
#!/bin/bash
# Filename : redis BIGKEYS key
# Version : 1.0
#auth liudh
WORK_HOME=./
cd ${WORK_HOME}
echo "">redis_bigkeys_list
date=`date`
function commit_bigkeys()
{
redis-cli -h $REDIS_IP -p $REDIS_PORT -n 0 --bigkeys >> redis_bigkeys_list
}
while read REDIS_IP REDIS_PORT
do
if [ -n ${REDIS_IP} ]; then
REDIS_IP=${REDIS_IP}
REDIS_PORT=${REDIS_PORT}
echo " ${date} ${REDIS_IP}: ${REDIS_PORT} analysis bigkeys begin " >>redis_bigkeys_list
commit_bigkeys
sleep 1
echo " ${date} ${REDIS_IP}: ${REDIS_PORT} analysis bigkeys over " >>redis_bigkeys_list
echo ""
fi
done < redis_bigkeys_ip_port.txt
echo "analysis over , please see list!"
redis_bigkeys_ip_port.txt
192.168.1.1 6379