日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

分布式緩存的通用方法—《可伸縮服務(wù)架構(gòu)》

 xujin3 2018-06-17

本文節(jié)選自《可伸縮服務(wù)架構(gòu):框架與中間件》 第四章《緩存的本質(zhì)和緩存使用的優(yōu)秀實(shí)踐》。PS:本文最后有《可伸縮服務(wù)架構(gòu):框架與中間件》共5本抽獎(jiǎng)活動(dòng),試試你的運(yùn)氣吧~


4 分布式緩存的通用方法

筆者所在的多家互聯(lián)網(wǎng)公司大量使用了緩存,對(duì)分布式緩存的應(yīng)用可謂遍地開花,筆者曾供職的一家社交媒體網(wǎng)站,號(hào)稱是世界上使用緩存最多的公司。毋庸置疑,緩存幫助我們解決了很多性能問(wèn)題,甚至幫助我們解決了一些并發(fā)問(wèn)題。


4.4.1  緩存編程的具體方法

各種分布式緩存如Redis,都提供了不同語(yǔ)言的客戶端API,我們可以使用這些API直接訪問(wèn)緩存,也可以通過(guò)注解等方法使用緩存。


1.編程法

編程法指通過(guò)編程的方式直接訪問(wèn)緩存,偽代碼如下:

這種方法實(shí)現(xiàn)起來(lái)簡(jiǎn)單,但是每次使用時(shí)都得敲入類似上面這樣的一段代碼,很煩瑣,可以將這部分內(nèi)容抽象成一個(gè)框架,請(qǐng)參考下面的小節(jié)。


2.Spring注入法

spring-data-redis項(xiàng)目(https://projects./spring-data-redis)實(shí)現(xiàn)了注入法,通過(guò)Bean注入就可以直接使用Spring的緩存模板提供的方法。


首先,引入spring-data-redis包:


然后在Spring環(huán)境下進(jìn)行如下配置:


再通過(guò)Spring環(huán)境注入使用的服務(wù)中:


3.注解法

spring-data-redis項(xiàng)目(https://projects./spring-data-redis)實(shí)現(xiàn)了注解法,通過(guò)注解就可以在一個(gè)方法內(nèi)部使用緩存,緩存操作都是透明的,我們不再需要重復(fù)寫上面的一段代碼。

首先,引入相應(yīng)的依賴包:

然后,通過(guò)一個(gè)配置Bean配置Redis連接信息,這個(gè)配置Bean會(huì)通過(guò)Spring環(huán)境下的Bean掃描載入:


再在Spring環(huán)境下載入這些配置:


最后,我們就可以通過(guò)注解來(lái)使用Redis緩存了,這樣我們的代碼就簡(jiǎn)單得多了:


4.4.2  應(yīng)用層訪問(wèn)緩存的模式

應(yīng)用層訪問(wèn)分布式緩存的服務(wù)架構(gòu)模式分為:雙讀雙寫、異步更新和串聯(lián)模式。


1.雙讀雙寫

雙讀雙寫的架構(gòu)如圖4-12所示。

圖4-12


這是我們最常用的緩存服務(wù)架構(gòu)模式,對(duì)于讀操作,我們先讀緩存,如果緩存不存在這份數(shù)據(jù),則再讀數(shù)據(jù)庫(kù),讀取數(shù)據(jù)庫(kù)后再回寫緩存;對(duì)于寫操作,我們先寫數(shù)據(jù)庫(kù),再寫緩存。


這種方式實(shí)現(xiàn)起來(lái)簡(jiǎn)單,但是對(duì)應(yīng)用層不透明,應(yīng)用層需要處理讀寫順序的邏輯,可參考4.4.1節(jié)。


2.異步更新

異步更新的架構(gòu)如圖4-13所示。在異步更新的方式中,應(yīng)用層只讀寫緩存,在這種情況下,全量數(shù)據(jù)會(huì)被保存在緩存中,并且不設(shè)置緩存系統(tǒng)的過(guò)期時(shí)間,由異步的更新服務(wù)將數(shù)據(jù)庫(kù)里變更的或者新增的數(shù)據(jù)更新到緩存中。也有通過(guò)MySQL的binlog將MySQL中的更新操作推送到緩存的實(shí)現(xiàn)方法,這種方法和異步更新如出一轍,以Facebook的方案(請(qǐng)參考論文Scaling Memcache atFacebook)為例,如圖4-14所示。

圖4-13

圖4-14

這種方法實(shí)現(xiàn)起來(lái)稍微復(fù)雜,增加了更新服務(wù),這里的更新服務(wù)需要定時(shí)調(diào)度任務(wù)的設(shè)計(jì),請(qǐng)參考第6章的內(nèi)容,這需要更多的開發(fā)和運(yùn)維成本,在設(shè)計(jì)異步服務(wù)時(shí)要充分保證異步服務(wù)的可用性,要有完善的監(jiān)控和報(bào)警,否則緩存數(shù)據(jù)將和數(shù)據(jù)庫(kù)不一致。但是在這種模式下性能最好,因?yàn)閼?yīng)用層讀緩存即可,不需要讀取數(shù)據(jù)庫(kù)。


