溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

怎么把Reids的7千萬個(gè)Key刪完

發(fā)布時(shí)間:2021-10-28 17:41:08 來源:億速云 閱讀:107 作者:iii 欄目:web開發(fā)

本篇內(nèi)容主要講解“怎么把Reids的7千萬個(gè)Key刪完”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“怎么把Reids的7千萬個(gè)Key刪完”吧!

難點(diǎn)分析

共用Redis服務(wù)集群

由于這條業(yè)務(wù)線的數(shù)據(jù)在Redis大概在3G左右,完全沒必要單獨(dú)建一個(gè)Redis服務(wù)集群,本著能節(jié)約就節(jié)約的態(tài)度,當(dāng)初就決定和其他項(xiàng)目共享一個(gè)集群(這個(gè)集群配置:16個(gè)節(jié)點(diǎn),128G內(nèi)存,還算豪華吧~)集群配置如下:

怎么把Reids的7千萬個(gè)Key刪完

在這種共用集群的情況下,導(dǎo)致無法簡單粗暴的釋放。因此只能選擇刪除Key的方式。

Key命名不規(guī)范

要?jiǎng)h除Key,首先就要精準(zhǔn)的定位出哪些Key需要?jiǎng)h除,如果勿刪Key,會(huì)影響到其他服務(wù)正常運(yùn)轉(zhuǎn)!如果Key本身設(shè)置了過期時(shí)間,但有些數(shù)據(jù)需是持久化的。然而那該死的項(xiàng)目經(jīng)理一直催項(xiàng)目進(jìn)度,導(dǎo)致開發(fā)人員在開發(fā)過程中很多地方都沒有設(shè)計(jì)到位,比如Redis Key散落在項(xiàng)目代碼的每個(gè)角落;比如命名不是很規(guī)范。真不知道是怎么review代碼!哦,想必是沒有時(shí)間review,那該死的項(xiàng)目經(jīng)理……

我隨便截個(gè)支付服務(wù)中的Key命名:

怎么把Reids的7千萬個(gè)Key刪完

怎么樣?是不是覺得我們開發(fā)人員寫的代碼很low~別笑,在實(shí)際工作中,還有比這更low的!希望你別遇到,不然真的很痛苦~

解決思路

經(jīng)過以上的分析,我們簡單歸納如下:

  •  我們真正關(guān)心的是那些未設(shè)置過期時(shí)間的Key

  •  不能誤刪除Key,否則下個(gè)月績效也沒了

  •  由于Key的命名及使用及其不規(guī)范,導(dǎo)致Key的定位難度很大

看來,通過scan命令掃描匹配Key的方式行不通了。只能通過人肉搜索了~

幸而Idea的搜索大法好,這個(gè)項(xiàng)目中使用的是spring-boot-starter-data-redis.因此我通過搜索RedisTemplate和StringRedisTemplate定位所有操作redis的代碼,具體步驟如下:

1、通過這些代碼統(tǒng)計(jì)出Key的前綴并錄入到文本中;

2、通過python腳本把載入文中中的的Key并在后面加上“*”通配符;

3、通過python腳本通過scan命令掃描出這些key;

4、為了便于檢查,我們并沒有直接使用del命令刪除key,在刪除key之前,先通過debug object key的方式得到其序列化的長度,再執(zhí)行刪除并返回序列化長度。這樣,我們就可以統(tǒng)計(jì)出所有key的序列化長度來得到我們釋放的空間大小。關(guān)鍵代碼如下: 

def get_key(rdbConn,start):         try:         keys_list = rdbConn.scan(start,count=2000)         return keys_list         exceptException,e:         print e     ''' Redis DEBUG OBJECT command got key info '''     def get_key_info(rdbConn,keyName):         try:         rpiple = rdbConn.pipeline()         rpiple.type(keyName)         rpiple.debug_object(keyName)         rpiple.ttl(keyName)         key_info_list = rpiple.execute()         return key_info_list         exceptException,e:         print"INFO : ",e     def redis_key_static(key_info_list):         keyType = key_info_list[0]         keySize = key_info_list[1]['serializedlength']         keyTtl = key_info_list[2]         key_size_static(keyType,keySize,keyTtl)

通過以上方式,能夠統(tǒng)計(jì)出究竟釋放了多少內(nèi)存了。

