開篇
?寫這篇文章是因為近期在準備雙11大促資源的盤點,盤點過程中發(fā)現(xiàn)部門占用的redis空間總共720G已經(jīng)接近占滿了,正常情況下第一反應是聯(lián)系采購新的服務器擴容內(nèi)存,但是因為好奇我們的redis集群內(nèi)部數(shù)據(jù)占用情況而打算先分析一下,這才有這篇文章,也給所有想對redis內(nèi)存存儲一窺究竟的同學提供一個思路。
推薦兩個工具
?在github上有兩個分析redis rdb文件的開源工具redis-rdb-tools,rdr。redis-rdb-tools用于分析所有key及占用空間;rdr能夠分析出所有key但是沒法計算key占用空間,不過額外提供圖形化界面。
redis-rdb-tools介紹
安裝介紹
- 提供多種途徑進行安裝,看心情選擇其中一個即可
Rdbtools is a parser for Redis' dump.rdb files.
The parser generates events similar to an xml sax parser, and is very efficient memory wise.
In addition, rdbtools provides utilities to :
1. Generate a Memory Report of your data across all databases and keys
2. Convert dump files to JSON
3. Compare two dump files using standard diff tools
Pre-Requisites :
1. python-lzf is optional but highly recommended to speed up parsing.
2. redis-py is optional and only needed to run test cases.
To install from PyPI (recommended) :
1.pip install rdbtools python-lzf
To install from source :
1.git clone https://github.com/sripathikrishnan/redis-rdb-tools
2.cd redis-rdb-tools
3.sudo python setup.py install
功能分析
- 解析rdb文件所有的key/value的內(nèi)容
- 解析rdb文件所有的key以及對應的value的大小
生成所有的key/value
> 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/value以及對應的存儲空間
> 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
rdr介紹
安裝介紹
下載:http://ohjx11q65.bkt.clouddn.com/rdr
賦予執(zhí)行權限:$ chmod a+x ./rdr
功能分析
- 解析rdb文件中所有的key的值
$ ./rdr keys example.rdb
portfolio:stock_follower_count:ZH314136
portfolio:stock_follower_count:ZH654106
portfolio:stock_follower:ZH617824
portfolio:stock_follower_count:ZH001019
portfolio:stock_follower_count:ZH346349
portfolio:stock_follower_count:ZH951803
portfolio:stock_follower:ZH924804
portfolio:stock_follower_count:INS104806
- 解析rdb并顯示key對應value的近似占用
$ ./rdr show -p 8080 *.rdb
show example
問題定位過程
- 通過redis-rdb-tools工具導出所有的key以及對應的占用情況
- 通過linux的sort命令按照key的占用空間進行倒序進行排列
- 通過rdr工具導出所有的key占用情況并和redis-rdb-tools工具導出所有的key進行對比
- 開始懷疑大hash的并通過scan溫和的導出所有hash的key,通過hlen以及hscan觀察hash中filed對應的value大小,確定hash結構的變量存在問題
- 因為業(yè)務屬性我們的key最多保存7天,針對所有的hash類的key需要重點關注TTL時間
- 最后通過python腳本連接redis集群,結合scan、hscan、del等命令刪除所有垃圾數(shù)據(jù)
- 中間走過的一個彎路是懷疑redis的key/value額外占用了太多空間,后來網(wǎng)上資料發(fā)現(xiàn)一個key最多也就額外占用50Bypte左右,不可能造成大量內(nèi)存占用,所以可以排除這個方向。
工具使用的截圖



溫和清理redis的hash數(shù)據(jù)
定位hash數(shù)據(jù)結構的key
- 切記一定使用scan!切記一定使用scan!切記一定使用scan!
- 通過scan加前綴匹配來掃面所有相關的hash的key,然后篩選其中待刪除的key
from rediscluster import StrictRedisCluster
from redis import Redis
import time
import threading
conn_list = [
"1.1.1.1:2222",
"2.2.2.2:222",
]
def scan_key(conn):
data = conn.split(":")
print data[0],data[1]
redis_client = Redis(data[0], data[1])
cursor = 0
count = 0
while True:
// 根據(jù)前綴如fuck_redis進行掃描
scan_result = redis_client.scan(cursor, "fuck_redis*", 400)
cursor = scan_result[0]
key_list = scan_result[1]
if len(key_list) > 0:
for key in key_list:
# redis_cluster.delete(key)
print key, redis_client.ttl(key)
if cursor == 0:
break
for conn in conn_list:
// 啟動多線程進行key的掃描
new_thread = threading.Thread(target=scan_key, args=(conn,))
new_thread.start()
刪除hash數(shù)據(jù)結構的key
- 切記用hscan先刪除hash的field,然后再刪除hash的key!切記用hscan先刪除hash的field,然后再刪除hash的key!切記用hscan先刪除hash的field,然后再刪除hash的key!
- 清理hash的field后再刪除hash的key,防止redis阻塞造成服務抖動!
from rediscluster import StrictRedisCluster
nodes = [
{"host": "1.1.1.1", "port": "6451"}
]
// redis3.0內(nèi)部通過集群方式進行刪除
redis_cluster = StrictRedisCluster(startup_nodes=nodes, decode_responses=True)
// 待刪除的hash key
key_list = [xxx__ooo_0"]
// 遍歷所有待刪除的key進行溫和刪除
for hash_key in key_list:
cursor = 0
count = 0
while True:
// 溫和的采用hscan進行遍歷刪除
scan_result = redis_cluster.hscan(hash_key, cursor, count=200)
cursor = scan_result[0]
result = scan_result[1]
for key in result:
redis_cluster.hdel(hash_key, key)
if count%200 == 0:
print hash_key, key
if cursor == 0:
redis_cluster.delete(hash_key)
break
結束語
深入到細節(jié)才能從本質(zhì)上解決問題,不加分析就直接擴容其實在某種程度上掩蓋了所有問題,不可??!