高並發系統三大利器之緩存,你都會了嗎?

2020-09-05 JAVA互聯搬磚工人

引言

隨著網際網路的高速發展,市面上也出現了越來越多的網站和app。我們判斷一個軟體是否好用,用戶體驗就是一個重要的衡量標準。比如說我們經常用的微信,打開一個頁面要十幾秒,發個語音要幾分鐘對方才能收到。相信這樣的軟體大家肯定是都不願意用的。軟體要做到用戶體驗好,響應速度快,緩存就是必不可少的一個神器。緩存又分進程內緩存和分布式緩存兩種:分布式緩存如redis、memcached等,還有本地(進程內)緩存如ehcache、GuavaCache、Caffeine等。

緩存特徵

緩存作為一個數據數據模型對象,那麼它有一些什麼樣的特徵呢?下面我們分別來介紹下這些特徵。

命中率

  • 命中率=命中數/(命中數+沒有命中數)當某個請求能夠通過訪問緩存而得到響應時,稱為緩存命中。緩存命中率越高,緩存的利用率也就越高。

最大空間

  • 緩存中可以容納最大元素的數量。當緩存存放的數據超過最大空間時,就需要根據淘汰算法來淘汰部分數據存放新到達的數據。

淘汰算法

  • 緩存的存儲空間有限制,當緩存空間被用滿時,如何保證在穩定服務的同時有效提升命中率?這就由緩存淘汰算法來處理,設計適合自身數據特徵的淘汰算法能夠有效提升緩存命中率。常見的淘汰算法有:

FIFO(first in first out)

  • 「先進先出」。最先進入緩存的數據在緩存空間不夠的情況下(超出最大元素限制)會被優先被清除掉,以騰出新的空間接受新的數據。策略算法主要比較緩存元素的創建時間。「適用於保證高頻數據有效性場景,優先保障最新數據可用」

LFU(less frequently used)

  • 「最少使用」,無論是否過期,根據元素的被使用次數判斷,清除使用次數較少的元素釋放空間。策略算法主要比較元素的hitCount(命中次數)。「適用於保證高頻數據有效性場景」

LRU(least recently used)

  • 「最近最少使用」,無論是否過期,根據元素最後一次被使用的時間戳,清除最遠使用時間戳的元素釋放空間。策略算法主要比較元素最近一次被get使用時間。「比較適用於熱點數據場景,優先保證熱點數據的有效性。」

進程緩存

為什麼需要引入本地緩存,本地緩存的應用場景有哪些?

本地緩存的話是我們的應用和緩存都在同一個進程裡面,獲取緩存數據的時候純內存操作,沒有額外的網絡開銷,速度非常快。它適用於緩存一些應用中基本不會變化的數據,比如(國家、省份、城市等)。

項目中一般如何使用、怎麼樣加載、怎麼樣更新?

進程緩存的話,一般可以在應用啟動的時候,把需要的數據加載到系統中。更新緩存的話可以採取定時更新(實時性不高)。具體實現的話就是在應用中起一個定時任務(「ScheduledExecutorService」「TimerTask」等),讓它每隔多久去加載變更(數據變更之後可以修改資料庫最後修改的時間,每次查詢變更數據的時候都可以根據這個最後變更時間加上半小時大於當前時間的數據)的數據重新到緩存裡面來。如果覺得這個比較麻煩的話,還可以直接全部全量更新(就跟項目啟動加載數據一樣)。這種方式的話,對數據更新可能會有點延遲。可能這臺機器看到的是更新後的數據,那臺機器看到的數據還是老的(機器發布時間可能不一樣)。所以這種方式比較適用於對數據實時性要求不高的數據。如果對實時性有要求的話可以通過廣播訂閱mq消息。如果有數據更新mq會把更新數據推送到每一臺機器,這種方式的話實時性會比前一種「定時更新」的方法會好。但是實現起來會比較複雜。

本地緩存有哪些實現方式?

常見本地緩存有以下幾種實現方式:

從上述表格我們看出性能最佳的是Caffeine。關於這個本地緩存的話我還是強烈推薦的,裡面提供了豐富的api,以及各種各樣的淘汰算法。如需了解更加詳細的話可以看下以前寫的這個篇文章《本地緩存性能之王Caffeine》。

