zoukankan      html  css  js  c++  java
  • Redis RDB 分析工具 rdbtools 说明

    背景

           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格式

    rdbrdbtools工具包其中之一的工具,也是解析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":["Bu00e4ttre sent u00e4n aldrig","Bra karl reder sig sju00e4lv","Man ska inte ku00f6pa grisen i su00e4cken"],
    "bin_data":"\xFEu0000u00e2\xF2"}]

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

    > rdb -c json /var/redis/6379/dump.rdb --escape raw
    
    [{
    "Citat":["Bu00c3u00a4ttre sent u00c3u00a4n aldrig","Bra karl reder sig sju00c3u00a4lv","Man ska inte ku00c3u00b6pa grisen i su00c3u00a4cken"],
    "bin_data":"u00feu0000u00c3u00a2u00f2"}]

    生成内存报告:

    使用-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":"u6211u4eecu7684u5929u7a7a",
      "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中的valuesrdb -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.csv
      # cat ttt.csv 
      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

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

    总结

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

  • 相关阅读:
    在Android工程中运行Java程序问题
    sklearn.neighbors.kneighbors_graph的简单属性介绍
    python中的“.T”操作
    numpy中关于*和dot的区别
    一个Window Service引发的感想
    项目管理之初步认识
    由敏捷开发中开发认领自己的工作内容的感想
    SQL Server2008 inner join多种方式的实践
    浅谈业务逻辑和技术哪个更重要
    敏捷人生之初步认识
  • 原文地址:https://www.cnblogs.com/zhoujinyi/p/13276697.html
Copyright © 2011-2022 走看看