www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁 > 公眾號(hào)精選 > 架構(gòu)師社區(qū)
[導(dǎo)讀]“ 由于有一條業(yè)務(wù)線不理想,高層決定下架業(yè)務(wù)。對(duì)于我們技術(shù)團(tuán)隊(duì)而言,其對(duì)應(yīng)的所有服務(wù)器資源和其他相關(guān)資源都要釋放。 圖片來自 Pexels 釋放了 8 臺(tái)應(yīng)用服務(wù)器;1 臺(tái) ES 服務(wù)器;刪除分布式定時(shí)任務(wù)中心相關(guān)的業(yè)務(wù)任務(wù);備份并刪除 MySQL 數(shù)據(jù)庫;刪除 Red

由于有一條業(yè)務(wù)線不理想,高層決定下架業(yè)務(wù)。對(duì)于我們技術(shù)團(tuán)隊(duì)而言,其對(duì)應(yīng)的所有服務(wù)器資源和其他相關(guān)資源都要釋放。


熬了一個(gè)通宵,終于把7千萬個(gè)Key刪完了

圖片來自 Pexels


釋放了 8 臺(tái)應(yīng)用服務(wù)器;1 臺(tái) ES 服務(wù)器;刪除分布式定時(shí)任務(wù)中心相關(guān)的業(yè)務(wù)任務(wù);備份并刪除 MySQL 數(shù)據(jù)庫;刪除 Redis 中相關(guān)的業(yè)務(wù)緩存數(shù)據(jù)。


CTO 指名點(diǎn)姓讓我?guī)ь^沖鋒,才扣了我績(jī)效……好吧,沖~


其他都還好,不多時(shí)就解決了。唯獨(dú)這刪除 Redis 中的數(shù)據(jù),害得我又熬了一個(gè)通宵,真是折煞我也!


難點(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)存,還算豪華吧~)


集群配置如下:

熬了一個(gè)通宵,終于把7千萬個(gè)Key刪完了

在這種共用集群的情況下,導(dǎo)致無法簡(jiǎn)單粗暴的釋放。因此只能選擇刪除 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 命名:

熬了一個(gè)通宵,終于把7千萬個(gè)Key刪完了

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


解決思路


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

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

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

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


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


幸而 Idea 的搜索大法好,這個(gè)項(xiàng)目中使用的是 spring-boot-starter-data-redis。


因此我通過搜索 RedisTemplate 和 StringRedisTemplate 定位所有操作 Redis 的代碼。


具體步驟如下:
  • 通過這些代碼統(tǒng)計(jì)出 Key 的前綴并錄入到文本中。

  • 通過 Python 腳本把載入文中中的的 Key 并在后面加上“*”通配符。

  • 通過 Python 腳本通過 Scan 命令掃描出這些 Key。

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


關(guān)鍵代碼如下:

def get_key(rdbConn,start):
    try:
    keys_list = rdbConn.scan(start,count=2000)
    return keys_list
    except Exception,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
    except Exception,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:

熬了一個(gè)通宵,終于把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")
public class RedisKeyPrefixProperties {
    private Boolean enable = Boolean.TRUE;
    private String key;
    public Boolean getEnable() {
        return enable;
    }
    public void setEnable(Boolean enable) {
        this.enable = enable;
    }
    public String getKey() {
        return key;
    }
    public void setKey(String key) {
        this.key = key;
    }
}

/**
 * @desc 對(duì)字符串序列化新增前綴
 * @author create by liming sun on 2020-07-21 14:09:51
 */

public class PrefixStringKeySerializer extends StringRedisSerializer {
    private Charset charset = StandardCharsets.UTF_8;
    private RedisKeyPrefixProperties prefix;

    public PrefixStringKeySerializer(RedisKeyPrefixProperties prefix) {
        super();
        this.prefix = prefix;
    }

    @Override
    public String deserialize(@Nullable byte[] bytes) {
        String saveKey = new String(bytes, charset);
        if (prefix.getEnable() != null && prefix.getEnable()) {
            String prefixKey = spliceKey(prefix.getKey());
            int indexOf = saveKey.indexOf(prefixKey);
            if (indexOf > 0) {
                saveKey = saveKey.substring(indexOf);
            }
        }
        return (saveKey.getBytes() == null ? null : saveKey);
    }