3.串聯(lián)模式

串聯(lián)模式的架構(gòu)如圖4-15所示。

圖4-15


在這種串聯(lián)模式下,應(yīng)用直接在緩存上進(jìn)行讀寫操作,緩存作為代理層,根據(jù)需要和配置與數(shù)據(jù)庫(kù)進(jìn)行讀寫操作。


在微服務(wù)的設(shè)置中并不推薦采用這種服務(wù)的串聯(lián)模式,因?yàn)樗趹?yīng)用和數(shù)據(jù)庫(kù)中間增加了一層代理層,需要設(shè)計(jì)和維護(hù)這多出的一層,還要保證高可用性,成本較高,但是這種模式有著特殊的場(chǎng)景,比如我們需要在代理層開啟緩存加速,例如Varnish等。


4.4.3  分布式緩存分片的三種模式

在互聯(lián)網(wǎng)行業(yè)里,我們做的都是用戶端的產(chǎn)品,有調(diào)查稱中國(guó)有6億互聯(lián)網(wǎng)用戶,這么多的用戶會(huì)給互聯(lián)網(wǎng)應(yīng)用帶來(lái)了海量的請(qǐng)求,這也需要存儲(chǔ)海量的數(shù)據(jù),因此,單機(jī)的緩存滿足不了對(duì)海量數(shù)據(jù)緩存的需求。


我們通常通過(guò)多個(gè)緩存節(jié)點(diǎn)來(lái)緩存大量的臨時(shí)數(shù)據(jù),來(lái)加速緩存的存取速度,例如,可以把微博的粉絲關(guān)系存儲(chǔ)在緩存中,在獲取某個(gè)用戶有權(quán)限看見的微博時(shí),我們就可以使用這些粉絲關(guān)系,粉絲關(guān)系的數(shù)據(jù)量非常大,一個(gè)大V用戶可能有幾千萬(wàn)或者上億的關(guān)注量,可想而知,我們需要多大的內(nèi)存才能夠存儲(chǔ)這么多的粉絲關(guān)系。


在通用的解決方案中,我們會(huì)對(duì)這些大數(shù)據(jù)量進(jìn)行切片,數(shù)據(jù)被分成大小相等的分片,一個(gè)緩存節(jié)點(diǎn)負(fù)責(zé)存儲(chǔ)其中的多個(gè)分片。分片通常有三種實(shí)現(xiàn)方式,包括客戶端分片、代理分片和集群分片。


1.客戶端分片

對(duì)緩存進(jìn)行客戶端分片的方案如圖4-16所示。

圖4-16


