背景
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提供了以下實用程序:
- 生成所有數據庫和鍵中數據的內存報告
- 將轉儲文件轉換為JSON
- 使用標准差異工具比較兩個轉儲文件
安裝 rdbtools
前提條件:
- 安裝 python-lzf :加快解析速度
pip install python-lzf
- 安裝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')
測試說明
一、rdb:根據要求分析這個RDB文件
- 按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"}}]
-
導出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
- 導出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,
- 導出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,
- 按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 ... ...
- 分析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,
- 導出指定數據庫的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,
- 導出匹配(正則)的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,
- 不導出匹配(正則)的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,
- 導出指定類型的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"}}]
- 導出大於指定字節的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,
- 導出內存字節排名前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,
- 導出指定編碼轉義: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,
- 導出keys(過期keys除外):rdb --command memory --no-expire dump.rdb
- 導出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
三、redis-profiler:RDB分析生成html

-- RDB分析結果到html文件 # redis-profiler dump.rdb -f pp.html
分析后的效果圖(一部分)如:
總結
通過本文對於rdbtools說明,能夠更好的解析RDB其內部的存儲信息,從而方便給出優化和維護的建議,關於rdbtools更多的說明可以看官網。