26

Redis RDB 分析工具 rdbtools 说明

 3 years ago
source link: http://www.cnblogs.com/zhoujinyi/p/13276697.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

背景

Redis是基于内存的KV数据库,内存作为存储介质,关注其内存的使用情况是一个重要指标,解析其内部的存储信息是给出优化方法和维护的最基本要求。解析内存有二种方法:第一个是通过scan遍历所有key,针对每个key进行分析(memory usage);第二个是基于RDB文件进行所有key的分析( redis-rdb-tools )。本文将介绍如何使用 rdbtools 工具。

说明

rdbtools 工具包括了 3个可执行文件:

rdb  -- 解析整个rdb文件
redis-memory-for-key -- 解析server里的单个key
redis-profiler --解析rdb文件成html格式

rdb rdbtools工具包其中之一的工具,也 是解析dump.rdb文件的工具:分析内存并将数据导出到JSON,Rdbtools是Redis的dump.rdb文件的解析器,解析器生成类似于xml。rdbtools提供了以下实用程序:

  1. 生成所有数据库和键中数据的内存报告
  2. 将转储文件转换为JSON
  3. 使用标准差异工具比较两个转储文件

安装 rdbtools

前提条件:

  1. 安装 python-lzf :加快解析速度

    pip install python-lzf
    
  2. 安装redis-py:可选,仅在运行测试用例时需要

PyPI安装(推荐)

pip install rdbtools python-lzf

源码安装

git clone https://github.com/sripathikrishnan/redis-rdb-tools
cd redis-rdb-tools
sudo python setup.py install

命令行用法示例

help:

1, rdb --help:解析整个rdb文件

usage: rdb [options] /path/to/dump.rdb

Example : rdb --command json -k "user.*" /var/redis/6379/dump.rdb

positional arguments:
-- 要处理的dump文件
  dump_file             RDB Dump file to process       

optional arguments:
-- 帮助
  -h, --help            show this help message and exit  
-- 要处理的命令,-c后的有效参数为:json, diff,justkeys, justkeyvals, memory,protocol 
  -c CMD, --command CMD
                        Command to execute. Valid commands are json, diff,
                        justkeys, justkeyvals, memory and protocol  
-- 输出文件       
  -f FILE, --file FILE  Output file
-- 数据库号,可以提供多个数据库。如果未指定,则包括所有数据库。
  -n DBS, --db DBS      Database Number. Multiple databases can be provided.
                        If not specified, all databases will be included.
-- 要导出的key。这可以是一个正则表达式
  -k KEYS, --key KEYS   Keys to export. This can be a regular expression
-- key不导出。这可以是一个正则表达式
  -o NOT_KEYS, --not-key NOT_KEYS
                        Keys Not to export. This can be a regular expression
-- 解析的数据类型。可能的值为string,hash,set,sortedset,list。可以输入多种类型提供。如果未指定,则为所有数据类型
  -t TYPES, --type TYPES
                        Data types to include. Possible values are string,
                        hash, set, sortedset, list. Multiple typees can be
                        provided. If not specified, all data types will be
                        returned
--  将key的内存输出限制为大于或等此值(以字节为单位)
  -b BYTES, --bytes BYTES
                        Limit memory output to keys greater to or equal to
                        this value (in bytes)
--  将内存按大小输出前N个key                       
  -l LARGEST, --largest LARGEST
                        Limit memory output to only the top N keys (by size)
-- 将字符串转义为编码:raw(默认),print,utf8或base64。
  -e {raw,print,utf8,base64}, --escape {raw,print,utf8,base64}
                        Escape strings to encoding: raw (default), print,
                        utf8, or base64.
-- 使用command protocol参数,从所有键中删除到期的key                       
  -x, --no-expire       With protocol command, remove expiry from all keys
-- 使用command protocol参数,将N秒添加到key的到期时间
  -a N, --amend-expire N
                        With protocol command, add N seconds to key expiry
                        time

2,redis-memory-for-key --help:-- 解析server里指定的单个key

Usage: redis-memory-for-key [options] redis-key
Examples :
redis-memory-for-key user:13423
redis-memory-for-key -s localhost -p 6379 user:13423


Options:
-- 帮助
  -h, --help            show this help message and exit
-- 服务地址,默认127.0.0.1
  -s HOST, --server=HOST
                        Redis Server hostname. Defaults to 127.0.0.1
-- 服务端口,默认6379                        
  -p PORT, --port=PORT  Redis Server port. Defaults to 6379
--服务密码
  -a PASSWORD, --password=PASSWORD
                        Password to use when connecting to the server
-- 数据库号,默认0
  -d DB, --db=DB        Database number, defaults to 0 

3,redis-profiler --help:

Usage: redis-profiler [options] /path/to/dump.rdb

Example 1 : redis-profiler -k "user.*" -k "friends.*" -f memoryreport.html /var/redis/6379/dump.rdb
Example 2 : redis-profiler /var/redis/6379/dump.rdb

Options:
-- 帮助
  -h, --help            show this help message and exit
-- 输出
  -f FILE, --file=FILE  Output file
-- 组合在一起的键。 多个正则表达式
  -k KEYS, --key=KEYS   Keys that should be grouped together. Multiple regexes
                        can be provided

每次运行以上工具时都需要指定一个命令,以指示对解析的RDB数据应执行的操作。 操作有:

转储的JSON:

> rdb --command json /var/redis/6379/dump.rdb

[{
"user003":{"fname":"Ron","sname":"Bumquist"},
"lizards":["Bush anole","Jackson's chameleon","Komodo dragon","Ground agama","Bearded dragon"],
"user001":{"fname":"Raoul","sname":"Duke"},
"user002":{"fname":"Gonzo","sname":"Dr"},
"user_list":["user003","user002","user001"]},{
"baloon":{"helium":"birthdays","medical":"angioplasty","weather":"meteorology"},
"armadillo":["chacoan naked-tailed","giant","Andean hairy","nine-banded","pink fairy"],
"aroma":{"pungent":"vinegar","putrid":"rotten eggs","floral":"roses"}}]

过滤解析:

正则表达式匹配key,并且仅打印键和值:

> rdb --command justkeyvals --key "user.*" /var/redis/6379/dump.rdb

user003 fname Ron,sname Bumquist,
user001 fname Raoul,sname Duke,
user002 fname Gonzo,sname Dr,
user_list user003,user002,user001

仅处理数据库2中hash类型的a开头的key:

> rdb -c json --db 2 --type hash --key "a.*" /var/redis/6379/dump.rdb

[{},{
"aroma":{"pungent":"vinegar","putrid":"rotten eggs","floral":"roses"}}]

dump文件转换为JSON:

输出是UTF-8编码的JSON。 默认情况下,回调尝试使用UTF-8解析RDB数据,并使用\U表示符转义非'ASCII可打印'字符,或使用\x转义非UTF-8可解析的字节。 尝试对RDB数据进行解码可能会导致二进制数据错误,可以通过使用--escape raw选项来避免这种情况。 另一种选择是使用-e base64进行二进制数据的Base64编码。 

解析dump 文件并在标准输出上打印JSON:

> rdb -c json /var/redis/6379/dump.rdb

[{
"Citat":["B\u00e4ttre sent \u00e4n aldrig","Bra karl reder sig sj\u00e4lv","Man ska inte k\u00f6pa grisen i s\u00e4cken"],
"bin_data":"\\xFE\u0000\u00e2\\xF2"}]

将dump文件解析为原始字节,并在标准输出上打印JSON:

> rdb -c json /var/redis/6379/dump.rdb --escape raw

[{
"Citat":["B\u00c3\u00a4ttre sent \u00c3\u00a4n aldrig","Bra karl reder sig sj\u00c3\u00a4lv","Man ska inte k\u00c3\u00b6pa grisen i s\u00c3\u00a4cken"],
"bin_data":"\u00fe\u0000\u00c3\u00a2\u00f2"}]