客戶端分片通過(guò)應(yīng)用層直接操作分片邏輯,分片規(guī)則需要在同一個(gè)應(yīng)用的多個(gè)節(jié)點(diǎn)間保存,每個(gè)應(yīng)用層都嵌入一個(gè)操作切片的邏輯實(shí)現(xiàn),一般通過(guò)依賴Jar包來(lái)實(shí)現(xiàn)。筆者曾工作過(guò)的幾家大的互聯(lián)網(wǎng)公司都有內(nèi)部的緩存分片的實(shí)現(xiàn),多數(shù)是采用在應(yīng)用層直接實(shí)現(xiàn)的方式,應(yīng)用層分片的性能更好,實(shí)現(xiàn)簡(jiǎn)單,有問(wèn)題時(shí)容易定位和修復(fù),4.6 節(jié)介紹的開源項(xiàng)目redic(https:///robertleepeak/redic)也是采用這種方案實(shí)現(xiàn)的。

這種解決方案的性能很好,實(shí)現(xiàn)起來(lái)比較簡(jiǎn)單,適合快速上線,而且切分邏輯是自己開發(fā)的,如果在生產(chǎn)上出了問(wèn)題,則都比較容易解決;但是它侵入了業(yè)務(wù)邏輯的實(shí)現(xiàn),會(huì)讓緩存服務(wù)器保持的應(yīng)用程序連接比較多,這要看應(yīng)用服務(wù)器池的節(jié)點(diǎn)數(shù)量,需要提前進(jìn)行容量評(píng)估,請(qǐng)參考《分布式服務(wù)架構(gòu):原理、設(shè)計(jì)與實(shí)戰(zhàn)》第3章的內(nèi)容。


2.代理分片

對(duì)緩存進(jìn)行代理分片的方案如圖4-17所示。


代理分片就是在應(yīng)用層和緩存服務(wù)器中間增加一個(gè)代理層,把分片的路由規(guī)則配置在代理層,代理層對(duì)外提供與緩存服務(wù)器兼容的接口給應(yīng)用層,應(yīng)用層的開發(fā)人員不用關(guān)心分片規(guī)則,只需關(guān)心業(yè)務(wù)邏輯的實(shí)現(xiàn),待業(yè)務(wù)邏輯實(shí)現(xiàn)以后,在代理層配置路由規(guī)則即可。

圖4-17


這種方案的好處是讓應(yīng)用層開發(fā)人員專注于業(yè)務(wù)邏輯的實(shí)現(xiàn),把緩存分片的配置留給代理層做,具體可以由運(yùn)維人員來(lái)實(shí)施;缺點(diǎn)是增加了代理層。盡管代理層是輕量級(jí)的轉(zhuǎn)發(fā)協(xié)議,但是畢竟要實(shí)現(xiàn)緩存協(xié)議的解析,并通過(guò)分片的路由規(guī)則來(lái)路由請(qǐng)求,對(duì)每個(gè)緩存操作都增加了一層代理網(wǎng)絡(luò)傳輸,對(duì)性能是有影響的,對(duì)增加的代理層也需要進(jìn)行維護(hù),也有硬件成本,還要有能夠解決Bug的技術(shù)專家,成本很高。


流行的Codis框架就是代理分片的典型實(shí)現(xiàn)。


3.集群分片

緩存自身提供的集群分片方案如圖4-18所示。


有的緩存自身提供了集群功能,集群可以實(shí)現(xiàn)分片和高可用特性,我們只需要把它們當(dāng)成一個(gè)由多個(gè)緩存服務(wù)器節(jié)點(diǎn)組成的大緩存機(jī)器來(lái)使用即可,分片和高可用等對(duì)應(yīng)用層是透明的,由運(yùn)維人員配置即可使用,典型的就是Redis 3.0提供的Cluster。我們已經(jīng)在4.3.2節(jié)介紹了Redis集群。

圖4-18


4.4.4  分布式緩存的遷移方案

處理分布式緩存遷移是比較困難的,通常我們將其分為平滑遷移和停機(jī)遷移。這里講解通用的遷移方案,擴(kuò)容實(shí)際上是遷移的一種特殊案例,我們?cè)谙旅鎸W(xué)習(xí)的方案全部適用。我們會(huì)在講解該方案的過(guò)程中,以擴(kuò)容為例來(lái)說(shuō)明相應(yīng)的步驟和實(shí)現(xiàn)細(xì)節(jié)。


1.平滑遷移

平滑遷移適合對(duì)可用性要求較高的場(chǎng)景,例如,線上的交易服務(wù)對(duì)緩存依賴較大,不能忍受停機(jī)帶來(lái)的業(yè)務(wù)損失,也沒(méi)有交易的低峰期,我們對(duì)此只能采用平滑遷移的方式。


平滑遷移使用的是雙寫方案,方案分成4個(gè)步驟:雙寫、遷移歷史數(shù)據(jù)、切讀、下雙寫。


