背景
线上阿里云 redis 内存使用持续飙升,短时间内爆了好几次,一开始清空了些存了一百多万key的库,但内存缓慢释放了一定程度还是没什么用,仍然在警戒线附近,只好扩容,扩容又有几秒瞬断,引发了很多问题,因为我们所有的业务组都在使用同一个redis 服务器而且约定 redis 使用都是存的有时效性的,通过阿里云管理后台可以看到每天早上 redis 开始直线上升,等12个小时时间到期,又呈断崖式下跌,迫切需要分析下线上 redis 的内存具体使用情况,以诊断问题,看是哪个业务组使用导致的,能否从代码层面去优化并从根源上解决掉问题。这里直接使用 redis-rdb-tools 来进行静态分析,整个分析的过程非常简单但很实用,值得拥有!!!
使用
创建备份
为了不影响线上实例的使用,自建 Redis 的一般会采用 bgsave 生成 rdb 文件,云数据库 Redis 版本可以在控制台上进行数据备份和下载操作,下载后的数据为 rdb 格式文件。步骤详见下图:
介绍
redis-rdb-tools 是一个 python 的解析 rdb 文件的工具,在分析内存的时候,我们主要用它生成内存快照。主要有以下三个功能:
- 生成内存快照
- 转储成 json 格式
- 使用标准的 diff 工具比较两个 dump 文件
redis-rdb-tools 安装
redis-rdb-tools 有两种安装方式,任选其一即可。
使用 PYPI 安装
pip install rdbtools
从源码安装
git clone https://github.com/sripathikrishnan/redis-rdb-tools
cd redis-rdb-tools
sudo python setup.py install
使用 redis-rdb-tools 生成内存快照
语法
Usage: rdb [options] /path/to/dump.rdb
Example : rdb --command json -k "user.*" /var/redis/6379/dump.rdb
Options:
-h, --help #显示此帮助消息并退出;
-c FILE, --command=FILE #指定rdb文件;
-f FILE, --file=FILE #指定导出文件;
-n DBS, --db=DBS #解析指定数据库,如果不指定默认包含所有;
-k KEYS, --key=KEYS #指定需要导出的KEY,可以使用正则表达式;
-o NOT_KEYS, --not-key=NOT_KEYS #指定不需要导出的KEY,可以使用正则表达式;
-t TYPES, --type=TYPES #指定解析的数据类型,可能的值有:string,hash,set,sortedset,list;可以提供多个类型,如果没有指定,所有数据类型都返回;
-b BYTES, --bytes=BYTES #限制输出KEY大大小;
-l LARGEST, --largest=LARGEST #根据大小限制的top key;
-e ESCAPE, --escape=ESCAPE #指定输出编码,默认RAW;
生成内存快照的命令为:rdb -c memory dump.rdb > memory.csv
,然后报错了!!!
[root@localhost mnt]# rdb -c memory hins6119739_data_20190109234325.rdb > memory.csv
WARNING: python-lzf package NOT detected. Parsing dump file will be very slow unless you install it. To install, run the following command:
pip install python-lzf
按照提示去安装 python-lzf,结果又报错了...
[root@localhost mnt]# pip install python-lzf
Collecting python-lzf==0.2.4
Using cached
https://files.pythonhosted.org/p
Installing collected packages: python-lzf
Running setup.py install for python-lzf ... error
Complete output from command /usr/bin/python2 -u -c "import setuptools, tokenize;__file__='/tmp/pip-install-2h02L5/python-lzf/setup.py';f=getattr(tokenize, 'open', open) (__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install -- record /tmp/pip-record-IPT2FC/install-record.txt --single-version-externally-managed --compile:
running install
running build
running build_ext
building 'lzf' extension
creating build
creating build/temp.linux-x86_64-2.7
gcc -pthread -fno-strict-aliasing -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -DNDEBUG -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -fPIC -I. -I/usr/include/python2.7 -c lzf_module.c -o build/temp.linux-x86_64-2.7/lzf_module.o -Wall
lzf_module.c:3:20: fatal error: Python.h: No such file or directory
#include "Python.h"
^
compilation terminated.
error: command 'gcc' failed with exit status 1
----------------------------------------
Command "/usr/bin/python2 -u -c "import setuptools, tokenize;__file__='/tmp/pip-install-2h02L5/python- lzf/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record /tmp/pip-record-IPT2FC/install- record.txt --single-version-externally-managed --compile" failed with error code 1 in /tmp/pip-install- 2h02L5/python-lzf/
莫慌!运行 yum install python-devel
即可,此时再安装 python-lzf 并生成内存快照就好了。会生成一个 CSV 格式的内存报告。包含的列有:
- database :key在redis的db
- type :key类型
- key :key值
- size_in_bytes :key的内存大小(byte)
- encoding :value的存储编码形式
- num_elements :key中的value的个数
- len_largest_element :key中的value的长度
- expiry :key过期时间
分析内存快照
直接将 CSV 的数据导入到 MySQL,当然怎么顺手怎么来,导入 SQLLite,Postgres等都行,视个人而异,这样就可以利用sql语句很方便的对Redis的内存数据进行各种分析了,导入方法自行百度。
数据导入以后,接下来想怎么分析就怎么分析了,举几个简单的例子:
查询key的个数
select count(*) from memory;
查询总的内存占用
select sum(size_in_bytes) from memory;
查询内存占用最高的10个key
select * from memory order by size_in_bytes desc limit 10;
查询value个数1000个以上的list
select * from memory where type='list' and num_elements > 1000;
总结
可以看到内存占用最高的 Top100 全是应用市场业务组的,一个key最大34MB以上的 string 类型的 value,这一百个加起来就用了1.95G,虽然不像别的库key存了五十万,一百万,只存了900多个,但却占了95%的内存使用。
通过使用 redis-rdb-tools + mysql的方式,可以方便的对 redis 实例的内存情况进行静态的分析。整个过程也比较简单,获取到 rdb 之后即可。能够帮助排除业务中潜在的风险点,找到业务性能瓶颈。