生成内存报告:

使用-c memory 运行会生成CSV报告,其中包含该键使用的近似内存。 --bytes C 和 --largest N 可用于将输出限制为大于C字节的键或N个最大键。

> rdb -c memory /var/redis/6379/dump.rdb --bytes 128 -f memory.csv
> cat memory.csv

database,type,key,size_in_bytes,encoding,num_elements,len_largest_element
0,list,lizards,241,quicklist,5,19
0,list,user_list,190,quicklist,3,7
2,hash,baloon,138,ziplist,3,11
2,list,armadillo,231,quicklist,5,20
2,hash,aroma,129,ziplist,3,11

生成的CSV具有以下列:

database:数据库编号
type:数据类型
key:键
size_in_bytes:使用的内存:包括键,值和任何其他开销
encoding:RDB编码类型
num_elements:key中的value的个数
len_largest_element:key中的value的长度
expiry:过期值

注意:内存使用情况是近似的。 通常,实际使用的内存将略高于报告的内存。 可以按键或数据库编号或数据类型过滤报告。 内存报告应有助于检测由应用程序逻辑引起的内存泄漏。 它还将帮助优化Redis的内存使用。

查找单键使用的内存:

查找特定键使用的内存(运行整个内存报告非常耗时),使用redis-memory-for-key:

> redis-memory-for-key person:1

> redis-memory-for-key -s localhost -p 6379 -a mypassword person:1

Key             person:1
Bytes                111
Type                hash
Encoding            ziplist
Number of Elements        2
Length of Largest Element    8

比较RDB文件:

使用--command diff选项,并将输出通过管道传递到标准sort:

> rdb --command diff /var/redis/6379/dump1.rdb | sort > dump1.txt
> rdb --command diff /var/redis/6379/dump2.rdb | sort > dump2.txt

运行差异程序:

> kdiff3 dump1.txt dump2.txt

要限制文件的大小,可以使用--key选项过滤键

使用Redis协议:

使用 protocol 命令将RDB文件转换为redis协议流:

> rdb -c protocol /var/redis/6379/dump.rdb

*4
$4
HSET
$9
users:123
$9
firstname
$8
Sripathi

可以将输出通过管道传输到netcat并重新导入数据的子集。如果要将数据在两个Redis实例上共享,则可以使用--key标志选择数据的子集,然后将输出传递给正在运行的Redis实例并加载该数据。 当输出打印协议时,--escape选项可以避免出现不可打印/控制字符。

默认情况下,如果过期时间在rdb文件中存在,则会删除过去所有过期的键。 如果不需要此行为,则使用-x/--no-expire选项将忽略所有关键的到期命令。 使用-a/--amend-expire选项设置将来的到期时间,该选项会为已设置为到期的每个密钥的到期时间增加整数秒,不会更改尚未设置有效期的key。

使用解析器(Python):


from rdbtools import RdbParser, RdbCallback
from rdbtools.encodehelpers import bytes_to_unicode

class MyCallback(RdbCallback):
    ''' Simple example to show how callback works.
        See RdbCallback for all available callback methods.
        See JsonCallback for a concrete example
    '''

    def __init__(self):
        super(MyCallback, self).__init__(string_escape=None)

    def encode_key(self, key):
        return bytes_to_unicode(key, self._escape, skip_printable=True)

    def encode_value(self, val):
        return bytes_to_unicode(val, self._escape)

    def set(self, key, value, expiry, info):
        print('%s = %s' % (self.encode_key(key), self.encode_value(value)))

    def hset(self, key, field, value):
        print('%s.%s = %s' % (self.encode_key(key), self.encode_key(field), self.encode_value(value)))

    def sadd(self, key, member):
        print('%s has {%s}' % (self.encode_key(key), self.encode_value(member)))

    def rpush(self, key, value):
        print('%s has [%s]' % (self.encode_key(key), self.encode_value(value)))

    def zadd(self, key, score, member):
        print('%s has {%s : %s}' % (str(key), str(member), str(score)))