本地緩存缺點

  • 本地緩存與業務系統耦合在一起,應用之間無法直接共享緩存的內容。需要每個應用節點單獨的維護自己的緩存。每個節點都需要一份一樣的緩存,對伺服器內存造成一種浪費。本地緩存機器重啟、或者宕機都會丟失。

分布式緩存

  • 分布式緩存是與應用分離的緩存組件或服務,其最大的優點是自身就是一個獨立的應用,與本地應用隔離,多個應用可直接的共享緩存。常見的分布式緩存有redis、MemCache等。

分布式緩存的應用

在高並發的環境下,比如春節搶票大戰,一到放票的時間節點,分分鐘大量用戶以及黃牛的各種搶票軟體流量進入12306,這時候如果每個用戶的訪問都去資料庫實時查詢票的庫存,大量讀的請求湧入到資料庫,瞬間Db就會被打爆,cpu直接上升100%,服務馬上就要宕機或者假死。即使進行了分庫分表也是無法避免的。為了減輕db的壓力以及提高系統的響應速度。一般都會在資料庫前面加上一層緩存,甚至可能還會有多級緩存。

緩存常見問題

緩存雪崩

指大量緩存同一時間段集體失效,或者緩存整體不能提供服務,導致大量的請求全部到達資料庫 對數據CPU和內存造成巨大壓力,嚴重的會造成資料庫宕機。因此而形成的一系列連鎖反應造成整個系統奔潰。解決這個問題可以從以下方面入手:

  • 保證緩存的高可用。使用redis的集群模式,即使個別redis節點下線,緩存還是可以用。一般稍微大點的公司還可能會在多個機房部署Redis。這樣即使某個機房突然停電,或者光纖又被挖斷了,這時候緩存還是可以使用。
  • 使用多級緩存。不同級別緩存時間過時時間不一樣,即使某個級別緩存過期了,還有其他緩存級別 兜底。比如我們Redis緩存過期了,我們還有本地緩存。這樣的話即使沒有命中redis,有可能會命中本地緩存。
  • 緩存永不過期。Redis中保存的key永久不失效,這樣的話就不會出現大量緩存同時失效的問題,但是這種做法會浪費更多的存儲空間,一般應該也不會推薦這種做法。
  • 使用隨機過期時間。為每一個key都合理的設計一個過期時間,這樣可以避免大量的key在同一時刻集體失效。
  • 異步重建緩存。這樣的話需要維護每個key的過期時間,定時去輪詢這些key的過期時間。例如一個key的value設置的過期時間是30min,那我們可以為這個key設置它自己的一個過期時間為20min。所以當這個key到了20min的時候我們就可以重新去構建這個key的緩存,同時也更新這個key的一個過期時間。

緩存穿透

指查詢一個不存在的數據,每次通過接口或者去查詢資料庫都查不到這個數據,比如黑客的惡意攻擊,比如知道一個訂單號後,然後就偽造一些不存在的訂單號,然後並發來請求你這個訂單詳情。這些訂單號在緩存中都查詢不到,然後會導致把這些查詢請求全部打到資料庫或者SOA接口。這樣的話就會導致資料庫宕機或者你的服務大量超時。這種查詢不存在的數據就是緩存擊穿。解決這個問題可以從以下方面入手:

  • 緩存空值,對於這些不存在的請求,仍然給它緩存一個空的結果,這種方式簡單粗暴,但是如果後續這個請求有新值了需要把原來緩存的空值刪除掉(所以一般過期時間可以稍微設置的比較短)。
  • 通過布隆過濾器。查詢緩存之前先去布隆過濾器查詢下這個數據是否存在。如果數據不存在,然後直接返回空。這樣的話也會減少底層系統的查詢壓力。
  • 緩存沒有直接返回。這種方式的話要根據自己的實際業務來進行選擇。比如固定的數據,一些省份信息或者城市信息,可以全部緩存起來。這樣的話數據有變化的情況,緩存也需要跟著變化。實現起來可能比較複雜。

緩存擊穿

