如何優雅的刪除Redis的大key

關於Redis大鍵(Key),咱們從[空間複雜性]和訪問它的[時間複雜度]兩個方面來定義大鍵。前者主要表示Redis鍵的佔用內存大小;後者表示Redis集合數據類型(set/hash/list/sorted set)鍵,所含有的元素個數。如下兩個示例:git

1個大小200MB的String鍵(String Object最大512MB),內存空間佔用較大;1個包含100000000(1kw)個字段的Hash鍵,對應訪問模式(如hgetall)時間複雜度高

由於內存空間複雜性處理耗時都很是小,測試 del 200MB String鍵耗時約1毫秒,而刪除一個含有1kw個字段的Hash鍵,卻會阻塞Redis進程數十秒。因此本文只從時間複雜度分析大的集合類鍵。刪除這種大鍵的風險,以及怎麼優雅地刪除。github

在Redis集羣中,應用程序儘可能避免使用大鍵;直接影響容易致使集羣的容量和請求出現」傾斜問題「,具體分析見文章:redis-cluster-imbalance。但在實際生產過程當中,總會有業務使用不合理,出現這類大鍵;當DBA發現後推動業務優化改造,而後刪除這個大鍵;若是直接刪除它,DEL命令可能阻塞Redis進程數十秒,對應用程序和Redis集羣可用性形成嚴重的影響。redis

1、直接刪除大Key的風險

DEL命令在刪除單個集合類型的Key時,命令的時間複雜度是O(M),其中M是集合類型Key包含的元素個數。測試

DEL keyTime complexity: O(N) where N is the number of keys that will be removed. When a key to remove holds a value other than a string, the individual complexity for this key is O(M) where M is the number of elements in the list, set, sorted set or hash. Removing a single key that holds a string value is O(1).優化

生產環境中遇到過屢次因業務刪除大Key,致使Redis阻塞,出現故障切換和應用程序雪崩的故障。測試刪除集合類型大Key耗時,通常每秒可清理100w~數百w個元素; 若是數千w個元素的大Key時,會致使Redis阻塞上10秒可能致使集羣判斷Redis已經故障,出現故障切換;或應用程序出現雪崩的狀況。this

說明:Redis是單線程處理。單個耗時過大命令,致使阻塞其餘命令,容易引發應用程序雪崩或Redis集羣發生故障切換。因此避免在生產環境中使用耗時過大命令。spa

Redis刪除大的集合鍵的耗時, 測試估算,可參考;和硬件環境、Redis版本和負載等因素有關線程

Key類型 Item數量 耗時
Hash ~100萬 ~1000ms
List ~100萬 ~1000ms
Set ~100萬 ~1000ms
Sorted Set ~100萬 ~1000ms

當咱們發現集羣中有大key時,要刪除時,如何優雅地刪除大Key?code

2、如何優雅地刪除各種大Key

從Redis2.8版本開始支持SCAN命令,經過m次時間複雜度爲O(1)的方式,遍歷包含n個元素的大key.這樣避免單個O(n)的大命令,致使Redis阻塞。 這裏刪除大key操做的思想也是如此。進程

2.1  Delete Large Hash Key

經過hscan命令,每次獲取500個字段,再用hdel命令,每次刪除1個字段。Python代碼:

def del_large_hash():
  r = redis.StrictRedis(host='redis-host1', port=6379)
    large_hash_key ="xxx" 
    cursor = '0'
    while cursor != 0:
        cursor, data = r.hscan(large_hash_key, cursor=cursor, count=500)
        for item in data.items():
                r.hdel(large_hash_key, item[0])

2.2  Delete Large Set Key

刪除大set鍵,使用sscan命令,每次掃描集合中500個元素,再用srem命令每次刪除一個鍵Python代碼:

def del_large_set():
  r = redis.StrictRedis(host='redis-host1', port=6379)
  large_set_key = 'xxx'   
  cursor = '0'
  while cursor != 0:
    cursor, data = r.sscan(large_set_key, cursor=cursor, count=500)
    for item in data:
      r.srem(large_size_key, item)

2.3  Delete Large List Key

刪除大的List鍵,未使用scan命令; 經過ltrim命令每次刪除少許元素。Python代碼:

def del_large_list():
  r = redis.StrictRedis(host='redis-host1', port=6379)
  large_list_key = 'xxx'  
  while r.llen(large_list_key)>0:
      r.ltrim(large_list_key, 0, -101)

2.4  Delete Large Sorted set key

刪除大的有序集合鍵,和List相似,使用sortedset自帶的zremrangebyrank命令,每次刪除top 100個元素。Python代碼:

def del_large_sortedset():
  r = redis.StrictRedis(host='large_sortedset_key', port=6379)
  large_sortedset_key='xxx'
  while r.zcard(large_sortedset_key)>0:
    r.zremrangebyrank(large_sortedset_key,0,99)

3、Redis Lazy Free

應該從3.4版本開始,Redis會支持lazy delete free的方式,刪除大鍵的過程不會阻塞正常請求。

相關文章
相關標籤/搜索