callback = MyCallback()
parser = RdbParser(callback)
parser.parse('/var/redis/6379/dump.rdb')

View Code 

测试说明

一、rdb:根据要求分析这个RDB文件

  1. 按json格式导出rdb: rdb --command  json dump.rdb 
    
    # rdb -c json dump.rdb 
    
    [{
    "list_c":["z","y","x"],
    "zset_a":{"zjy":"10","zzz":"11","zjj":"12","zjq":"13"},
    "hash_c":{"name":"zjy","age":"30","address":"hz"},
    "set_a":["c","a","b"],
    "list_a":["d","c","b","a"],
    "string_b":"BINLOG_START_FILE_NAME, --binlog_start_file_name=BINLOG_START_FILE_NAME",
    "string_c":"\u6211\u4eec\u7684\u5929\u7a7a",
    "zset_c":{"mysql":"100","redis":"200","mongodb":"300"},
    "string_a":"XR-14772977XR-14772977XR-14772977XR-14772977XR-14772977XR-14772977XR-14772977XR-14772977XR-147729771111aaasssss",
    "set_b":["x","z","y"],
    "hash_a":{"a":"a","b":"b","c":"c","d":"d"},
    "list_b":["mongodb","redis","mysql"],
    "zset_b":{"a":"1","b":"2","c":"3","d":"4"},
    "set_c":["jll","zxx","zjy","zjj"],
    "hash_b":{"x":"x","y":"y","z":"z"}}]
    
    View Code
  2. 导出rdb中的keys:rdb -c justkeys dump.rdb

    
    # rdb -c justkeys dump.rdb|uniq
    
    list_c
    zset_a
    hash_c
    set_a
    list_a
    string_b
    string_c
    zset_c
    string_a
    set_b
    hash_a
    list_b
    zset_b
    set_c
    hash_b
    
    View Code
  3. 导出rdb中的values

    rdb -c justkeyvals dump.rdb

    
    # rdb -c justkeyvals dump.rdb
    
    list_c z,y,x,
    zset_a zjy 10,zzz 11,zjj 12,zjq 13,
    hash_c name zjy,age 30,address hz,
    set_a c,a,b,
    list_a d,c,b,a,
    string_b BINLOG_START_FILE_NAME, --binlog_start_file_name=BINLOG_START_FILE_NAME,
    string_c 我们的天空,
    zset_c mysql 100,redis 200,mongodb 300,
    string_a XR-14772977XR-14772977XR-14772977XR-14772977XR-14772977XR-14772977XR-14772977XR-14772977XR-147729771111aaasssss,
    set_b x,z,y,
    hash_a a a,b b,c c,d d,
    list_b mongodb,redis,mysql,
    zset_b a 1,b 2,c 3,d 4,
    set_c jll,zxx,zjy,zjj,
    
    View Code
  4. 导出rdb中keys的内存分析:

    rdb -c memory dump.rdb

    
    ]# rdb -c memory dump.rdb
    database,type,key,size_in_bytes,encoding,num_elements,len_largest_element,expiry
    
    0,list,list_c,148,quicklist,3,1,
    0,sortedset,zset_a,88,ziplist,4,3,
    0,hash,hash_c,91,ziplist,3,8,
    0,set,set_a,284,hashtable,3,1,
    0,list,list_a,151,quicklist,4,1,
    0,string,string_b,136,string,71,71,
    0,string,string_c,80,string,15,15,
    0,sortedset,zset_c,93,ziplist,3,7,
    0,string,string_a,184,string,111,111,
    0,set,set_b,284,hashtable,3,1,
    0,hash,hash_a,83,ziplist,4,1,
    0,list,list_b,162,quicklist,3,7,
    0,sortedset,zset_b,79,ziplist,4,1,
    0,set,set_c,364,hashtable,4,3,
    0,hash,hash_b,77,ziplist,3,1,
    
    View Code
  5. 按RESP协议导出RDB内容:

    rdb -c protocol dump.rdb

    
    -- RESP
    # rdb -c protocol dump.rdb
    *2
    $6
    SELECT
    $1
    0
    *3
    $5
    RPUSH
    $6
    list_c
    $1
    z
    *3
    $5
    RPUSH
    $6
    list_c
    $1
    y
    *3
    $5
    RPUSH
    $6
    list_c
    $1
    x
    *4
    $4
    ZADD
    $6
    zset_a
    $2
    ...
    ...
    
    -- 管道导入
    # rdb --command protocol dump.rdb | nc 192.168.163.134 7777
    +OK
    :1
    :2
    :3
    :1
    :1
    :1
    :1
    :1
    :1
    :1
    :1
    :1
    :1
    :1
    :2
    :3
    :4
    +OK
    +OK
    :1
    :1
    :1
    +OK
    :1
    :1
    ...
    ...
    
    View Code
  6. 分析RDB结果导出到文件:

    rdb -c memory dump.rdb -f ttt.txt

    
    # cat ttt.txt 
    database,type,key,size_in_bytes,encoding,num_elements,len_largest_element,expiry
    0,list,list_c,148,quicklist,3,1,
    0,sortedset,zset_a,88,ziplist,4,3,
    0,hash,hash_c,91,ziplist,3,8,
    0,set,set_a,284,hashtable,3,1,
    0,list,list_a,151,quicklist,4,1,
    0,string,string_b,136,string,71,71,
    0,string,string_c,80,string,15,15,
    0,sortedset,zset_c,93,ziplist,3,7,
    0,string,string_a,184,string,111,111,
    0,set,set_b,284,hashtable,3,1,
    0,hash,hash_a,83,ziplist,4,1,
    0,list,list_b,162,quicklist,3,7,
    0,sortedset,zset_b,79,ziplist,4,1,
    0,set,set_c,364,hashtable,4,3,
    0,hash,hash_b,77,ziplist,3,1,
    
    View Code
  7. 导出指定数据库的keys: rdb -c justkeyvals dump.rdb -n

    0

    
    rdb -c justkeyvals dump.rdb -n 0
    
    list_c z,y,x,
    zset_a zjy 10,zzz 11,zjj 12,zjq 13,
    hash_c name zjy,age 30,address hz,
    set_a c,a,b,
    list_a d,c,b,a,
    string_b BINLOG_START_FILE_NAME, --binlog_start_file_name=BINLOG_START_FILE_NAME,
    string_c 我们的天空,
    zset_c mysql 100,redis 200,mongodb 300,
    string_a XR-14772977XR-14772977XR-14772977XR-14772977XR-14772977XR-14772977XR-14772977XR-14772977XR-147729771111aaasssss,
    set_b x,z,y,
    hash_a a a,b b,c c,d d,
    list_b mongodb,redis,mysql,
    zset_b a 1,b 2,c 3,d 4,
    set_c jll,zxx,zjy,zjj,
    
    View Code
  8. 导出 匹配(正则)的 keys: rdb --command justkeyvals --key ".*set*"  dump.rdb
    
    -- 导出包含set关键词的key:
    rdb -c justkeyvals -k ".*set*" dump.rdb -n 0
    
    zset_a zjy 10,zzz 11,zjj 12,zjq 13,
    set_a c,a,b,
    zset_c mysql 100,redis 200,mongodb 300,
    set_b x,z,y,
    zset_b a 1,b 2,c 3,d 4,
    
    View Code
  9. 不导出匹配(正则)的keys:

    rdb --command justkeyvals --not-key ".*set*" dump.rdb

    
    -- 导出除有set字符串之外的keys:
    # rdb --command justkeyvals --not-key ".*set*" dump.rdb 
    
    list_c z,y,x,
    hash_c name zjy,age 30,address hz,
    list_a d,c,b,a,
    string_b BINLOG_START_FILE_NAME, --binlog_start_file_name=BINLOG_START_FILE_NAME,
    string_c 我们的天空,
    string_a XR-14772977XR-14772977XR-14772977XR-14772977XR-14772977XR-14772977XR-14772977XR-14772977XR-147729771111aaasssss,
    hash_a a a,b b,c c,d d,
    list_b mongodb,redis,mysql,
    
    View Code
  10. 导出指定类型的keys:

    rdb --command json --type hash dump.rdb

    
    -- 只解析hash类型的keys:
    # rdb --command json --type hash dump.rdb 
    [{
    "hash_c":{"name":"zjy","age":"30","address":"hz"},
    "hash_a":{"a":"a","b":"b","c":"c","d":"d"},
    "hash_b":{"x":"x","y":"y","z":"z"}}]
    
    View Code
  11. 导出大于指定字节的keys:

    rdb --command memory --bytes 128 dump.rdb 

    
    -- 指定大于等于128字节的key:
    # rdb --command memory --bytes 128  dump.rdb 
    database,type,key,size_in_bytes,encoding,num_elements,len_largest_element,expiry
    0,list,list_c,148,quicklist,3,1,
    0,set,set_a,284,hashtable,3,1,
    0,list,list_a,151,quicklist,4,1,
    0,string,string_b,136,string,71,71,
    0,string,string_a,184,string,111,111,
    0,set,set_b,284,hashtable,3,1,
    0,list,list_b,162,quicklist,3,7,
    0,set,set_c,364,hashtable,4,3,
    
    View Code
  12.  

    导出内存字节排名前3个keys:rdb --command memory --largest 3 dump.rdb 

    
    -- 内存排名前3:
    # rdb --command memory --largest 3 dump.rdb 
    database,type,key,size_in_bytes,encoding,num_elements,len_largest_element,expiry
    0,set,set_c,364,hashtable,4,3,
    0,set,set_a,284,hashtable,3,1,
    0,set,set_b,284,hashtable,3,1,
    
    View Code
  13. 导出指定编码转义:rdb --command justkeyvals --escape raw dump.rdb

    
    # rdb --command justkeyvals --escape raw dump.rdb 
    
    list_c z,y,x,
    zset_a zjy 10,zzz 11,zjj 12,zjq 13,
    hash_c name zjy,age 30,address hz,
    set_a c,a,b,
    list_a d,c,b,a,
    string_b BINLOG_START_FILE_NAME, --binlog_start_file_name=BINLOG_START_FILE_NAME,
    string_c 我们的天空,
    zset_c mysql 100,redis 200,mongodb 300,
    string_a XR-14772977XR-14772977XR-14772977XR-14772977XR-14772977XR-14772977XR-14772977XR-14772977XR-147729771111aaasssss,
    set_b x,z,y,
    hash_a a a,b b,c c,d d,
    list_b mongodb,redis,mysql,
    zset_b a 1,b 2,c 3,d 4,
    set_c jll,zxx,zjy,zjj,
    
    View Code
  14. 导出keys(过期keys除外) rdb --command memory --no-expire  dump.rdb  
  15. 导出keys(给过期keys添加时间): rdb --command memory --amend-expire 100 dump.rdb 

以上操作参数可以相互叠加使用,按照实际要求进行组合。并且可以导出成csv文件,导入到数据库里进行聚合统计和监控。

二、redis-memory-for-key:查看指定key的内存

查看指定key的内存分析情况: redis-memory-for-key --server =192.168.163.134 --port =8379 f


-- 查看该服务器上key为f的内存情况:
# redis-memory-for-key --server=192.168.163.134 --port=8379 f
Key                f
Bytes                56
Type                string

View Code

三、redis-profiler:RDB分析生成html


-- RDB分析结果到html文件
# redis-profiler dump.rdb -f pp.html

View Code

分析后的效果图(一部分)如:

FRfAnmV.png!web

总结

通过本文对于 rdbtools 说明,能够更好的解析RDB其内部的存储信息,从而方便给出优化和维护的建议,关于 rdbtools 更多的说明可以看官网。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK