欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺談Redis高并發(fā)緩存架構(gòu)性能優(yōu)化實戰(zhàn)

 更新時間:2022年05月11日 14:35:00   作者:楓度柚子  
本文主要介紹了淺談Redis高并發(fā)緩存架構(gòu)性能優(yōu)化實戰(zhàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

場景1: 中小型公司Redis緩存架構(gòu)以及線上問題實戰(zhàn)

線程A在master獲取鎖之后,master在同步數(shù)據(jù)到slave時,master突然宕機(此時數(shù)據(jù)還沒有同步到slave),然后slave會自動選舉成為新的master,此時線程B獲取鎖,結(jié)果成功了,這樣會造成多個線程獲取同一把鎖

解決方案

  • 網(wǎng)上說RedLock能解決分布式鎖失效的問題。對于RedLock實現(xiàn)原理是: 超過半數(shù)Redis節(jié)點加鎖成功之后才能算成功,否則返回false,和Zookeeper的"ZAB"原理很類似,而且與Redis Cluster集群中解決腦裂問題的方案類似,但是RedLock方案有很大的弊端,也就是會造成Redis可用性的延遲,眾所周知,Redis的AP(可用性+分區(qū)容忍性)機制,假如把Redis變成CP(一致性+分區(qū)容忍性),這樣肯定會犧牲一定的可用性,與Redis初衷不符合,也就是說還不如使用Zookeeper。
  • Zookeeper具備CP機制以及實現(xiàn)了ZAB,能夠確保某一個節(jié)點宕機,也能保證數(shù)據(jù)一致性,而且效率會比Redis高很多,更適合做分布式鎖

場景2: 大廠線上大規(guī)模商品緩存數(shù)據(jù)冷熱分離實戰(zhàn)

問題: 在高并發(fā)場景下,一定要把所有的緩存數(shù)據(jù)一直保存在緩存不讓其失效嗎?

雖然一直緩存所有數(shù)據(jù)沒什么大問題,但是考慮到如果數(shù)據(jù)太多,就會一直占用緩存空間(內(nèi)存資源非常寶貴),并且數(shù)據(jù)的維護性也是需要耗時的.

解決方案

  • 對緩存數(shù)據(jù)做冷熱分離。在查詢數(shù)據(jù)時,我們只需要在查詢代碼中再次更新過期時間,這樣就能保證熱點數(shù)據(jù)一直在緩存中,而不經(jīng)常訪問的數(shù)據(jù)過期了就自動從緩存中刪除。

流程分析

  • 假如一個熱點數(shù)據(jù)每天訪問特別高,不停的查詢該數(shù)據(jù),每次查詢時再次更新過期時間,那么在這個過期時間之內(nèi)只要有人訪問就會一直存在緩存中,這樣就保證熱點商品數(shù)據(jù)不會因為過期時間而從緩存中移除;
  • 而對于不經(jīng)常訪問的冷門數(shù)據(jù)到了過期時間就可以自動釋放了,同時也釋放除了一部分緩存空間,而且當(dāng)再次訪問冷門數(shù)據(jù)的時候,從數(shù)據(jù)庫拿到的永遠是最新的數(shù)據(jù),也減少了維護成本。

場景3: 基于DCL機制解決熱點緩存并發(fā)重建問題實戰(zhàn)

DCL(雙重檢測鎖)

問題: 冷門數(shù)據(jù)突然變成了熱門數(shù)據(jù),大量的請求突發(fā)性的對熱點數(shù)據(jù)進行緩存重建導(dǎo)致系統(tǒng)壓力暴增

解決方案

  • 最容易想到的就是加鎖
  • DCL機制。先查一次,緩存有數(shù)據(jù)就直接返回,沒有數(shù)據(jù),就加鎖,在鎖的代碼塊中再次先查詢緩存。這樣鎖的目的就是為了當(dāng)?shù)谝淮尉彺鎻臄?shù)據(jù)庫查詢更新到緩存中,代碼塊執(zhí)行完,其他線程再次進來,此時緩存中就已經(jīng)存在數(shù)據(jù)了,這樣就減少了查詢數(shù)據(jù)庫的次數(shù)