    @Override
    public byte[] serialize(@Nullable String key) {
        if (prefix.getEnable() != null && prefix.getEnable()) {
            key = spliceKey(prefix.getKey()) + key;
        }
        return (key == null ? null : key.getBytes(charset));
    }

    private String spliceKey(String prefixKey) {
        if (StringUtils.isNotBlank(prefixKey) && !prefixKey.endsWith(":")) {
            prefixKey = prefixKey + "::";
        }
        return prefixKey;
    }
}


使用效果:為了避免再次發(fā)生這種工作低效而又不得不做的事情,我們?cè)陂_發(fā)規(guī)范中規(guī)定,新項(xiàng)目中 Redis 的使用必須設(shè)置此配置,前綴就設(shè)置為:項(xiàng)目編號(hào)。


另外,一個(gè)模塊中的 Key 必須統(tǒng)一定義在二方庫的 RedisKeyConstant 類中。


配置如下:

spring: 
    redis: 
        prefix:
            enabletrue
            key: E00P01

@Bean
public RedisTemplate<StringObject> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
    RedisTemplate<StringObject> redisTemplate = new RedisTemplate<>();
    redisTemplate.setConnectionFactory(redisConnectionFactory);
    // 支持key前綴設(shè)置的key Serializer
    redisTemplate.setKeySerializer(new PrefixStringKeySerializer());
    redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    return redisTemplate;
}

通過以上方式,我們至少可以從項(xiàng)目維度來區(qū)分出 Key,避免了多個(gè)項(xiàng)目之間共用同一個(gè)集群時(shí)而導(dǎo)致重復(fù) Key 的問題。


從項(xiàng)目維度對(duì) Key 進(jìn)行了劃分。更方便管理和運(yùn)維。如果對(duì)于 Key 的管理粒度要求更細(xì),我們甚至可以細(xì)化到具體業(yè)務(wù)維度。


我們?cè)跍y(cè)試環(huán)境進(jìn)行了壓測(cè),增加 Key 前綴對(duì) Redis 性能幾乎沒有影響。性能方面能接受。

總結(jié)


通過本次事情,我發(fā)現(xiàn)對(duì)于大多數(shù)開發(fā)者而言,差距其實(shí)不在于智力,而是在于態(tài)度。


比如這次事件暴露出來的問題:大家都知道要遵循開發(fā)規(guī)范,然而到了真正“打仗”的時(shí)候,負(fù)責(zé)這個(gè)項(xiàng)目的開發(fā)者卻沒有幾個(gè)人能始終如一的做好這些細(xì)微之事。


另外,Reviewer 的工作其實(shí)是極其重要的,他就像那“紀(jì)檢委”,如果“紀(jì)檢委”都放水睜一只眼閉一只眼,那麻煩可就大了!千里之提,毀于日常的點(diǎn)滴松懈?。?/span>


經(jīng)過這次事件之后,如果上天再給一次這樣的機(jī)會(huì),我一定會(huì)對(duì)項(xiàng)目經(jīng)理說:接著奏樂,接著舞!


作者:浪漫先生 && 51cto技術(shù)棧整理

出處:https://juejin.im/post/5f18423fe51d453493113f5c


特別推薦一個(gè)分享架構(gòu)+算法的優(yōu)質(zhì)內(nèi)容,還沒關(guān)注的小伙伴,可以長(zhǎng)按關(guān)注一下:

熬了一個(gè)通宵,終于把7千萬個(gè)Key刪完了

熬了一個(gè)通宵,終于把7千萬個(gè)Key刪完了

熬了一個(gè)通宵,終于把7千萬個(gè)Key刪完了

長(zhǎng)按訂閱更多精彩▼

熬了一個(gè)通宵,終于把7千萬個(gè)Key刪完了

如有收獲,點(diǎn)個(gè)在看,誠(chéng)摯感謝

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問題,請(qǐng)聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
關(guān)閉
關(guān)閉