自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

程序員修神之路--緩存架構(gòu)不夠好,系統(tǒng)容易癱瘓

存儲 存儲軟件
如果按照傳統(tǒng)的緩存和DB的流程,一個(gè)請求到來的時(shí)候,首先會查詢緩存中是否存在,如果緩存中不存在則去查詢對應(yīng)的數(shù)據(jù)庫。

 

  •  緩存能大幅度提高系統(tǒng)性能,也能大幅度提高系統(tǒng)癱瘓幾率
  • 怎么樣防止緩存系統(tǒng)被穿透?
  • 緩存的雪崩是不是可以完全避免?

前幾篇文章我們介紹了緩存的優(yōu)勢以及數(shù)據(jù)一致性的問題,在一個(gè)面臨高并發(fā)系統(tǒng)中,緩存幾乎成了每個(gè)架構(gòu)師應(yīng)對高流量的首沖解決方案,但是,一個(gè)好的緩存系統(tǒng),除了和數(shù)據(jù)庫一致性問題之外,還存在著其他問題,給整體的系統(tǒng)設(shè)計(jì)引入了額外的復(fù)雜性。而這些復(fù)雜性問題的解決方案也直接了影響系統(tǒng)的穩(wěn)定性,最常見的比如緩存的命中率問題,在一個(gè)高并發(fā)系統(tǒng)中,核心功能的緩存命中率一般要保持在90%以上甚至更高,如果低于這個(gè)命中率,整個(gè)系統(tǒng)可能就面臨著隨時(shí)被峰值流量擊垮的可能,這個(gè)時(shí)候我們就需要優(yōu)化緩存的使用方式了。

如果按照傳統(tǒng)的緩存和DB的流程,一個(gè)請求到來的時(shí)候,首先會查詢緩存中是否存在,如果緩存中不存在則去查詢對應(yīng)的數(shù)據(jù)庫。假如系統(tǒng)每秒的請求量為10000,而緩存的命中率為60%,則每秒穿透到數(shù)據(jù)庫的請求數(shù)為4000,對于關(guān)系型數(shù)據(jù)庫mysql來說,每秒4000的請求量對于分了一主三從的Mysql數(shù)據(jù)庫架構(gòu)來說也已經(jīng)足夠大了,再加上主從的同步延遲等諸多因素,這個(gè)時(shí)候你的mysql已經(jīng)行走在down機(jī)邊緣了。

“緩存的最終目的,是在保證請求低延遲的情況下,盡最大努力提高系統(tǒng)的吞吐量

那緩存系統(tǒng)可能會影響系統(tǒng)崩潰的原因有那些呢?

緩存穿透

“緩存穿透是指:當(dāng)一個(gè)請求到來的時(shí)候,在緩存中沒有查找到對應(yīng)的數(shù)據(jù)(緩存未命中),業(yè)務(wù)系統(tǒng)不得不從數(shù)據(jù)庫(這里其實(shí)可以籠統(tǒng)的成為后端系統(tǒng))中加載數(shù)據(jù)

緩存穿透

 

發(fā)生緩存穿透的原因根據(jù)場景分為兩種:

請求的數(shù)據(jù)在緩存和數(shù)據(jù)中都不存在

當(dāng)數(shù)據(jù)在緩存和數(shù)據(jù)庫都不存在的時(shí)候,如果按照一般的緩存設(shè)計(jì),每次請求都會到數(shù)據(jù)庫查詢一次,然后返回不存在,這種場景下,緩存系統(tǒng)幾乎沒有起任何作用。在正常的業(yè)務(wù)系統(tǒng)中,發(fā)生這種情況的概率比較小,就算偶爾發(fā)生,也不會對數(shù)據(jù)庫造成根本上的壓力。

最可怕的是出現(xiàn)一些異常情況,比如系統(tǒng)中有死循環(huán)的查詢或者被黑客攻擊的時(shí)候,尤其是后者,他會故意偽造大量的請求來讀取不存在的數(shù)據(jù)而造成數(shù)據(jù)庫的down機(jī),最典型的場景為:如果系統(tǒng)的用戶id是連續(xù)遞增的int型,黑客很容易偽造用戶id來模擬大量的請求。