public Product get(Long productId) {
    Product product = null;
    String productCacheKey = RedisKeyPrefixConst.PRODUCT_CACHE + productId;
    //DCL機制:第一次先從緩存里查數(shù)據(jù)
    product = getProductFromCache(productCacheKey);
    if (product != null) {
        return product;
    }
  
    //加分布式鎖解決熱點緩存并發(fā)重建問題
    RLock hotCreateCacheLock = redisson.getLock(LOCK_PRODUCT_HOT_CACHE_CREATE_PREFIX + productId);
    hotCreateCacheLock.lock();
    // 這個優(yōu)化謹慎使用,防止超時導(dǎo)致的大規(guī)模并發(fā)重建問題
    // hotCreateCacheLock.tryLock(1, TimeUnit.SECONDS);
    try {
        //DCL機制:在分布式鎖里面第二次查詢
        product = getProductFromCache(productCacheKey);
        if (product != null) {
            return product;
        }

        //RLock productUpdateLock = redisson.getLock(LOCK_PRODUCT_UPDATE_PREFIX + productId);
        RReadWriteLock productUpdateLock = redisson.getReadWriteLock(LOCK_PRODUCT_UPDATE_PREFIX + productId);
        RLock rLock = productUpdateLock.readLock();
        //加分布式讀鎖解決緩存雙寫不一致問題
        rLock.lock();
        try {
            product = productDao.get(productId);
            if (product != null) {
                redisUtil.set(productCacheKey, JSON.toJSONString(product),
                        genProductCacheTimeout(), TimeUnit.SECONDS);
            } else {
                //設(shè)置空緩存解決緩存穿透問題
                redisUtil.set(productCacheKey, EMPTY_CACHE, genEmptyCacheTimeout(), TimeUnit.SECONDS);
            }
        } finally {
            rLock.unlock();
        }
    } finally {
        hotCreateCacheLock.unlock();
    }

    return product;
}

場景4: 突發(fā)性熱點緩存重建導(dǎo)致系統(tǒng)壓力暴增

問題: 假如當(dāng)前有10w個線程沒有拿到鎖正在排隊,這種情況只能等到獲取鎖的線程執(zhí)行完代碼釋放鎖后,那排隊的10w個線程才能再次競爭鎖。這里需要關(guān)注的問題點就是又要再次競爭鎖,意味著線程競爭鎖的次數(shù)可能最少>1,頻繁的競爭鎖對Redis性能也是有消耗的,有沒有更好的辦法讓每個線程競爭鎖的次數(shù)盡可能減少呢?

解決方案

  • 可以通過tryLock(time,TimeUnit)先讓所有線程嘗試獲取鎖

  • 假如獲取鎖的線程執(zhí)行數(shù)據(jù)庫查詢?nèi)缓髮?shù)據(jù)更新到緩存所需要的時間為1s,那么當(dāng)其他線程獲取鎖時間結(jié)束后,會解除阻塞狀態(tài)直接往下執(zhí)行,然后再次查詢緩存的時候發(fā)現(xiàn)緩存有數(shù)據(jù)了就直接返回。

  • 這樣設(shè)計的好處就是把分布式鎖在某些特定的場景使其"串行變并發(fā)",不過這個優(yōu)化需要謹慎使用,防止超時導(dǎo)致的大規(guī)模并發(fā)重建問題。畢竟沒有任何方案是完全解決問題的,主要是根據(jù)公司業(yè)務(wù)而定.

場景5: 解決大規(guī)模緩存擊穿導(dǎo)致線上數(shù)據(jù)庫壓力暴增

緩存擊穿/緩存失效: 可能同一時間熱點數(shù)據(jù)全部過期而造成緩存查不到數(shù)據(jù),請求就會從數(shù)據(jù)庫查詢,高并發(fā)情況下會導(dǎo)致數(shù)據(jù)庫壓力

解決方案

  • 對于這個場景,可以給數(shù)據(jù)設(shè)置過期時間時,不要將所有緩存數(shù)據(jù)的過期時間設(shè)置為相同的過期時間,最好可以給每個數(shù)據(jù)的過期時間設(shè)置一個隨機數(shù),保證數(shù)據(jù)在不同的時間段過期。

代碼案例

private Integer genProductCacheTimeout() {
  //加隨機超時機制解決緩存批量失效(擊穿)問題
  return PRODUCT_CACHE_TIMEOUT + new Random().nextInt(5) * 60 * 60;
}

場景6: 黑客工資導(dǎo)致緩存穿透線上數(shù)據(jù)庫宕機

緩存穿透: 如果黑客通過腳本文件不停的傳一些不存在的參數(shù)刷網(wǎng)站的接口,而這種垃圾參數(shù)在緩存和數(shù)據(jù)庫又不存在,這樣就會一直地查數(shù)據(jù)庫,最終可能導(dǎo)致數(shù)據(jù)庫并發(fā)量過大而卡死宕機。

