redis占用內(nèi)存診斷過程

開篇

?寫這篇文章是因為近期在準備雙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ì)上解決問題,不加分析就直接擴容其實在某種程度上掩蓋了所有問題,不可??!

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容