這種方式還有一個(gè)變種,就是不需要遷移老數(shù)據(jù),在第1步中雙寫后,在一定的時(shí)間里通過(guò)新規(guī)則對(duì)新緩存進(jìn)行寫入,新緩存已經(jīng)有了足夠的數(shù)據(jù),這樣我們就不用再遷移舊數(shù)據(jù),直接進(jìn)入第3步即可。


首先,假設(shè)我們的應(yīng)用現(xiàn)在使用了具有兩個(gè)分片的緩存集群,通過(guò)關(guān)鍵字哈希的方式進(jìn)行路由,如圖4-19所示。

圖4-19

因?yàn)閮蓚€(gè)分片已經(jīng)不能滿足緩存容量的需求,所以現(xiàn)在需要擴(kuò)容到4個(gè)分片,達(dá)到原來(lái)兩倍的緩存總大小,因此我們需要遷移。


遷移的具體過(guò)程如下。


第1步,雙寫。按照新規(guī)則和舊規(guī)則同時(shí)往新緩存和舊緩存中寫數(shù)據(jù),如圖4-20所示。

圖4-20

這里,我們?nèi)匀话凑张f的規(guī)則,也就是關(guān)鍵字哈希除以2取余來(lái)路由分片,同時(shí)按照新的規(guī)則,也就是關(guān)鍵字哈希除以4取余來(lái)路由到新的4個(gè)分片上,來(lái)完成數(shù)據(jù)的雙寫。


這個(gè)步驟有優(yōu)化的空間,因?yàn)槭窃诔杀稊U(kuò)容的場(chǎng)景下,所以我們不需要準(zhǔn)備4個(gè)全新的分片。新規(guī)則中前兩個(gè)分片的數(shù)據(jù),其實(shí)是舊規(guī)則中兩個(gè)分片數(shù)據(jù)的子集,并且規(guī)則一致,所以我們可以重用前兩個(gè)分片,也就是說(shuō)一共需要兩個(gè)新的分片,用來(lái)處理關(guān)鍵字哈希取余后為2和3的情況;使用舊的緩存分片來(lái)處理關(guān)鍵字哈希取余后0和1的情況即可。如圖4-21所示。

圖4-21


第2步,遷移歷史數(shù)據(jù)。把舊緩存集群中的歷史數(shù)據(jù)讀取出來(lái),按照新的規(guī)則寫到新的緩存集群中,如圖4-22所示。

圖4-22


在這個(gè)過(guò)程中,我們需要遷移歷史數(shù)據(jù),在遷移的過(guò)程中可能需要遷移工具,這也需要一部分開發(fā)工作量。在遷移后,我們還需要對(duì)遷移的數(shù)據(jù)進(jìn)行驗(yàn)證,表明我們的數(shù)據(jù)遷移成功。


在某些應(yīng)用場(chǎng)景下,緩存數(shù)據(jù)并不是應(yīng)用強(qiáng)依賴的,在緩存里獲取不到數(shù)據(jù),可以回源到數(shù)據(jù)庫(kù)獲取,因此在這種場(chǎng)景下通過(guò)容量評(píng)估,數(shù)據(jù)庫(kù)可以承受回源導(dǎo)致的壓力增加,就可以避免遷移舊數(shù)據(jù)。在另一種場(chǎng)景下,緩存數(shù)據(jù)一般是具有時(shí)效性的,應(yīng)用在雙寫期間不斷向新的集群中寫入新數(shù)據(jù),歷史數(shù)據(jù)會(huì)逐漸過(guò)時(shí),并被從舊的集群中刪除,在一定的時(shí)間流逝后,在新的集群中自然就有了最新的數(shù)據(jù),也就不再需要遷移歷史數(shù)據(jù)了,但是這需要進(jìn)行評(píng)估和驗(yàn)證。


第3步,切讀。把應(yīng)用層所有的讀操作路由到新的緩存集群上,如圖4-23所示。

圖4-23


這一步把應(yīng)用中讀取的操作的緩存數(shù)據(jù)源轉(zhuǎn)換成新的緩存集群,這時(shí)應(yīng)用的讀寫操作已經(jīng)完全發(fā)生在新的數(shù)據(jù)庫(kù)集群上了。這一步一般不需要上線代碼,我們會(huì)在一開始上雙寫時(shí)就實(shí)現(xiàn)開關(guān)邏輯,這里只需要將讀的開關(guān)切換到新的集群即可。