解決方案

  • 網(wǎng)關(guān)限流。Nginx、Sentinel、Hystrix都可以實現(xiàn)
  • 代碼層面。可以使用多級緩存,比如一級緩存采用布隆過濾器,二級緩存可以使用guava中的Cache,三級緩存使用Redis,為什么一級緩存使用布隆過濾器呢,其結(jié)構(gòu)和bitmap類似,用于存儲數(shù)據(jù)狀態(tài),能存大量的key

布隆過濾器

  • 布隆過濾器就是一個大型的位數(shù)組和幾個不一樣的無偏Hash函數(shù).當(dāng)布隆過濾器說某個值存在時,這個值可能不存在,當(dāng)說不存在時,那就肯定不存在。

場景7: 大V直播帶貨導(dǎo)致線上商品系統(tǒng)崩潰原因分析

問題: 這種場景可能是在某個時刻把冷門商品一下子變成了熱門商品。因為冷門的數(shù)據(jù)可能在緩存時間過期就刪除,而此時剛好有大量請求,比如直播期間推送一個商品連接,假如同時有幾十萬人搶購,而緩存沒有的話,意味著所有的請求全部達到了數(shù)據(jù)庫中查詢,而對于數(shù)據(jù)庫單節(jié)點支撐并發(fā)量也就不到1w,此時這么大的請求量,肯定會把數(shù)據(jù)庫整宕機(這種場景比較少,但是小概率還是會有)

解決方案

  • 可以通過tryLock(time,TimeUnit)先讓所有線程嘗試獲取鎖

  • 假如獲取鎖的線程執(zhí)行數(shù)據(jù)庫查詢?nèi)缓髮?shù)據(jù)更新到緩存所需要的時間為1s,那么當(dāng)其他線程獲取鎖時間結(jié)束后,會解除阻塞狀態(tài)直接往下執(zhí)行,然后再次查詢緩存的時候發(fā)現(xiàn)緩存有數(shù)據(jù)了就直接返回。

  • 這樣設(shè)計的好處就是把分布式鎖在某些特定的場景使其"串行變并發(fā)",不過這個優(yōu)化需要謹慎使用,防止超時導(dǎo)致的大規(guī)模并發(fā)重建問題。畢竟沒有任何方案是完全解決問題的,主要是根據(jù)公司業(yè)務(wù)而定.

場景8: Redis分布式鎖解決緩存與數(shù)據(jù)庫雙寫不一致問題實戰(zhàn)

解決方案

  • 重入鎖保證并發(fā)安全。通常說在分布式鎖中再加一把鎖,鎖太重,性能不是很好,還有優(yōu)化空間
  • 分布式讀寫鎖(ReadWriteLock),實現(xiàn)機制和ReentranReadWriteLock一直,適合讀多寫少的場景,注意讀寫鎖的key得一致
  • 使用canal通過監(jiān)聽binlog日志及時去修改緩存,但是引入中間件,增加系統(tǒng)的維護度

Lua腳本設(shè)置讀寫鎖

local mode = redis.call('hget', KEYS[1], 'mode');
if (mode == false) 
then redis.call('hset', KEYS[1], 'mode', 'read'); 
redis.call('hset', KEYS[1], ARGV[2], 1); 
redis.call('set', KEYS[2] .. ':1', 1); 
redis.call('pexpire', KEYS[2] .. ':1', ARGV[1]);
redis.call('pexpire', KEYS[1], ARGV[1]); 
return nil; 
end; 
if (mode == 'read') or (mode == 'write' and redis.call('hexists', KEYS[1], ARGV[3]) == 1) 
then local ind = redis.call('hincrby', KEYS[1], ARGV[2], 1); 
local key = KEYS[2] .. ':' .. ind;
redis.call('set', key, 1); 
redis.call('pexpire', key, ARGV[1]); redis.call('pexpire', KEYS[1], ARGV[1]); 
return nil; 
end;
return redis.call('pttl', KEYS[1]);

ReadWriteLock代碼案例

@Transactional
public Product update(Product product) {
  Product productResult = null;
  //RLock productUpdateLock = redisson.getLock(LOCK_PRODUCT_UPDATE_PREFIX + product.getId());
  RReadWriteLock productUpdateLock = redisson.getReadWriteLock(LOCK_PRODUCT_UPDATE_PREFIX + product.getId());
  // 添加寫鎖
  RLock writeLock = productUpdateLock.writeLock();
  //加分布式寫鎖解決緩存雙寫不一致問題
  writeLock.lock();
  try {
      productResult = productDao.update(product);
      redisUtil.set(RedisKeyPrefixConst.PRODUCT_CACHE + productResult.getId(), JSON.toJSONString(productResult),
      genProductCacheTimeout(), TimeUnit.SECONDS);
   } finally {
          writeLock.unlock();
   }
  return productResult;
}