請求的數(shù)據(jù)在緩存中不存在,在數(shù)據(jù)庫中存在

這種場景一般屬于業(yè)務(wù)的正常需求,因?yàn)榫彺嫦到y(tǒng)的容量一般是有限制的,比如我們最常用的Redis做為緩存,就受到服務(wù)器內(nèi)存大小的限制,所以所有的業(yè)務(wù)數(shù)據(jù)不可能都放入緩存系統(tǒng)中,根據(jù)互聯(lián)網(wǎng)數(shù)據(jù)的二八規(guī)則,我們可以優(yōu)先把訪問最頻繁的熱點(diǎn)數(shù)據(jù)放入緩存系統(tǒng),這樣就能利用緩存的優(yōu)勢來抗住主要的流量來源,而剩余的非熱點(diǎn)數(shù)據(jù),就算是有穿透數(shù)據(jù)庫的可能性,也不會對數(shù)據(jù)庫造成致命壓力。

換句話說,每個(gè)系統(tǒng)發(fā)生緩存穿透是不可避免的,而我們需要做的是盡量避免大量的請求發(fā)生穿透,那怎么解決緩存穿透問題呢?解決緩存的穿透問題本質(zhì)上是要解決怎么樣攔截請求的問題,一般情況下會有以下幾種方案:

回寫空值

當(dāng)請求的數(shù)據(jù)在數(shù)據(jù)庫中不存在的時(shí)候,緩存系統(tǒng)可以把對應(yīng)的key寫入一個(gè)空值,這樣當(dāng)下次同樣的請求就不會直接穿透數(shù)據(jù)庫,而直接返回緩存中的空值了。這種方案是最簡單粗暴的,但是要注意幾點(diǎn):

  • 當(dāng)有大量的空值被寫入緩存系統(tǒng)中,同樣會占用內(nèi)存,不過理論上不會太多,完全取決于key的數(shù)量。而且根據(jù)緩存淘汰策略,可能會淘汰正常的數(shù)據(jù)緩存項(xiàng)
  • 空值的過期時(shí)間應(yīng)該短一些,比如正常的數(shù)據(jù)緩存過期時(shí)間可能為2小時(shí),可以考慮空值的過期時(shí)間為10分鐘,這樣做一是為了盡快釋放服務(wù)器的內(nèi)存空間,二是如果業(yè)務(wù)產(chǎn)生相應(yīng)的真實(shí)數(shù)據(jù),可以讓緩存的空值快速失效,盡快做到緩存和數(shù)據(jù)庫一致。
  1. //獲取用戶信息 
  2.         public static UserInfo GetUserInfo(int userId) 
  3.         { 
  4.             //從緩存讀取用戶信息 
  5.             var userInfo = GetUserInfoFromCache(userId); 
  6.             if (userInfo == null
  7.             { 
  8.                 //回寫空值到緩存,并設(shè)置緩存過期時(shí)間為10分鐘 
  9.                 CacheSystem.Set(userId, null,10); 
  10.             } 
  11.  
  12.             return userInfo; 
  13.         } 

布隆過濾器

“布隆過濾器:將所有可能存在的數(shù)據(jù)哈希到一個(gè)足夠大的 bitmap 中,一個(gè)一定不存在的數(shù)據(jù)會被這個(gè)bitmap攔截掉,從而避免了對底層存儲系統(tǒng)的查詢壓力

布隆過濾器有幾個(gè)很大的優(yōu)勢

  • 占用內(nèi)存非常小
  • 對于判斷一個(gè)數(shù)據(jù)不存在百分百正確

由于布隆過濾器基于hash算法,所以在時(shí)間復(fù)雜度上是O(1),在應(yīng)對高并發(fā)的場景下非常合適,不過使用布隆過濾器要求系統(tǒng)在產(chǎn)生數(shù)據(jù)的時(shí)候需要在布隆過濾器同時(shí)也寫入數(shù)據(jù),而且布隆過濾器也不支持刪除數(shù)據(jù),因?yàn)槎鄠€(gè)數(shù)據(jù)可能會重用同一個(gè)位置。