由于這個(gè)集群是有這么接近7千萬個(gè)key:

怎么把Reids的7千萬個(gè)Key刪完

因此,等到了第二天天亮,我睡眼朦朧的看了一下,終于刪除完畢了,時(shí)間07:13...早高峰即將來臨……

知恥而后勇

從來沒有經(jīng)歷過因業(yè)務(wù)下線而清除資源的經(jīng)驗(yàn)。這次事情真心讓我覺得細(xì)微之處見真功夫的道理。如果一開始我們就能夠遵循開發(fā)規(guī)范來使用和設(shè)計(jì)redis key,也不至于浪費(fèi)這么多時(shí)間。為了讓key的命名和使用更加規(guī)范,以及今后避免再次遇到這種情況,下午睡醒之后,我就在redis公共組件庫里面添加了一個(gè)配置和自定義了key序列化,代碼如下:

@ConfigurationProperties(prefix = "spring.redis.prefix")  publicclassRedisKeyPrefixProperties{      privateBoolean enable = Boolean.TRUE;      privateString key;      publicBoolean getEnable() {          return enable;      }      publicvoid setEnable(Boolean enable) {          this.enable = enable;      }      publicString getKey() {          return key;      }      publicvoid setKey(String key) {          this.key = key;      }  }
/**   * @desc 對字符串序列化新增前綴   * @author create by liming sun on 2020-07-21 14:09:51   */  publicclassPrefixStringKeySerializerextendsStringRedisSerializer{      privateCharset charset = StandardCharsets.UTF_8;      privateRedisKeyPrefixProperties prefix;      publicPrefixStringKeySerializer(RedisKeyPrefixProperties prefix) {          super();          this.prefix = prefix;      }      @Override      publicString deserialize(@Nullablebyte[] bytes) {          String saveKey = newString(bytes, charset);          if(prefix.getEnable() != null&& prefix.getEnable()) {              String prefixKey = spliceKey(prefix.getKey());              int indexOf = saveKey.indexOf(prefixKey);              if(indexOf > 0) {                  saveKeysaveKey = saveKey.substring(indexOf);              }          }          return(saveKey.getBytes() == null? null: saveKey);      }      @Override      publicbyte[] serialize(@NullableString key) {          if(prefix.getEnable() != null&& prefix.getEnable()) {              key = spliceKey(prefix.getKey()) + key;          }          return(key == null? null: key.getBytes(charset));      }      privateString spliceKey(String prefixKey) {          if(StringUtils.isNotBlank(prefixKey) && !prefixKey.endsWith(":")) {              prefixKeyprefixKey = prefixKey + "::";          }          return prefixKey;      }  }

使用效果

為了避免再次發(fā)生這種工作低效而又不得不做的事情,我們在開發(fā)規(guī)范中規(guī)定,新項(xiàng)目中redis的使用必須設(shè)置此配置,前綴就設(shè)置為:項(xiàng)目編號(hào)。另外,一個(gè)模塊中的key必須統(tǒng)一定義在二方庫的RedisKeyConstant類中。配置如下:

spring:      redis:          prefix:              enable: true              key: E00P01
@Bean     publicRedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {         RedisTemplate<String, Object> redisTemplate = newRedisTemplate<>();         redisTemplate.setConnectionFactory(redisConnectionFactory);         // 支持key前綴設(shè)置的key Serializer         redisTemplate.setKeySerializer(newPrefixStringKeySerializer());         redisTemplate.setValueSerializer(newGenericJackson2JsonRedisSerializer());         return redisTemplate;     }

通過以上方式,我們至少可以從項(xiàng)目維度來區(qū)分出key,避免了多個(gè)項(xiàng)目之間共用同一個(gè)集群時(shí)而導(dǎo)致重復(fù)key的問題。從項(xiàng)目維度對key進(jìn)行了劃分。更方便管理和運(yùn)維。如果對于key的管理粒度要求更細(xì),我們甚至可以細(xì)化到具體業(yè)務(wù)維度。我們在測試環(huán)境進(jìn)行了壓測,增加key前綴對redis性能幾乎沒有影響。性能方面能接受。

到此,相信大家對“怎么把Reids的7千萬個(gè)Key刪完”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI