Redis 中查找大对象
大key的苦恼
redis作为一个高性能内存数据库,在实际业务中应用的非常广泛,虽然redis的性能很好,但是在实际使用过程中,如果使用不当,也会造成一些性能问题,比如数据中存在大key。什么是大key?顾名思义就是单个key中的数据比较大,通常来说,单个key的value值不会很大,这种情况下,key的读取,删除操作不会影响性能,如果value过大,读取或删除会相对耗时,大家都知道,redis是单线程,耗时操作就会阻塞其它请求,给性能上带来一些影响。所以,不管是作为开发还是运维人员,使用redis都应该经常关注数据中有没有大key。
大Key会带来哪些问题呢?
1、如果是集群模式下,无法做到负载均衡,导致请求倾斜到某个实例上,而这个实例的QPS会比较大,内存占用也较多;对于Redis单线程模型又容易出现CPU瓶颈,当内存出现瓶颈时,只能进行纵向库容,使用更加昂贵的高配置服务器。
2、涉及到大key的操作,尤其是使用hgetall、lrange 0 -1、get、hmget
等操作时,网卡可能会成为瓶颈,也会到导致堵塞其它操作,qps 就有可能出现突降或者突升的情况,趋势上看起来十分不平滑,严重时会导致应用程序连不上,实例或者集群在某些时间段内不可用的状态。
3、假如这个key需要进行删除操作,如果直接进行DEL 操作,被操作的实例会被Block住,导致无法响应应用的请求,而这个Block的时间会随着key的变大而变长。
几种定位某个Redis实例大key的方法
1、在redis实例上执行bgsave,然后我们对dump出来的rdb文件进行分析,找到其中的大KEY。
繁琐。视个人情况决定是否使用。
2、redis-cli 原生自带 –bigkeys 功能,可以找到某个实例 5种数据类型(String、hash、list、set、zset)的最大key。
该命令是redis自带,但是只能找出五种数据类型里最大的key。很明显,这并不能帮助我们去发现整个数据里的大key,所以一般不使用
3、通过读取rdb文件, 避免对线上服务的压力。
rdb是一种二进制文件格式, 更多的结合第4种方式。
4、更优雅的第三方工具。
第三方工具很多。适合自己的就是最好的。
比如 redis-rdb-tools 、 godis-cli-bigkey 等等。本文以 redis-rdb-tools
举例。
查找过程
注意以下所有试验基于redis 5.0.3版本!
redis-cli 方式
命令 : redis-cli -p 6579 -a 'passwd' --bigkeys -i 0.1
-- 每扫描100个key休息0.1秒
[root@instance-s9zssenm bin]# redis-cli -p 6579 -a 'passwd' --bigkeys -i 0.1
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
# Scanning the entire keyspace to find biggest keys as well as
# average sizes per key type. You can use -i 0.1 to sleep 0.1 sec
# per 100 SCAN commands (not usually needed).
[00.00%] Biggest string found so far '1' with 1 bytes
[00.00%] Biggest string found so far 'CACHE_DIAMOND:WHITE_IPS_CACHE_KEY' with 116 bytes
-------- summary -------
Sampled 2 keys in the keyspace!
Total key length in bytes is 34 (avg len 17.00)
Biggest string found 'CACHE_DIAMOND:WHITE_IPS_CACHE_KEY' has 116 bytes
2 strings with 117 bytes (100.00% of keys, avg size 58.50)
0 lists with 0 items (00.00% of keys, avg size 0.00)
0 sets with 0 members (00.00% of keys, avg size 0.00)
0 hashs with 0 fields (00.00% of keys, avg size 0.00)
0 zsets with 0 members (00.00% of keys, avg size 0.00)
0 streams with 0 entries (00.00% of keys, avg size 0.00)
redis-cli 原理
使用scan命令去遍历所有的键,对每个键根据其类型执行"STRLEN","LLEN","SCARD","HLEN","ZCARD"这些命令获取其长度或者元素个数。
redis-cli 缺点
1、线上使用:虽然scan命令通过游标遍历建空间并且在生产上可以通过对从服务执行该命令,但毕竟是一个线上操作。
2、set,zset,list以及hash类型只能获取有多少个元素。但其实元素多的不一定占用空间大。
redis-rdb-tools 开源工具
系统: CentOS 7.4 64bit
rdb 文件位置: /usr/local/redis/6379_dump.rdb
# 安装
yum install python-pip gcc gcc-c++ python-devel git tmux -y
pip install rdbtools python-lzf
# 创建安装目录
mkdir mointor
cd mointor
# 下载源码
git clone https://github.com/sripathikrishnan/redis-rdb-tools
cd redis-rdb-tools
# 安装
python setup.py install
# 防止 redis dump.rdb 文件过大,途中意外退出窗口
tmux
# 找出 dump.rdb 位置,指定
rdb -c memory /usr/local/redis/6379_dump.rdb > /data/redis.csv
# 内存报告生成后,结合用linux sort命令排序,根据內存列排序,找出最高的key有哪些
sort -k4nr -t , /data/redis.csv > /data/sort.txt
# 查看前100个排序最高的数据
awk -F ',' '{print $3}' /data/sort.txt | head -100 | sort -k1 | uniq > /data/result.txt
# 查出 shiro_redis_session:0824c1d1-fbb7-4d43-9e1a-25e98e64174a 这个 key 占用多少内存,结果单位是 MB
grep 'shiro_redis_session:0824c1d1-fbb7-4d43-9e1a-25e98e64174a' /data/sort.txt | awk -F ',' '{sum += $4};END {print sum/1024/1024}'
csv 文件的结构
rdb文件格式
rdb 是一种二进制文件格式, rdb文件的整体结构如下:
rdb字段分析:
字段 | 描述 |
---|---|
“REDIS” | 魔数 |
REDIS_VERSION | REDIS0009(redis5.0.3版本) |
AUX_FIELD_KEY_VALUE_PAIRS | 附加属性字段 |
DB_NUM | db_num(0-15) |
DB_DICT_SIZE | db字典大小 |
EXPIRE_DICT_SIZE | expire 字典大小 (db和expire过期时间在Redis中是两个独立的hash table) |
KEY_VALUE_PAIRS | 一个个key-value pairs |
EOF | EOF结束标志(0xFF) |
CHECKSUM | 8字节的checksum |
Redis中定义了一些opcode(1字节),去标记opcode之后保存的是什么类型的数据。
如下图所示:
opcode 252标记一个过期时间,
248和249分别表示lru或者lfu,接着是value_type,标记值的类型,接着就是一个个key和vlaue.
我们看下value_type和redis中数据类型的对应关系.
value_type就是值类型这一列,括号中的数字就是保存到rdb文件中时的实际使用数字.
因此,解析rdb文件, 都是通过value_type去获取每个value的大小。
~ end