public Product get(Long productId) {
    Product product = null;
    String productCacheKey = RedisKeyPrefixConst.PRODUCT_CACHE + productId;

    //從緩存里查數(shù)據(jù)
    product = getProductFromCache(productCacheKey);
    if (product != null) {
        return product;
    }

    //加分布式鎖解決熱點緩存并發(fā)重建問題
    RLock hotCreateCacheLock = redisson.getLock(LOCK_PRODUCT_HOT_CACHE_CREATE_PREFIX + productId);
    hotCreateCacheLock.lock();
    // 這個優(yōu)化謹慎使用,防止超時導(dǎo)致的大規(guī)模并發(fā)重建問題
    // hotCreateCacheLock.tryLock(1, TimeUnit.SECONDS);
    try {
        product = getProductFromCache(productCacheKey);
        if (product != null) {
            return product;
        }

        //RLock productUpdateLock = redisson.getLock(LOCK_PRODUCT_UPDATE_PREFIX + productId);
        RReadWriteLock productUpdateLock = redisson.getReadWriteLock(LOCK_PRODUCT_UPDATE_PREFIX + productId);
        // 添加讀鎖
        RLock rLock = productUpdateLock.readLock();
        //加分布式讀鎖解決緩存雙寫不一致問題
        rLock.lock();
        try {
            product = productDao.get(productId);
            if (product != null) {
                redisUtil.set(productCacheKey, JSON.toJSONString(product),
                        genProductCacheTimeout(), TimeUnit.SECONDS);
            } else {
                //設(shè)置空緩存解決緩存穿透問題
                redisUtil.set(productCacheKey, EMPTY_CACHE, genEmptyCacheTimeout(), TimeUnit.SECONDS);
            }
        } finally {
            rLock.unlock();
        }
    } finally {
        hotCreateCacheLock.unlock();
    }

    return product;
}

場景9: 大促壓力暴增導(dǎo)致分布式鎖串行爭用問題優(yōu)化

解決方案

  • 可以采用分段鎖,和JDK7的ConcurrentHashMap的實現(xiàn)原理很類似,將一個鎖,分成多個鎖,比如lock,分成lock_1、lock_2...
  • 然后將庫存平均分攤到每把鎖,這樣做的目的是分攤分布式鎖的壓力,本來只有一個鎖,意味著所有的線程進來只能一個線程獲取到鎖,如果分攤為10把鎖,那么同一時間可以有10個線程同時獲取到鎖對同一個商品進行操作,也就意味著在同等環(huán)境下,分段鎖的效率比只用一個鎖要高得多

場景10: 利用多級緩存解決Redis線上集群緩存雪崩問題

緩存雪崩: 緩存支撐不住或者宕機,然后大量請求涌入數(shù)據(jù)庫。

解決方案

  • 網(wǎng)關(guān)限流。Nginx、Sentinel、Hystrix都可以實現(xiàn)
  • 代碼層面。可以使用多級緩存,比如一級緩存采用布隆過濾器,二級緩存可以使用guava中的Cache,三級緩存使用Redis,為什么一級緩存使用布隆過濾器呢,其結(jié)構(gòu)和bitmap類似,用于存儲數(shù)據(jù)狀態(tài),能存大量的key

場景11: 一次微博明顯熱點事件導(dǎo)致系統(tǒng)崩潰原因分析

問題: 比如微博上某一天某個明星事件成為了熱點新聞,此時很多吃瓜群眾全部涌入這個熱點,如果并發(fā)每秒達到幾十萬甚至上百萬的并發(fā)量,但是Redis服務(wù)器單節(jié)點只能支撐并發(fā)10w而已,那么可能因為這么高的并發(fā)量導(dǎo)致很多請求卡死在那,要知道我們其他業(yè)務(wù)服務(wù)也會用到Redis,一旦Redis卡死,就會影響到其他業(yè)務(wù),導(dǎo)致整個業(yè)務(wù)癱瘓,這就是典型的緩存雪崩問題

解決方案: 參考場景10

場景12: 大廠對熱點數(shù)據(jù)處理方案