第4步,下線雙寫。把寫入舊的集群的邏輯下線,如圖4-24所示。

圖4-24


這一步通常是在雙寫和切讀后驗(yàn)證沒(méi)有任何問(wèn)題,并保證數(shù)據(jù)一致性的情況下,才把這部分代碼下線的。同時(shí)可以把舊的分片下線,如果是擴(kuò)容的場(chǎng)景,并且重用了舊的分片1和分片2,則還可以清理分片1和分片2中的冗余數(shù)據(jù)。


2.停機(jī)遷移

停機(jī)遷移的方法比較簡(jiǎn)單,通常分為停止應(yīng)用、遷移歷史數(shù)據(jù)、更改應(yīng)用的數(shù)據(jù)源、啟動(dòng)應(yīng)用這4個(gè)步驟,如圖4-25所示。

圖4-25


具體的遷移步驟如下。

(1)停機(jī)應(yīng)用,先將應(yīng)用停止服務(wù)。

(2)遷移歷史數(shù)據(jù),按照新的規(guī)則把歷史數(shù)據(jù)遷移到新的緩存集群中。

(3)更改應(yīng)用的數(shù)據(jù)源配置,指向新的緩存集群。

(4)重新啟動(dòng)應(yīng)用。

這種方式的好處是實(shí)現(xiàn)比較簡(jiǎn)單、高效,能夠有效避免數(shù)據(jù)的不一致,但是需要由業(yè)務(wù)方評(píng)估影響,一般在晚上交易量比較小或者非核心服務(wù)的場(chǎng)景下比較適用。


3.一致性哈希

實(shí)際上,Redis的客戶端Jedis本身實(shí)現(xiàn)了基于一致性哈希的客戶端路由框架,這種框架的好處是便于動(dòng)態(tài)擴(kuò)容,當(dāng)一致性哈希中的節(jié)點(diǎn)的負(fù)載較高時(shí),我們可以動(dòng)態(tài)地插入更多的節(jié)點(diǎn),來(lái)減少已存節(jié)點(diǎn)的壓力。


一致性哈希算法是在1997年由麻省理工學(xué)院的Karger等人在解決分布式緩存問(wèn)題時(shí)提出的一種方案,設(shè)計(jì)目標(biāo)是解決網(wǎng)絡(luò)中的熱點(diǎn)問(wèn)題,后來(lái)在分布式系統(tǒng)中也得到了廣泛應(yīng)用。研究過(guò)Redis和Memcache緩存的人一般都了解一致性哈希算法,他們都在客戶端實(shí)現(xiàn)了一致性哈希。


一致性哈希的邏輯如圖4-26所示。

圖4-26


在收到訪問(wèn)一個(gè)主鍵的請(qǐng)求后,可通過(guò)下面的流程尋找這個(gè)主鍵的存儲(chǔ)節(jié)點(diǎn)。

(1)求出Redis服務(wù)器(節(jié)點(diǎn))的哈希值,并將其配置到0-232的圓(continuum)上。

(2)采用同樣的方法求出存儲(chǔ)數(shù)據(jù)的鍵的哈希值,并映射到相同的圓上。

(3)從數(shù)據(jù)映射到的位置開始順時(shí)針查找,找到的第1臺(tái)服務(wù)器就是將數(shù)據(jù)保存的位置。

(4)如果在尋找的過(guò)程中超過(guò)232仍然找不到節(jié)點(diǎn),就會(huì)保存到第1臺(tái)服務(wù)器上。


在擴(kuò)容的場(chǎng)景下添加一臺(tái)服務(wù)器節(jié)點(diǎn)5時(shí),只有在圓上增加服務(wù)器的位置到逆時(shí)針?lè)较虻牡谝慌_(tái)服務(wù)器上的鍵會(huì)受到影響,如圖4-27所示。

圖4-27


我們看到,在節(jié)點(diǎn)3和節(jié)點(diǎn)4之間增加了節(jié)點(diǎn)5,影響范圍是節(jié)點(diǎn)3到節(jié)點(diǎn)5之間的數(shù)據(jù),而并不影響其他節(jié)點(diǎn)的數(shù)據(jù),因此,這為緩存的擴(kuò)容提供了便利性,當(dāng)緩存壓力增加且緩存容量不夠時(shí),我們通??梢酝ㄟ^(guò)在線增加節(jié)點(diǎn)的方式來(lái)完成擴(kuò)容。