image

 

緩存雪崩

“緩存雪崩是指緩存中數(shù)據(jù)大批量同時(shí)過期,造成查詢數(shù)據(jù)庫數(shù)據(jù)量巨大,引起數(shù)據(jù)庫壓力過大導(dǎo)致系統(tǒng)崩潰。

與緩存穿透現(xiàn)象不同,緩存穿透是指緩存中不存在數(shù)據(jù)而造成會對數(shù)據(jù)庫造成大量查詢,而緩存雪崩是因?yàn)榫彺嬷写嬖跀?shù)據(jù),但是同時(shí)大量過期造成。但是本質(zhì)上是一樣的,都是對數(shù)據(jù)庫造成了大量的請求。

無論是穿透還是雪崩都面臨著同樣的數(shù)據(jù)會有多個(gè)線程同時(shí)請求,同時(shí)查詢數(shù)據(jù)庫,同時(shí)回寫緩存的一致性問題。舉例來說,當(dāng)多個(gè)線程同時(shí)請求用戶id為1的用戶,這個(gè)時(shí)候緩存正好失效,那這多個(gè)線程同時(shí)會查詢數(shù)據(jù)庫,然后同時(shí)會回寫緩存,最可怕的是,這個(gè)回寫的過程中,另外一個(gè)線程更新了數(shù)據(jù)庫,就造成了數(shù)據(jù)不一致,這個(gè)問題在之前的文章中著重講過,大家一定要注意。

同樣的數(shù)據(jù)會被多個(gè)線程產(chǎn)生多個(gè)請求是產(chǎn)生雪崩的一個(gè)原因,針對這種情況的解決方案是把多個(gè)線程的請求順序化,使其只有一個(gè)線程會產(chǎn)生對數(shù)據(jù)庫的查詢操作,比如最常見的鎖機(jī)制(分布式鎖機(jī)制),現(xiàn)在最常見的分布式鎖是用redis來實(shí)現(xiàn),但是redis實(shí)現(xiàn)分布式鎖也有一定的坑。

多個(gè)緩存key同時(shí)失效的場景是產(chǎn)生雪崩的主要原因,針對這樣的場景一般可以利用以下幾種方案來解決

設(shè)置不同過期時(shí)間

給緩存的每個(gè)key設(shè)置不同的過期時(shí)間是最簡單的防止緩存雪崩的手段,整體思路是給每個(gè)緩存的key在系統(tǒng)設(shè)置的過期時(shí)間之上加一個(gè)隨機(jī)值,或者干脆是直接隨機(jī)一個(gè)值,有效的平衡key批量過期時(shí)間段,消掉單位之間內(nèi)過期key數(shù)量的峰值。

  1. public static int SetUserInfo(int userId) 
  2.        { 
  3.            //讀取用戶信息 
  4.            var userInfo = GetUserInfoFromDB(userId); 
  5.            if (userInfo != null
  6.            { 
  7.                //回寫到緩存,并設(shè)置緩存過期時(shí)間為隨機(jī)時(shí)間 
  8.                var cacheExpire = new Random().Next(1, 100); 
  9.                CacheSystem.Set(userId, userInfo, cacheExpire); 
  10.                return cacheExpire; 
  11.            } 
  12.  
  13.            return 0; 
  14.        } 

后臺單獨(dú)線程更新

這種場景下,可以把緩存設(shè)置為永不過期,緩存的更新不是由業(yè)務(wù)線程來更新,而是由專門的線程去負(fù)責(zé)。當(dāng)緩存的key有更新時(shí)候,業(yè)務(wù)方向mq發(fā)送一個(gè)消息,更新緩存的線程會監(jiān)聽這個(gè)mq來實(shí)時(shí)響應(yīng)以便更新緩存中對應(yīng)的數(shù)據(jù)。不過這種方式要考慮到緩存淘汰的場景,當(dāng)一個(gè)緩存的key被淘汰之后,其實(shí)也可以向mq發(fā)送一個(gè)消息,以達(dá)到更新線程重新回寫key的操作。

緩存的可用性和擴(kuò)展性

和數(shù)據(jù)庫一樣,緩存系統(tǒng)的設(shè)計(jì)同樣需要考慮高可用和擴(kuò)展性。雖然緩存系統(tǒng)本身的性能已經(jīng)比較高了,但是對于一些特殊的高并發(fā)的熱點(diǎn)數(shù)據(jù),還是會遇到單機(jī)的瓶頸。舉個(gè)栗子:假如某個(gè)明星出軌了,這個(gè)信息數(shù)據(jù)會緩存在某個(gè)緩存服務(wù)器的節(jié)點(diǎn)上,大量的請求會到達(dá)這個(gè)服務(wù)器節(jié)點(diǎn),當(dāng)?shù)竭_(dá)一定程度的時(shí)候同樣會發(fā)生down機(jī)的情況。類似于數(shù)據(jù)庫的主從架構(gòu),緩存系統(tǒng)也可以復(fù)制多分緩存副本到其他服務(wù)器上,這樣就可以將應(yīng)用的請求分散到多個(gè)緩存服務(wù)器上,緩解由于熱點(diǎn)數(shù)據(jù)出現(xiàn)的單點(diǎn)問題。