解決方案

  • 如果按照場景10的方案去實現(xiàn),需要考慮數(shù)據(jù)一致性問題,這樣就不得不每次對數(shù)據(jù)進行增加、刪除、更新都要立馬通知其他節(jié)點更新數(shù)據(jù),能做到及時更新數(shù)據(jù)的方案可能就是:Redis發(fā)布/訂閱、MQ等
  • 雖然說這些方案實現(xiàn)也可以,但是不可避免的我們需要再維護相關(guān)的中間件,提高了維護成本
  • 目前大廠對于熱點數(shù)據(jù)專門會有一個類似于熱點緩存系統(tǒng)來維護,所有的web應(yīng)用只需要監(jiān)聽這個系統(tǒng),只要有熱點時,直接更新緩存,這樣既能減少代碼耦合,還能更好的維護熱點數(shù)據(jù)。
  • 那么熱點數(shù)據(jù)來源怎么獲取呢?可以在設(shè)計查詢的接口使用類似于Spring AOP的方式,每次查詢就把數(shù)據(jù)傳送到熱點數(shù)據(jù),一般大廠都會有數(shù)據(jù)分析崗位,根據(jù)熱點規(guī)則將數(shù)據(jù)分類

到此這篇關(guān)于淺談Redis高并發(fā)緩存架構(gòu)性能優(yōu)化實戰(zhàn)的文章就介紹到這了,更多相關(guān)Redis高并發(fā)緩存內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 使用Redis完成接口限流的過程

    使用Redis完成接口限流的過程

    在一個高并發(fā)系統(tǒng)中對流量的把控是非常重要的,當(dāng)巨大的流量直接請求到我們的服務(wù)器上沒多久就可能造成接口不可用,為了避免這種情況的發(fā)生我們就需要在請求接口時對接口進行限流的操作,這篇文章主要介紹了使用Redis完成接口限流的過程,需要的朋友可以參考下
    2024-05-05
  • Redis?集群模式(Cluster)原理詳解

    Redis?集群模式(Cluster)原理詳解

    redis?cluster集群是一個由多個主從節(jié)點集群組成的分布式服務(wù)集群,它具有復(fù)制、高可用和分片特性,cluster集群不需要sentinel?哨兵也能完成節(jié)點移除和故障轉(zhuǎn)移的功能,本文就詳細的給大家介紹一下Redis?集群模式原理,感興趣的朋友跟著小編一起來看看吧
    2023-07-07
  • Redis分布式鎖詳細介紹

    Redis分布式鎖詳細介紹

    大家好,本篇文章主要講的是Redis分布式鎖詳細介紹,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽
    2021-12-12
  • Redis集群節(jié)點通信過程/原理流程分析

    Redis集群節(jié)點通信過程/原理流程分析

    這篇文章主要介紹了Redis集群節(jié)點通信過程/原理,詳細介紹了Cluster(集群)的節(jié)點通信的流程,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-03-03
  • 詳解Redis在SpringBoot工程中的綜合應(yīng)用

    詳解Redis在SpringBoot工程中的綜合應(yīng)用

    這篇文章主要介紹了Redis在SpringBoot工程中的綜合應(yīng)用,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-10-10
  • Redis?的內(nèi)存淘汰策略和過期刪除策略的區(qū)別

    Redis?的內(nèi)存淘汰策略和過期刪除策略的區(qū)別

    這篇文章主要介紹了Redis?的內(nèi)存淘汰策略和過期刪除策略的區(qū)別,Redis?是可以對?key?設(shè)置過期時間的,因此需要有相應(yīng)的機制將已過期的鍵值對刪除,而做這個工作的就是過期鍵值刪除策略
    2022-07-07
  • Redisson之lock()和tryLock()的區(qū)別及說明

    Redisson之lock()和tryLock()的區(qū)別及說明

    這篇文章主要介紹了Redisson之lock()和tryLock()的區(qū)別及說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • Redis刪除過期key策略詳解

    Redis刪除過期key策略詳解

    Redis是一款高性能的開源內(nèi)存數(shù)據(jù)庫,廣泛應(yīng)用于緩存、消息隊列、實時分析等場景,在Redis中,我們經(jīng)常需要刪除過期的key,以釋放內(nèi)存空間并保持數(shù)據(jù)的有效性,本文將為您詳細介紹Redis的過期key刪除策略,幫助您更好地管理和優(yōu)化Redis數(shù)據(jù)庫
    2023-10-10
  • 使用redis生成唯一編號及原理示例詳解

    使用redis生成唯一編號及原理示例詳解

    今天介紹下如何使用redis生成唯一的序列號,其實主要思想還是利用redis單線程的特性,可以保證操作的原子性,使讀寫同一個key時不會出現(xiàn)不同的數(shù)據(jù),感興趣的朋友跟隨小編一起看看吧
    2021-09-09
  • Redis類型type與編碼encoding原理及使用示例

    Redis類型type與編碼encoding原理及使用示例

    這篇文章主要為大家介紹了Redis類型type與編碼encoding原理及使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-03-03

最新評論