fifo页面置换算法设计思路_千万级并发!如何设计一个多级缓存系统?
什么是一個(gè)多級(jí)緩存系統(tǒng)?它有什么用?我們又如何設(shè)計(jì)一個(gè)多級(jí)緩存系統(tǒng)?
圖片來(lái)自 Pexels
所謂多級(jí)緩存系統(tǒng),就是指在一個(gè)系統(tǒng)的不同的架構(gòu)層級(jí)進(jìn)行數(shù)據(jù)緩存,以提升訪問(wèn)效率。
我們都知道,一個(gè)緩存系統(tǒng),它面臨著許多問(wèn)題,比如緩存擊穿,緩存穿透,緩存雪崩,緩存熱點(diǎn)等等問(wèn)題,那么,對(duì)于一個(gè)多級(jí)緩存系統(tǒng),它有什么問(wèn)題呢?
有如下幾個(gè):
- 緩存熱點(diǎn):多級(jí)緩存系統(tǒng)大多應(yīng)用在高并發(fā)場(chǎng)景下,所以我們需要解決熱點(diǎn) Key 問(wèn)題,如何探測(cè)熱點(diǎn) Key?
- 數(shù)據(jù)一致性:各層緩存之間的數(shù)據(jù)一致性問(wèn)題,如應(yīng)用層緩存和分布式緩存之前的數(shù)據(jù)一致性問(wèn)題。
- 緩存過(guò)期:緩存數(shù)據(jù)可以分為兩大類,過(guò)期緩存和不過(guò)期緩存?如何設(shè)計(jì),如何設(shè)計(jì)過(guò)期緩存?
在這之前,我們先看看一個(gè)簡(jiǎn)單的多級(jí)緩存系統(tǒng)的架構(gòu)圖:
整個(gè)多級(jí)緩存系統(tǒng)被分為三層:
- 應(yīng)用層 Nginx 緩存
- 分布式 Redis 緩存集群
- Tomcat 堆內(nèi)緩存
整個(gè)架構(gòu)流程如下:當(dāng)接收到一個(gè)請(qǐng)求時(shí),首先會(huì)分發(fā)到 Nginx 集群中,這里可以采用 Nginx 的負(fù)載均衡算法分發(fā)給某一臺(tái)機(jī)器,使用輪詢可以降低負(fù)載,或者采用一致性 Hash 算法來(lái)提升緩存命中率。
當(dāng) Nginx 層沒(méi)有緩存數(shù)據(jù)時(shí),會(huì)繼續(xù)向下請(qǐng)求,在分布式緩存集群中查找數(shù)據(jù),如果緩存命中,直接返回(并且寫(xiě)入 Nginx 應(yīng)用緩存中),如果未命中,則回源到 Tomcat 集群中查詢堆內(nèi)緩存。
在分布式緩存中查詢不到數(shù)據(jù),將會(huì)去 Tomcat 集群中查詢堆內(nèi)緩存,查詢成功直接返回(并寫(xiě)入分 Redis 主集群中),查詢失敗請(qǐng)求數(shù)據(jù)庫(kù);堆內(nèi)緩存。
如果以上緩存中都沒(méi)有命中,則直接請(qǐng)求數(shù)據(jù)庫(kù),返回結(jié)果,同步數(shù)據(jù)到分布式緩存中。
在簡(jiǎn)單了解了多級(jí)緩存的基本架構(gòu)之后,我們就該思考如何解決上面提到的一系列問(wèn)題。
緩存熱點(diǎn)
緩存熱點(diǎn),是一個(gè)很常見(jiàn)的問(wèn)題,比如“某某明星宣布結(jié)婚”等等,都可能產(chǎn)生大量請(qǐng)求訪問(wèn)的問(wèn)題,一個(gè)最麻煩也是最容易讓人忽視的事情就是如何探測(cè)到熱點(diǎn) Key。
在緩存系統(tǒng)中,除了一些常用的熱點(diǎn) Key 外,在某些特殊場(chǎng)合下也會(huì)出現(xiàn)大量的熱點(diǎn) Key,我們?cè)撊绾伟l(fā)現(xiàn)呢?
有以下策略:
- 數(shù)據(jù)調(diào)研。可以分析歷史數(shù)據(jù)以及針對(duì)不同的場(chǎng)合去預(yù)測(cè)出熱點(diǎn) Key,這種方式雖然不能百分百使得緩存命中,但是卻是一種最簡(jiǎn)單和節(jié)省成本的方案。
- 實(shí)時(shí)計(jì)算。可以使用現(xiàn)有的實(shí)時(shí)計(jì)算框架,比如 Storm、Spark Streaming、Flink 等框架統(tǒng)計(jì)一個(gè)時(shí)間段內(nèi)的請(qǐng)求量,從而判斷熱點(diǎn) Key。或者也可以自己實(shí)現(xiàn)定時(shí)任務(wù)去統(tǒng)計(jì)請(qǐng)求量。
這里我們著重討論一下第二種解決方案,對(duì)于熱點(diǎn) Key 問(wèn)題,當(dāng)緩存系統(tǒng)中沒(méi)有發(fā)現(xiàn)緩存時(shí),需要去數(shù)據(jù)庫(kù)中讀取數(shù)據(jù)。
當(dāng)大量請(qǐng)求來(lái)的時(shí)候,一個(gè)請(qǐng)求獲取鎖去請(qǐng)求數(shù)據(jù)庫(kù),其他阻塞,接著全部去訪問(wèn)緩存,這樣可能因?yàn)橐慌_(tái)服務(wù)器撐不住從而宕機(jī)。
比如正常一臺(tái)服務(wù)器并發(fā)量為 5W 左右,產(chǎn)生熱點(diǎn) Key 的時(shí)候達(dá)到了 10W 甚至 20W,這樣服務(wù)器肯定會(huì)崩。所以我們?cè)诎l(fā)現(xiàn)熱點(diǎn) Key 之后還需要做到如何自動(dòng)負(fù)載均衡。
結(jié)合以上問(wèn)題我們重新設(shè)計(jì)架構(gòu),如下圖所示:
我們將整個(gè)應(yīng)用架構(gòu)分為:
- 應(yīng)用層
- 分布式緩存
- 系統(tǒng)層
- 數(shù)據(jù)層
在應(yīng)用層,我們采用 Nginx 集群,并且對(duì)接實(shí)時(shí)計(jì)算鏈路,通過(guò) Flume 監(jiān)控 Nginx 日志,將數(shù)據(jù)傳輸?shù)?Kafka 集群中,然后 Flink 集群消費(fèi)數(shù)據(jù)進(jìn)行統(tǒng)計(jì)。
如果統(tǒng)計(jì)結(jié)果為熱點(diǎn) Key,則將數(shù)據(jù)寫(xiě)入 Zookeeper 的節(jié)點(diǎn)中,而應(yīng)用系統(tǒng)通過(guò)監(jiān)控 Znode 節(jié)點(diǎn),讀取熱點(diǎn) Key 數(shù)據(jù),去數(shù)據(jù)庫(kù)中加載數(shù)據(jù)到緩存中并且做到負(fù)載均衡。
實(shí)際上,對(duì)于應(yīng)用系統(tǒng)中的每一臺(tái)服務(wù)器,還需要一層防護(hù)機(jī)制,限流熔斷,這樣做的目的是為了防止單臺(tái)機(jī)器請(qǐng)求量過(guò)高,使得服務(wù)器負(fù)載過(guò)高,不至于服務(wù)器宕機(jī)或者大量請(qǐng)求訪問(wèn)數(shù)據(jù)庫(kù)。
簡(jiǎn)單思路就是為每一臺(tái)服務(wù)器設(shè)計(jì)一個(gè)閥值,當(dāng)請(qǐng)求量大于該值就直接返回用戶空白頁(yè)面或者提示用戶幾秒后刷新重新訪問(wèn)。
數(shù)據(jù)一致性
數(shù)據(jù)一致性問(wèn)題主要體現(xiàn)在緩存更新的時(shí)候,如何更新緩存,保證數(shù)據(jù)庫(kù)與緩存以及各層緩存層之間的一致性。
對(duì)于緩存更新問(wèn)題,先寫(xiě)緩存還是先寫(xiě)數(shù)據(jù)庫(kù),這里省略若干字。之前的文章介紹過(guò),有興趣的讀者可以翻閱。
在單層緩存系統(tǒng)中,我們可以先采用刪除緩存然后更新數(shù)據(jù)庫(kù)的方案來(lái)解決其數(shù)據(jù)一致性問(wèn)題,那么對(duì)于多級(jí)緩存呢?
如果使用這種方案,我們需要考慮,如果先刪除緩存,那么需要逐層去做刪除操作,那么這一系列操作對(duì)系統(tǒng)帶來(lái)的耗時(shí)也是很可觀的。
如果我們使用分布式事務(wù)機(jī)制,就需要考慮該不該將寫(xiě)緩存放入事務(wù)當(dāng)中,因?yàn)槲覀兏路植际骄彺?#xff0c;需要走網(wǎng)絡(luò)通信,大量的請(qǐng)求將導(dǎo)致網(wǎng)路抖動(dòng)甚至阻塞,增加了系統(tǒng)的延遲,導(dǎo)致系統(tǒng)短時(shí)間內(nèi)不可用。
如果我們不將寫(xiě)緩存這一操作放入事務(wù)當(dāng)中,那么可能引起短時(shí)間內(nèi)數(shù)據(jù)不一致。
這也就是分布式系統(tǒng)的 CAP 理論,我們不能同時(shí)達(dá)到高可用和一致性。那么該如何抉擇呢?
這里我們選擇保證系統(tǒng)的可用性,就一個(gè)秒殺系統(tǒng)來(lái)講,短暫的不一致性問(wèn)題對(duì)用戶的體驗(yàn)影響并不大(當(dāng)然,這里不涉及支付系統(tǒng))。
而可用性對(duì)用戶來(lái)說(shuō)卻很重要,一個(gè)活動(dòng)可能在很短的時(shí)間內(nèi)結(jié)束,而用戶需要在這段時(shí)間內(nèi)搶到自己心儀的商品,所以可用性更重要一些(這里需要根據(jù)具體場(chǎng)景進(jìn)行權(quán)衡)。
在保證了系統(tǒng)的可用性的基礎(chǔ)上,我們?cè)撊绾螌?shí)現(xiàn)呢?如果實(shí)時(shí)性要求不是很高,我們可以采用全量+增量同步的方式進(jìn)行。
首先,我們可以按照預(yù)計(jì)的熱點(diǎn) Key 對(duì)系統(tǒng)進(jìn)行緩存預(yù)熱,全量同步數(shù)據(jù)到緩存系統(tǒng)。
接著,在需要更新緩存的時(shí)候,我們可以采用增量同步的方式更新緩存。比如我們可以使用阿里 Canal 框架同步 Binlog 的方式進(jìn)行數(shù)據(jù)的同步。
緩存過(guò)期
緩存系統(tǒng)中的所有數(shù)據(jù),根據(jù)數(shù)據(jù)的使用頻率以及場(chǎng)景,我們可以分為過(guò)期 Key 以及不過(guò)期 Key,那么對(duì)于過(guò)期緩存我們?cè)撊绾翁蕴?
下面有常用的幾種方案:
- FIFO:使用 FIFO 算法來(lái)淘汰過(guò)期緩存。
- LFU:使用 LFU 算法來(lái)淘汰過(guò)期緩存。
- LRU:使用 LRU 算法來(lái)淘汰過(guò)期緩存。
以上幾種方案是在緩存達(dá)到最大緩存大小的時(shí)候的淘汰策略,如果沒(méi)有達(dá)到最大緩存大小,我們有下面幾種方式:
- 定時(shí)刪除策略:設(shè)置一個(gè)定時(shí)任務(wù),在規(guī)定時(shí)間內(nèi)檢查并且刪除過(guò)期 Key。
- 定期刪除策略:這種策略需要設(shè)置刪除的周期以及時(shí)長(zhǎng),如何設(shè)置,需要根據(jù)具體場(chǎng)合來(lái)計(jì)算。
- 惰性刪除策略:在使用時(shí)檢查是否過(guò)期,如果過(guò)期直接去更新緩存,否則直接返回。
作者:不清不慎來(lái)源:架構(gòu)師社區(qū)
總結(jié)
以上是生活随笔為你收集整理的fifo页面置换算法设计思路_千万级并发!如何设计一个多级缓存系统?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: axs是什么币种
- 下一篇: java信息管理系统总结_java实现科