1. 程式人生 > 實用技巧 >洛谷P1049 裝箱問題

洛谷P1049 裝箱問題

快取穿透

快取穿透,是指查詢一個數據庫不存在的資料。對於系統A,假設一秒 5000 個請求,結果其中 4000 個請求是黑客發出的惡意攻擊。黑客發出的那 4000 個攻擊,快取中查不到,每次你去資料庫裡查,也查不到。

舉個栗子。資料庫 id 是從 1 開始的,結果黑客發過來的請求 id 全部都是負數。這樣的話,快取中不會有,請求每次都“視緩存於無物”,直接查詢資料庫。這種惡意攻擊場景的快取穿透就會直接把資料庫給打死。

處理方案

  1. 判斷合法的請求:介面層增加校驗,比如使用者鑑權,引數做校驗,不合法的校驗直接 return,比如 id 做基礎校驗,id<=0 直接攔截。
  2. 當查詢不存在時,也將結果儲存在快取中
    。但是這可能會存在一種問題:大量沒有查詢結果的請求儲存在快取中,這時我們就可以將這些請求的key設定得更短一些。
  3. 布隆過濾器(Bloom Filter):利用高效的資料結構和演算法快速判斷出你這個 Key 是否在資料庫中存在。

快取擊穿

快取擊穿指的是:一個 Key 非常熱點,在不停地扛著大量的請求,大併發集中對這一個點進行訪問,當這個 Key 在失效的瞬間,持續的大併發直接落到了資料庫上,就在這個 Key 的點上擊穿了快取。

處理方案

  1. 多級快取:結合redis快取和本地快取(ehcache、guava等)搭建多級快取。將一些重要的熱點資料儲存到本地快取中,有效緩解redis的壓力。

  2. 用加鎖或者佇列的方式保證快取的單執行緒(程序)寫,在加鎖方法內先從快取中再獲取一次,沒有再查DB寫入快取。

    public static String getData(String key) throws InterruptedException {
            //從Redis查詢資料
            String result = getDataByKV(key);
            //引數校驗
            if (StringUtils.isBlank(result)) {
                try {
                    //獲得鎖
                    if (reenLock.tryLock()) {
                        //去資料庫查詢
                        result = getDataByDB(key);
                        //校驗
                        if (StringUtils.isNotBlank(result)) {
                            //插進快取
                            setDataToKV(key, result);
                        }
                    } else {
                        //睡一會再拿
                        Thread.sleep(100L);
                        result = getData(key);
                    }
                } finally {
                    //釋放鎖
                    reenLock.unlock();
                }
            }
            return result;
        }
    

快取雪崩

快取雪崩是指:大量快取集中在一段時間內失效,發生大量的快取穿透,所有的查詢都落在資料庫上,造成了快取雪崩,導致資料庫CPU和記憶體負載過高,甚至宕機。

快取雪崩和快取擊穿情況比較相似,快取擊穿是一個高併發的熱點key,在失效的瞬間,大量同個資料查詢的請求直接落到資料庫。而快取雪崩是指大面積快取在同一短時間內失效,導致大量各種資料請求落到資料庫上。

快取擊穿是點選破,快取雪崩是面擊破。

舉個栗子:如果首頁所有 Key 的失效時間都是 12 小時,中午 12 點重新整理的,我零點有個大促活動大量使用者湧入,假設每秒 6000 個請求,本來快取可以抗住每秒 5000 個請求,但是快取中所有 Key 都失效了。此時 6000 個/秒的請求全部落在了資料庫上,資料庫必然扛不住,真實情況可能 DBA 都沒反應過來直接掛了。

處理方案

  1. 快取高可用:搭建高可用redis叢集,主從+哨兵,redis cluster,避免全盤崩潰。

    即使個別節點、個別機器、甚至是機房宕掉,依然可以提供服務,例如 Redis Sentinel 和 Redis Cluster 都實現了高可用。

  2. 限流&降級: hystrix 限流&降級。

    當訪問量劇增、服務出現問題仍然需要保證服務還是可用的。系統可以根據一些關鍵資料進行自動降級,也可以配置開關實現人工降級,這裡會涉及到運維的配合。降級的最終目的是保證核心服務可用,即使是有損的。

  3. 多級快取:結合redis快取和本地快取(ehcache、guava等)搭建多級快取。將一些重要的熱點資料儲存到本地快取中,有效緩解redis的壓力。

  4. 快取失效時間點均勻分佈:儘量讓失效時間點均勻分佈,設定不同的過期時間。在設定Redis鍵的過期時間時,加上一個隨機數,這樣可以避免。

  5. Redis持久化和快速預熱:redis 持久化,一旦重啟,自動從磁碟上載入資料,快速恢復快取資料。

總的來說快取雪崩,可能是同一時間內大面積快取時效,或者是快取服務故障。大面積失效情況,可以在設定快取有效期是增加一個隨機值,使得快取有效期分佈均勻;或則設定多級快取機制,多一層本地快取機制,但是要考慮哪些資料適合放入本地快取。快取伺服器故障情況,需要考慮快取叢集的高可用、快取的持久化和快速預熱。

總結

  • 事前:Redis 高可用,主從+哨兵,Redis cluster,避免全盤崩潰。
  • 事中:本地 ehcache 快取 + Hystrix 限流+降級,避免MySQL 被打死。
  • 事後:Redis 持久化 RDB+AOF,一旦重啟,自動從磁碟上載入資料,快速恢復快取資料。

參考資料

https://www.cnblogs.com/yoishion/p/10791501.html

面試前,我們要複習多少Redis知識點?

https://juejin.im/post/5db66ed9e51d452a2f15d833