是指緩存裡面的一個熱點key(拼多多的五菱宏光神車的秒殺)在某個時間點過期。針對於這一個key有大量並發請求過來然後都會同時去資料庫請求數據,瞬間對資料庫造成巨大的壓力。這個的話可以用緩存雪崩的幾種解決方法來避免:

  • 緩存永不過期。Redis中保存的key永久不失效,這樣的話就不會出現大量緩存同時失效的問題,但是這種做法會浪費更多的存儲空間,一般應該也不會推薦這種做法。
  • 異步重建緩存。這樣的話需要維護每個key的過期時間,定時去輪詢這些key的過期時間。例如一個key的value設置的過期時間是30min,那我們可以為這個key設置它自己的一個過期時間為20min。所以當這個key到了20min的時候我們就可以重新去構建這個key的緩存,同時也更新這個key的一個過期時間。
  • 互斥鎖重建緩存。這種情況的話只能針對於同一個key的情況下,比如你有100個並發請求都要來取A的緩存,這時候我們可以藉助redis分布式鎖來構建緩存,讓只有一個請求可以去查詢DB其他99個(沒有獲取到鎖)都在外面等著,等A查詢到數據並且把緩存構建好之後其他99個請求都只需要從緩存取就好了。原理就跟我們java的DCL(double checked locking)思想有點類似。
    緩存更新

我們一般的緩存更新主要有以下幾種更新策略:

  • 先更新緩存,再更新資料庫
  • 先更新資料庫,再更新緩存
  • 先刪除緩存,再更新資料庫
  • 先更新數據源庫,再刪除緩存 至於選擇哪種更新策略的話,沒有絕對的選擇,可以根據自己的業務情況來選擇適合自己的不過一般推薦的話是選擇 「先更新數據源庫,再刪除緩存」。關於這幾種更新的介紹可以推薦大家看下博客園大佬孤獨煙寫的《分布式之資料庫和緩存雙寫一致性方案解析》這一篇文章,看完文章評論也可以去看看,評論跟內容一樣精彩。


總結

如果想要真正的設計好一個緩存,我們還是必須要掌握很多的知識,對於不同場景,緩存有各自不同的用法。比如實際工作中我們對於訂單詳情的一個緩存。我們可能會根據訂單的狀態來來構建緩存。我們就以機票訂單為例,已出行、或者已經取消的訂單我們基本上是不會去管的(訂單狀態已經終止了),這種的話數據基本也不會變了,所以對於這種訂單我們設置的過期時間是不是就可以久一點,比如7天或者30天。對於未出行即將起飛的訂單,這時候顧客是不是就會頻繁的去刷新訂單看看,看看有沒有晚點什麼的,或者登機口是在哪。對於這種實時性要求比較高的訂單我們過期時間還是要設置的比較短的,如果是需要更改訂單的狀態查詢的時候可以直接不走緩存,直接查詢master庫。畢竟這種更改訂單狀態的操作還是比較有限的。大多數情況都是用來展示的。展示的話是可以允許實時性要求沒那麼高。總的來說需要開具體的業務,沒有通用的方案。看你的業務需求的容忍度,畢竟脫離了業務來談技術都是耍流氓,是業務驅動技術。

以下文章來源於java金融 ,作者碼農二胖