4.4.5  緩存穿透、緩存并發(fā)和緩存雪崩

緩存穿透、緩存并發(fā)和緩存雪崩是常見的由于并發(fā)量大而導(dǎo)致的緩存問(wèn)題,本節(jié)講解其產(chǎn)生原因和解決方案。


緩存穿透通常是由惡意攻擊或者無(wú)意造成的;緩存并發(fā)是由設(shè)計(jì)不足造成的;緩存雪崩是由緩存同時(shí)失效造成的,三種問(wèn)題都比較典型,也是難以防范和解決的。本節(jié)給出通用的解決方案,以供在緩存設(shè)計(jì)的過(guò)程中參考和使用。


1.緩存穿透

緩存穿透指的是使用不存在的key進(jìn)行大量的高并發(fā)查詢,這導(dǎo)致緩存無(wú)法命中,每次請(qǐng)求都要穿透到后端數(shù)據(jù)庫(kù)系統(tǒng)進(jìn)行查詢,使數(shù)據(jù)庫(kù)壓力過(guò)大,甚至使數(shù)據(jù)庫(kù)服務(wù)被壓死。


我們通常將空值緩存起來(lái),再次接收到同樣的查詢請(qǐng)求時(shí),若命中緩存并且值為空,就會(huì)直接返回,不會(huì)透?jìng)鞯綌?shù)據(jù)庫(kù),避免緩存穿透。當(dāng)然,有時(shí)惡意襲擊者可以猜到我們使用了這種方案,每次都會(huì)使用不同的參數(shù)來(lái)查詢,這就需要我們對(duì)輸入的參數(shù)進(jìn)行過(guò)濾,例如,如果我們使用ID進(jìn)行查詢,則可以對(duì)ID的格式進(jìn)行分析,如果不符合產(chǎn)生ID的規(guī)則,就直接拒絕,或者在ID上放入時(shí)間信息,根據(jù)時(shí)間信息判斷ID是否合法,或者是否是我們?cè)?jīng)生成的ID,這樣可以攔截一定的無(wú)效請(qǐng)求。


當(dāng)然,每個(gè)設(shè)計(jì)人員都應(yīng)該對(duì)服務(wù)的可用性和健壯性負(fù)責(zé),應(yīng)該建設(shè)健壯的服務(wù),讓我們的服務(wù)像不倒翁一樣,因此,我們需要對(duì)服務(wù)設(shè)計(jì)限流和熔斷等功能,請(qǐng)參考《分布式服務(wù)架構(gòu):原理、設(shè)計(jì)與實(shí)戰(zhàn)》中第1章關(guān)于微服務(wù)設(shè)計(jì)模式的內(nèi)容。


2.緩存并發(fā)

緩存并發(fā)的問(wèn)題通常發(fā)生在高并發(fā)的場(chǎng)景下,當(dāng)一個(gè)緩存key過(guò)期時(shí),因?yàn)樵L問(wèn)這個(gè)緩存key 的請(qǐng)求量較大,多個(gè)請(qǐng)求同時(shí)發(fā)現(xiàn)緩存過(guò)期,因此多個(gè)請(qǐng)求會(huì)同時(shí)訪問(wèn)數(shù)據(jù)庫(kù)來(lái)查詢最新數(shù)據(jù),并且回寫緩存,這樣會(huì)造成應(yīng)用和數(shù)據(jù)庫(kù)的負(fù)載增加,性能降低,由于并發(fā)較高,甚至?xí)?dǎo)致數(shù)據(jù)庫(kù)被壓死。


我們通常有3種方式來(lái)解決這個(gè)問(wèn)題。

1)分布式鎖

使用分布式鎖,保證對(duì)于每個(gè)key同時(shí)只有一個(gè)線程去查詢后端服務(wù),其他線程沒(méi)有獲得分布式鎖的權(quán)限,因此只需要等待即可。這種方式將高并發(fā)的壓力轉(zhuǎn)移到了分布式鎖,因此對(duì)分布式鎖的考驗(yàn)很大。


2)本地鎖

