缓存层设计套路(一)
一、背景
對(duì)于傳統(tǒng)的后端業(yè)務(wù)場(chǎng)景(或者單機(jī)應(yīng)用)中,訪問(wèn)量以及對(duì)響應(yīng)時(shí)間的要求均不高,通常只使用DB即可滿足要求。這種架構(gòu)簡(jiǎn)單,便于快速部署,很多網(wǎng)站發(fā)展初期均考慮使用這種架構(gòu)。但是隨著訪問(wèn)量的上升,以及對(duì)響應(yīng)時(shí)間的要求提升,單DB無(wú)法再滿足要求。這時(shí)候通常會(huì)考慮DB拆分(sharding)、讀寫分離、甚至硬件升級(jí)(SSD)等以滿足新的業(yè)務(wù)需求。但是這種方式仍然會(huì)面臨很多問(wèn)題,主要體現(xiàn)在:
?
1、性能提升有限,很難達(dá)到數(shù)量級(jí)上的提升,尤其在互聯(lián)網(wǎng)業(yè)務(wù)場(chǎng)景下,隨著網(wǎng)站的發(fā)展,訪問(wèn)量經(jīng)常會(huì)面臨十倍、百倍的上漲。
2、成本高昂,為了承載N倍的訪問(wèn)量,通常需要掛載更多的只讀庫(kù),或者升級(jí)數(shù)據(jù)庫(kù)實(shí)例的規(guī)格。
?
在計(jì)算機(jī)科學(xué)領(lǐng)域中有一句話:任何問(wèn)題都可以通過(guò)增加一個(gè)間接的中間層來(lái)解決。本次的分享正是介紹解決以上問(wèn)題的一個(gè)中間層——緩存層設(shè)計(jì)。
????????
二、前言
鑒于緩存層的設(shè)計(jì)異常的復(fù)雜,需要考慮的問(wèn)題很多,諸如:更新策略,緩存穿透,緩存一致性,緩存并發(fā),緩存雪崩等。
本次只涉及到緩存的更新策略部分。
?
三、緩存層鳥瞰圖
?
?
如上圖所示,為了解決數(shù)據(jù)庫(kù)性能瓶頸問(wèn)題,對(duì)于讀多寫少的數(shù)據(jù)查詢,可以通過(guò)多架設(shè)一層緩存層來(lái)減少對(duì)DB的直接訪問(wèn)。由于一般緩存中間件(redis、memcached)的key-value對(duì)都是常駐內(nèi)存的,所以如果能直接命中緩存,一來(lái)可以極大的提高網(wǎng)站的響應(yīng)速度,二來(lái)也可以大幅地減少直接對(duì)數(shù)據(jù)庫(kù)的操作。
緩存層的工作原理一般分為以下兩步:
1??? 當(dāng)應(yīng)用發(fā)起查詢請(qǐng)求時(shí),可以先通過(guò)查詢緩存中的數(shù)據(jù),如果命中緩存結(jié)果即可馬上響應(yīng)請(qǐng)求。
2??? 如果沒有命中緩存,或者緩存已經(jīng)失效了,則需要直接查詢數(shù)據(jù)庫(kù),再次將結(jié)果緩存起來(lái),如果響應(yīng)請(qǐng)求,返回?cái)?shù)據(jù)。
?
?
四、緩存更新策略
有了以上基本了解,我們進(jìn)入到本次分享的主題——緩存更新策略。
?
首先思考一下,為什么會(huì)有緩存更新策略的問(wèn)題,這個(gè)策略需要解決的又是什么問(wèn)題?
?
?
?
緩存層是解決數(shù)據(jù)庫(kù)性能的一個(gè)中間層,既然是中間層,那么引入緩存層當(dāng)然不能影響以前正常的業(yè)務(wù)操作。這里就引出了一個(gè)問(wèn)題,就是如何確保緩存層中的數(shù)據(jù)與數(shù)據(jù)庫(kù)中數(shù)據(jù)的一致性問(wèn)題。緩存更新策略正是為了處理數(shù)據(jù)一致性的問(wèn)題而誕生的。
?
?
緩存更新的模式有四種:Cache aside,Read through,Write through,Write behind caching。
?
1、?Cache aside(緩存預(yù)留)
這是最常用最常用的策略。其具體邏輯如下:
失效:應(yīng)用程序先從cache取數(shù)據(jù),沒有得到,則從數(shù)據(jù)庫(kù)中取數(shù)據(jù),成功后,放到緩存中。
命中:應(yīng)用程序從cache中取數(shù)據(jù),取到后返回。
更新:先把數(shù)據(jù)存到數(shù)據(jù)庫(kù)中,成功后,再讓緩存失效。
?
2、Read/Write Through (直接讀/寫)
Read Through 就是在查詢操作中更新緩存,也就是說(shuō),當(dāng)緩存失效或過(guò)期的時(shí)候,Cache Aside是由調(diào)用方負(fù)責(zé)把數(shù)據(jù)加載入緩存,而Read Through則用緩存服務(wù)自己來(lái)加載,從而對(duì)應(yīng)用方是透明的。
Write Through 和Read Through類似,不過(guò)是在更新數(shù)據(jù)時(shí)發(fā)生。當(dāng)有數(shù)據(jù)更新的時(shí)候,如果沒有命中緩存,直接更新數(shù)據(jù)庫(kù),然后返回。如果命中了緩存,則更新緩存,然后再由Cache自己更新數(shù)據(jù)庫(kù)(這是一個(gè)同步操作)。
我們可以看到,在上面的Cache Aside中,我們的應(yīng)用代碼需要維護(hù)兩個(gè)數(shù)據(jù)存儲(chǔ),一個(gè)是緩存(Cache) ,一個(gè)是數(shù)據(jù)庫(kù)(Repository)。所以,應(yīng)用程序比較難維護(hù)。而Read/Write Through是把更新數(shù)據(jù)庫(kù)(Repository)的操作由緩存自己代理了,所以,對(duì)于應(yīng)用層來(lái)說(shuō),就簡(jiǎn)單很多了。可以理解為,應(yīng)用認(rèn)為后端就是一個(gè)單一的存儲(chǔ),而存儲(chǔ)自己維護(hù)自己的Cache。
?
/3、?Write Behind Caching(回寫)
在更新數(shù)據(jù)的時(shí)候,只更新緩存,不更新數(shù)據(jù)庫(kù),而我們的緩存會(huì)異步地批量更新數(shù)據(jù)庫(kù)。這個(gè)設(shè)計(jì)的好處就是讓數(shù)據(jù)的I/O操作飛快無(wú)比(因?yàn)橹苯硬僮鲀?nèi)存) ,因?yàn)楫惒?#xff0c;Write Behind Caching還可以合并對(duì)同一個(gè)數(shù)據(jù)的多次操作,所以性能的提高是相當(dāng)可觀的。
Write Behind Caching實(shí)現(xiàn)邏輯比較復(fù)雜,因?yàn)樗枰粉櫽心臄?shù)據(jù)是被更新了的,需要刷到持久層上。
?
但是,其帶來(lái)的問(wèn)題是,數(shù)據(jù)不是強(qiáng)一致性的,而且可能會(huì)丟失(Unix/Linux非正常關(guān)機(jī)會(huì)導(dǎo)致數(shù)據(jù)丟失,就是因?yàn)檫@個(gè)原因,因?yàn)長(zhǎng)inux文件系統(tǒng)的Page Cache的算法使用的就是write back,類似于Write Behind Caching)。在軟件設(shè)計(jì)上,我們基本上不可能做出一個(gè)沒有缺陷的設(shè)計(jì),就像算法設(shè)計(jì)中的時(shí)間換空間,空間換時(shí)間一個(gè)道理,有時(shí)候,強(qiáng)一致性和高性能,高可用和高性性是有沖突的。
?
?
?o?思考
?
思考如下場(chǎng)景:
1、用戶A將商品S的售價(jià)從50修改為100
2、同一時(shí)間用戶B在進(jìn)行開單操作
?
這種情況下如何確保用戶B在出售商品S的時(shí)候,售價(jià)是100呢?
?
使用上述的緩存更新策略,是否能解決這個(gè)場(chǎng)景問(wèn)題。
?
還是不能的,比如,一個(gè)是讀操作,但是沒有命中緩存,然后就到數(shù)據(jù)庫(kù)中取數(shù)據(jù),此時(shí)來(lái)了一個(gè)寫操作,寫完數(shù)據(jù)庫(kù)后,讓緩存失效,然后,之前的那個(gè)讀操作再把老的數(shù)據(jù)放進(jìn)去,所以,會(huì)造成臟數(shù)據(jù)。
?
但,這個(gè)案例理論上會(huì)出現(xiàn),不過(guò),實(shí)際上出現(xiàn)的概率可能非常低,因?yàn)檫@個(gè)條件需要發(fā)生在讀緩存時(shí)緩存失效,而且并發(fā)著有一個(gè)寫操作。而實(shí)際上數(shù)據(jù)庫(kù)的寫操作會(huì)比讀操作慢得多,而且還要鎖表,而讀操作必需在寫操作前進(jìn)入數(shù)據(jù)庫(kù)操作,而又要晚于寫操作更新緩存,所有的這些條件都具備的概率基本并不大。
?
所以,要么通過(guò)2PC或是Paxos協(xié)議保證一致性,要么就是拼命的降低并發(fā)時(shí)臟數(shù)據(jù)的概率,而Facebook使用了這個(gè)降低概率的這種方法,因?yàn)?PC太慢,而Paxos太復(fù)雜。當(dāng)然,最好還是為緩存設(shè)置上過(guò)期時(shí)間。
總結(jié)
以上是生活随笔為你收集整理的缓存层设计套路(一)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: ASA IPSEC ***配置
- 下一篇: Ubuntu终端命令行不显示颜色