相關焦點

  • 高並發系統三大利器:限流、降級、緩存
    什麼是高並發一、什麼是高並發高並發(High Concurrency)是網際網路分布式系統架構設計中必須考慮的因素之一,它通常是指,通過設計保證系統能夠同時並行處理很多請求高並發意味著大流量,需要運用技術手段抵抗流量的衝擊,這些手段好比操作流量,能讓流量更平穩地被系統所處理,帶給用戶更好的體驗。我們常見的高並發場景有:淘寶的雙11、春運時的搶票、微博大V的熱點新聞等。除了這些典型事情,每秒幾十萬請求的秒殺系統、每天千萬級的訂單系統、每天億級日活的信息流系統等,都可以歸為高並發。
  • 高並發系統三大利器之——緩存
    它適用於緩存一些應用中基本不會變化的數據,比如(國家、省份、城市等)。項目中一般如何使用、怎麼樣加載、怎麼樣更新?進程緩存的話,一般可以在應用啟動的時候,把需要的數據加載到系統中。更新緩存的話可以採取定時更新(實時性不高)。
  • 高並發系統三大利器之緩存
    進程緩存的話,一般可以在應用啟動的時候,把需要的數據加載到系統中。更新緩存的話可以採取定時更新(實時性不高)。關於這個本地緩存的話我還是強烈推薦的,裡面提供了豐富的api,以及各種各樣的淘汰算法。本地緩存缺點本地緩存與業務系統耦合在一起,應用之間無法直接共享緩存的內容。需要每個應用節點單獨的維護自己的緩存。每個節點都需要一份一樣的緩存,對伺服器內存造成一種浪費。本地緩存機器重啟、或者宕機都會丟失。
  • 高並發系統三大利器之緩存
    進程緩存的話,一般可以在應用啟動的時候,把需要的數據加載到系統中。更新緩存的話可以採取定時更新(實時性不高)。關於這個本地緩存的話我還是強烈推薦的,裡面提供了豐富的api,以及各種各樣的淘汰算法。如需了解更加詳細的話可以看下以前寫的這個篇文章《本地緩存性能之王Caffeine》。本地緩存缺點本地緩存與業務系統耦合在一起,應用之間無法直接共享緩存的內容。
  • 高並發系統三大利器之降級
    我曾經也一度是這樣認為的,直到有一天一個面試官問我熔斷和降級有啥區別嗎?我直接回答沒啥區別,然後就讓我回去等通知了,我才知道它們還是有區別的。我們先看下服務降級的定義:❝服務降級是當伺服器壓力劇增的情況下,根據當前業務情況及流量對一些服務和頁面有策略的降級,以此釋放伺服器資源以保證核心任務的正常運行。
  • 每日一題之 高並發系統
    優質文章,及時送達高並發系統的設計與實現在開發高並發系統時有三把利器用來保護系統:緩存、降級和限流。緩存:緩存比較好理解,在大型高並發系統中,如果沒有緩存資料庫將分分鐘被爆,系統也會瞬間癱瘓。使用緩存不單單能夠提升系統訪問速度、提高並發訪問量,也是保護資料庫、保護系統的有效方式。大型網站一般主要是「讀」,緩存的使用很容易被想到。在大型「寫」系統中,緩存也常常扮演者非常重要的角色。
  • PHP高並發高可用系統以及面試分析
    1.高並發,高可用系統的一些思考高並發依賴於場景和邏輯不一定每個場景都會產生高並發,不要為了高並發而盲目的設計,過度設計帶來 的問題遠比意料之外的高並發要多很多,依賴於具體場景和行為進行分析,或者被對手 ddos 攻擊,都會造成高並發中網站癱瘓,清洗流 量一般就可以的,不要讓辣雞流量貫穿整個業務。
  • 高並發,你真的理解透徹了嗎?
    比如讀性能有瓶頸會引入緩存,但是忽視了緩存命中率、熱點key、數據一致性等問題。3、理解片面,把高並發設計等同於性能優化:大談並發編程、多級緩存、異步化、水平擴容,卻忽視高可用設計、服務治理和運維保障。
  • 如何設計一個高並發系統?
    面試官也絕對不會這樣來問你,否則他就是蠢。假設你在某知名電商公司幹過高並發系統,用戶上億,一天流量幾十億,高峰期並發量上萬,甚至是十萬。那麼人家一定會仔細盤問你的系統架構,你們系統啥架構?怎麼部署的?部署了多少臺機器?緩存咋用的?MQ 咋用的?資料庫咋用的?就是深挖你到底是如何扛住高並發的。
  • 高級Java開發都會使用的GuavaCache內存緩存,你還不會嗎?
    )寫在前面: 長在業務系統中做開發,不會點高級知識點,有點不好意思了。在業務系統中,提高系統響應速度,提供系統高並發能力,其實方向很簡單,三個方向,六個字而已: 緩存降級限流。當然這是在排除代碼質量非常差的情況,如果代碼質量很差,都是while循環和高內存佔用,那麼其實再怎麼做都於事無補。除非你有一個馬雲爸爸,性能不夠,機器來湊嘛。阿里雲前來支持(1000臺機器夠了嗎?)
  • 高級Java開發都會使用的GuavaCache內存緩存,你還不會嗎?
    本篇主要是本地緩存代碼實戰,提供業務中常用的本地緩存使用代碼片段(直接跳過看標題五)寫在前面: 長在業務系統中做開發,不會點高級知識點,有點不好意思了。在業務系統中,提高系統響應速度,提供系統高並發能力,其實方向很簡單,三個方向,六個字而已: 緩存降級限流。
  • 「高並發」面試官:講講什麼是緩存穿透?擊穿?雪崩?如何解決?
    寫在前面在前面的《【高並發】Redis如何助力高並發秒殺系統?看完這篇我徹底懂了!!》一文中,我們以高並發秒殺系統中扣減庫存的場景為例,說明了Redis是如何助力秒殺系統的。那麼,說到Redis,往往更多的場景是被用作系統的緩存,說到緩存,尤其是分布式緩存系統,在實際高並發場景下,稍有不慎,就會造成緩存穿透、緩存擊穿和緩存雪崩的問題。那什麼是緩存穿透?什麼是緩存擊穿,又什麼是緩存雪崩呢?它們是如何造成的?又該如何解決呢?今天,我們就一起來探討這些問題。
  • 高並發系統設計要點
    但此種做法也有嚴重的弊端:如果某個服務節點宕機,那麼該節點上的所有Session對象都會丟失。以MySQL資料庫為例,各種版本MySQL默認的並發連接數約為一二百,單機可配置的最大連接數為16384(一般情況下,由於計算機自身硬體的限制,單機實際能夠負載的並發數最多為一千左右)。因此,高並發系統面臨的最大性能瓶頸就是資料庫。我們之前設計的各種緩存的目的,就是為了儘可能的減少對資料庫的訪問。
  • 高並發場景下的緩存有哪些常見的問題?
    但是,在高並發場景下,有可能多個請求並發的去從資料庫獲取數據,對後端資料庫造成極大的衝擊,甚至導致 「雪崩」現象。此外,當某個緩存key在被更新時,同時也可能被大量請求在獲取,這也會導致一致性的問題。那如何避免類似問題呢?
  • Alibaba雙11的高並發實戰經驗,被這份文檔詮釋的極透徹
    我們常見的高並發場景有:淘寶的雙11、春運時的搶票、微博大V的熱點新聞等。除了這些典型事情,每秒幾十萬請求的秒殺系統、每天千萬級的訂單系統、每天億級日活的信息流系統等,都可以歸為高並發。需要學習Java高並發技術、高並發架構的初、中級Java工程師。生產項目中需要用到Netty、Redis、 ZooKeeper三大框架的架構師或者項目人員。
  • 阿里P8從設計模式基礎知識入手,抽絲剝繭總結出高並發核心筆記
    內容簡介 本書從動態代理模式、Reactor模式、三大限流策略等知識入手,深入淺出地剖析Spring Cloud+Nginx系統架構的核心原理以及Web高並發開發。全書從基礎設計模式和基礎原理出發,理論與實戰相結合,系統和詳盡地介紹Spring Cloud + Nginx高並發核心編程。
  • 高並發系統三大利器之限流
    ❝限流可以認為服務降級的一種,限流就是限制系統的輸入和輸出流量已達到保護系統的目的。一般來說系統的吞吐量是可以被測算的,為了保證系統的穩定運行,一旦達到的需要限制的閾值,就需要限制流量並採取一些措施以完成限制流量的目的。比如:延遲處理,拒絕處理,或者部分拒絕處理等等。
  • 高並發系統下堆積機器方案的弊端與秒殺系統的技術難點與解決方案
    ,其實這裡還不是技術的瓶頸,只要擴展訂單系統集群,增加訂單系統的機器數量就可以抗下這樣甚至更高的高並發情況。我們再來看資料庫部分,你會發現無論你的訂單系統集群增加多少機器,它們訪問的還是一個資料庫。那麼每次面對秒殺系統這樣的活動時,資料庫要承受的壓力是極大的,很可能因此宕機,導致整個系統崩掉,後果是很可怕的。由此我們分析出,資料庫是秒殺系統面臨的一大瓶頸。
  • 天貓雙十一 1小時破千億,聊聊高並發系統
    而QPS和PV等指標遠遠大於這個值,這是真正的高並發系統。那阿里是怎麼打造這麼一個強大的系統?今天我們就聊聊高並發系統。一、高並發系統特點高並發系統的特點非常明顯,就是請求量非常大,請求量可以用QPS、TPS、PV等指標來表示。當單機的QPS高於10000的時候,就算的上一個真正的高並發系統。像淘寶、百度、12306這都屬於高並發的系統。
  • 阿里技術官甩出Java高並發秒殺系統,賊叼
    前言我們知道,高並發代表著大流量,高並發系統設計的魅力就在於我們能夠憑藉自己的聰明才智設計巧妙的方案,從而抵抗巨大流量的衝擊,帶給用戶更好的使用體驗作為一名程式設計師,不能連高並發都不會,無論是在面試還是工作中,都是不可避免的一部分,準備好筆記本,詳細介紹高並發:億級高並發系統設計(分為七篇)01 基礎篇高並發系統:它的通用設計方法是什麼