與分布式鎖類似,我們通過(guò)本地鎖的方式來(lái)限制只有一個(gè)線程去數(shù)據(jù)庫(kù)中查詢數(shù)據(jù),而其他線程只需等待,等前面的線程查詢到數(shù)據(jù)后再訪問(wèn)緩存。但是,這種方法只能限制一個(gè)服務(wù)節(jié)點(diǎn)只有一個(gè)線程去數(shù)據(jù)庫(kù)中查詢,如果一個(gè)服務(wù)有多個(gè)節(jié)點(diǎn),則還會(huì)有多個(gè)數(shù)據(jù)庫(kù)查詢操作,也就是說(shuō)在節(jié)點(diǎn)數(shù)量較多的情況下并沒(méi)有完全解決緩存并發(fā)的問(wèn)題。


3)軟過(guò)期

軟過(guò)期指對(duì)緩存中的數(shù)據(jù)設(shè)置失效時(shí)間,就是不使用緩存服務(wù)提供的過(guò)期時(shí)間,而是業(yè)務(wù)層在數(shù)據(jù)中存儲(chǔ)過(guò)期時(shí)間信息,由業(yè)務(wù)程序判斷是否過(guò)期并更新,在發(fā)現(xiàn)了數(shù)據(jù)即將過(guò)期時(shí),將緩存的時(shí)效延長(zhǎng),程序可以派遣一個(gè)線程去數(shù)據(jù)庫(kù)中獲取最新的數(shù)據(jù),其他線程這時(shí)看到延長(zhǎng)了的過(guò)期時(shí)間,就會(huì)繼續(xù)使用舊數(shù)據(jù),等派遣的線程獲取最新數(shù)據(jù)后再更新緩存。


也可以通過(guò)異步更新服務(wù)來(lái)更新設(shè)置軟過(guò)期的緩存,這樣應(yīng)用層就不用關(guān)心緩存并發(fā)的問(wèn)題了。


3.緩存雪崩

緩存雪崩指緩存服務(wù)器重啟或者大量緩存集中在某一個(gè)時(shí)間段內(nèi)失效,給后端數(shù)據(jù)庫(kù)造成瞬時(shí)的負(fù)載升高的壓力,甚至壓垮數(shù)據(jù)庫(kù)的情況。


通常的解決辦法是對(duì)不同的數(shù)據(jù)使用不同的失效時(shí)間,甚至對(duì)相同的數(shù)據(jù)、不同的請(qǐng)求使用不同的失效時(shí)間,例如,我們要緩存user數(shù)據(jù),會(huì)對(duì)每個(gè)用戶的數(shù)據(jù)設(shè)置不同的緩存過(guò)期時(shí)間,可以定義一個(gè)基礎(chǔ)時(shí)間,假設(shè)10秒,然后加上一個(gè)兩秒以內(nèi)的隨機(jī)數(shù),過(guò)期時(shí)間為10~12秒,就會(huì)避免緩存雪崩。


4.4.6  緩存對(duì)事務(wù)的支持

在使用Redis緩存的業(yè)務(wù)場(chǎng)景時(shí)經(jīng)常會(huì)有這樣的需求:要求遞減一個(gè)變量,如果遞減后變量小于等于0,則返回一個(gè)標(biāo)志;如果成功,則返回剩余的值,類似于數(shù)據(jù)庫(kù)事務(wù)的實(shí)現(xiàn)。


在實(shí)現(xiàn)中需要注意服務(wù)器端的多線程問(wèn)題及客戶端的多線程問(wèn)題。在服務(wù)器端可以利用服務(wù)器單線程執(zhí)行LUA腳本來(lái)保證,或者通過(guò)WATCH、EXEC、DISCARD、EXEC來(lái)保證。


在Redis中支持LUA腳本,由于Redis使用單線程實(shí)現(xiàn),因此我們首先給出LUA腳本的實(shí)現(xiàn)方案。在如下代碼中,我們看到變量被遞減,并判斷是否將小于0的操作放到LUA腳本里,利用Redis的單線程執(zhí)行的特性完成這個(gè)原子遞減的操作:

還可以通過(guò)Redis對(duì)事務(wù)的支持方法watch和multi來(lái)實(shí)現(xiàn),類似于一個(gè)CAS方法的實(shí)現(xiàn),如果對(duì)熱數(shù)據(jù)有競(jìng)爭(zhēng),則會(huì)返回失敗,然后重試直到成功:


    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多