和數(shù)據(jù)庫主從一樣,緩存的多個(gè)副本也面臨著數(shù)據(jù)的一致性問題,同步延遲問題,還有主從服務(wù)器相同key的過期時(shí)間問題。

至于緩存系統(tǒng)的擴(kuò)展性同樣的道理,也可以利用“分片”的原則,利用一致性哈希算法將不同的請求路由到不同的緩存服務(wù)器節(jié)點(diǎn),來達(dá)到水平擴(kuò)展的要求,這一點(diǎn)和應(yīng)用的水平擴(kuò)展道理一樣。

寫在最后

通過以上可以看出,無論是應(yīng)用服務(wù)器的高可用架構(gòu)還是數(shù)據(jù)庫的高可用架構(gòu),還是緩存的高可用其實(shí)道理都是類似的,當(dāng)我們掌握了其中一種就很容易的擴(kuò)展到任何場景中。如果這篇文章對你有多幫助,請分享給身邊的朋友,最后歡迎大家留言寫下你們在日常開發(fā)中用到的其他關(guān)于緩存高可用,可擴(kuò)展性,以及防止穿透和雪崩的方案,讓我們一起進(jìn)步!!

本文轉(zhuǎn)載自微信公眾號「架構(gòu)師修行之路」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系架構(gòu)師修行之路公眾號。

 

責(zé)任編輯:武曉燕 來源: 架構(gòu)師修行之路
相關(guān)推薦

2020-09-14 08:47:46

緩存程序員存儲

2022-12-06 07:34:36

語音助手自動(dòng)語音音箱

2012-03-27 09:36:59

編程語言

2019-11-26 09:24:19

程序員Kubernetes微服務(wù)

2020-09-07 07:36:32

數(shù)據(jù)庫集群程序員

2012-07-05 11:09:25

2020-09-22 08:07:50

緩存數(shù)據(jù)一致性

2020-10-10 08:07:36

分布式系統(tǒng)程序員

2018-04-03 17:08:08

程序員技能面試

2022-02-03 14:47:29

Windows 11微軟Bug

2025-03-25 08:14:25

2015-05-20 10:17:06

程序員

2021-10-16 07:33:26

Windows 11操作系統(tǒng)微軟

2010-03-08 10:10:57

程序員

2012-07-12 09:31:49

程序員

2021-03-02 08:31:18

分布式web 應(yīng)用數(shù)據(jù)存儲

2020-05-08 15:41:08

程序員技術(shù)設(shè)計(jì)

2020-02-28 15:49:26

2009-02-10 15:44:09

SCJPJava認(rèn)證

2011-03-25 10:53:53

程序員
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號