一.Redis簡(jiǎn)介
Redis 是完全開源免費(fèi)的,是一個(gè)高性能的key-value類型的內(nèi)存數(shù)據(jù)庫(kù)。整個(gè)數(shù)據(jù)庫(kù)統(tǒng)統(tǒng)加載在內(nèi)存當(dāng)中進(jìn)行操作,定期通過異步操作把數(shù)據(jù)庫(kù)數(shù)據(jù)flush到硬盤上進(jìn)行保存。因?yàn)槭羌儍?nèi)存操作,Redis的性能非常出色,每秒可以處理超過 10萬次讀寫操作,是已知性能最快的Key-Value DB。
Redis的出色之處不僅僅是性能,Redis最大的魅力是支持保存多種數(shù)據(jù)結(jié)構(gòu),此外單個(gè)value的最大限制是1GB,因此Redis可以用來實(shí)現(xiàn)很多有用的功能,比方說用List來做FIFO雙向鏈表,實(shí)現(xiàn)一個(gè)輕量級(jí)的高性 能消息隊(duì)列服務(wù),用他的Set可以做高性能的tag系統(tǒng)等等。另外Redis也可以對(duì)存入的Key-Value設(shè)置expire時(shí)間??偨Y(jié)來說,使用Redis的好處如下:
1.速度快,因?yàn)閿?shù)據(jù)存在內(nèi)存中,讀的速度是 110000 次 /s, 寫的速度是 81000 次 /s;
2.支持豐富數(shù)據(jù)類型,支持string,list,set,sorted set,hash;
3.支持事務(wù),操作都是原子性,對(duì)數(shù)據(jù)的更改要么全部執(zhí)行,要么全部不執(zhí)行,事務(wù)中任意命令執(zhí)行失敗,其余命令依然被執(zhí)行。也就是說 Redis 事務(wù)不保證原子性,也不支持回滾;事務(wù)中的多條命令被一次性發(fā)送給服務(wù)器,服務(wù)器在執(zhí)行命令期間,不會(huì)去執(zhí)行其他客戶端的命令請(qǐng)求。
4.豐富的特性:可用于緩存,消息(支持 publish/subscribe 通知),按key設(shè)置過期時(shí)間,過期后將會(huì)自動(dòng)刪除,具體淘汰策略有:
4.1.volatile-lru:從已經(jīng)設(shè)置過期時(shí)間的數(shù)據(jù)集中,挑選最近最少使用的數(shù)據(jù)淘汰
4.2.volatile-ttl:從已經(jīng)設(shè)置過期時(shí)間的數(shù)據(jù)集中,挑選即將要過期的數(shù)據(jù)淘汰
4.3.volatile-random:從已經(jīng)設(shè)置過期時(shí)間的數(shù)據(jù)集中,隨機(jī)挑選數(shù)據(jù)淘汰
4.4.allkeys-lru:從所有的數(shù)據(jù)集中,挑選最近最少使用的數(shù)據(jù)淘汰
4.5.allkeys-random:從所有的數(shù)據(jù)集中,隨機(jī)挑選數(shù)據(jù)淘汰
4.6.no-enviction:禁止淘汰數(shù)據(jù)
具體過期鍵的策略有:定時(shí)刪除(緩存過期時(shí)間到就刪除,創(chuàng)建timer耗CPU),惰性刪除(獲取的時(shí)候檢查,不獲取一直留在內(nèi)存,對(duì)內(nèi)存不友好),定期刪除(CPU和內(nèi)存的折中方案)
5.支持?jǐn)?shù)據(jù)持久化,可以將內(nèi)存中的數(shù)據(jù)保存在磁盤中,重啟的時(shí)候可以再次加載進(jìn)行使用;
6.支持?jǐn)?shù)據(jù)的備份,即 master - slave 模式的數(shù)據(jù)備份。
Redis的主要缺點(diǎn)是數(shù)據(jù)庫(kù)容量受到物理內(nèi)存的限制,不能用作海量數(shù)據(jù)的高性能讀寫,因此Redis適合的場(chǎng)景主要局限在較小數(shù)據(jù)量的高性能操作和運(yùn)算上。
二.Redis的數(shù)據(jù)類型
Redis 支持 5 中數(shù)據(jù)類型:string(字符串),hash(哈希),list(列表),set(集合),zset(sorted set:有序集合)。每種數(shù)據(jù)類型的具體命令請(qǐng)參考Redis 命令參考
string
string 是 redis 最基本的數(shù)據(jù)類型。一個(gè) key 對(duì)應(yīng)一個(gè) value。string 是二進(jìn)制安全的。也就是說 redis 的 string 可以包含任何數(shù)據(jù)。比如 jpg 圖片或者序列化的對(duì)象。string 類型是 redis 最基本的數(shù)據(jù)類型,string 類型的值最大能存儲(chǔ) 512 MB。
hash
Redis hash 是一個(gè)鍵值對(duì)(key - value)集合。Redis hash 是一個(gè) string 類型的 key 和 value 的映射表,hash 特別適合用于存儲(chǔ)對(duì)象。并且可以像數(shù)據(jù)庫(kù)中一樣只對(duì)某一項(xiàng)屬性值進(jìn)行存儲(chǔ)、讀取、修改等操作。
list
Redis 列表是簡(jiǎn)單的字符串列表,按照插入順序排序。我們可以網(wǎng)列表的左邊或者右邊添加元素。?list 就是一個(gè)簡(jiǎn)單的字符串集合,和 Java 中的 list 相差不大,區(qū)別就是這里的 list 存放的是字符串。list 內(nèi)的元素是可重復(fù)的??梢宰鱿㈥?duì)列或最新消息排行等功能。
set
redis 的 set 是字符串類型的無序集合。集合是通過哈希表實(shí)現(xiàn)的,因此添加、刪除、查找的復(fù)雜度都是 O(1)。redis 的 set 是一個(gè) key 對(duì)應(yīng)著多個(gè)字符串類型的 value,也是一個(gè)字符串類型的集合,和 redis 的 list 不同的是 set 中的字符串集合元素不能重復(fù),但是 list 可以。利用唯一性,可以統(tǒng)計(jì)訪問網(wǎng)站的所有獨(dú)立 ip。
Zset
redis zset 和 set 一樣都是字符串類型元素的集合,并且集合內(nèi)的元素不能重復(fù)。不同的是 zset 每個(gè)元素都會(huì)關(guān)聯(lián)一個(gè) double 類型的分?jǐn)?shù)。redis 通過分?jǐn)?shù)來為集合中的成員進(jìn)行從小到大的排序。zset 的元素是唯一的,但是分?jǐn)?shù)(score)卻可以重復(fù)??捎米髋判邪竦葓?chǎng)景。
三.redis適用場(chǎng)景
1.會(huì)話緩存(Session Cache)
最常用的一種使用Redis的情景是會(huì)話緩存(session cache)。用Redis緩存會(huì)話比其他存儲(chǔ)(如Memcached)的優(yōu)勢(shì)在于:Redis提供持久化。當(dāng)維護(hù)一個(gè)不是嚴(yán)格要求一致性的緩存時(shí),如果用戶的購(gòu)物車信息全部丟失,大部分人都會(huì)不高興的。
2.隊(duì)列
Reids在內(nèi)存存儲(chǔ)引擎領(lǐng)域的一大優(yōu)點(diǎn)是提供 list 和 set 操作,這使得Redis能作為一個(gè)很好的消息隊(duì)列平臺(tái)來使用。Redis作為隊(duì)列使用的操作,就類似于本地程序語言(如Python)對(duì) list 的 push/pop 操作。
3.全頁(yè)緩存
大型互聯(lián)網(wǎng)公司都會(huì)使用Redis作為緩存存儲(chǔ)數(shù)據(jù),提升頁(yè)面相應(yīng)速度。即使重啟了Redis實(shí)例,因?yàn)橛写疟P的持久化,用戶也不會(huì)看到頁(yè)面加載速度的下降。
4.排行榜/計(jì)數(shù)器
Redis在內(nèi)存中對(duì)數(shù)字進(jìn)行遞增或遞減的操作實(shí)現(xiàn)的非常好。集合(Set)和有序集合(Sorted Set)也使得我們?cè)趫?zhí)行這些操作的時(shí)候變的非常簡(jiǎn)單。
四.Redis高可用架構(gòu)
1.持久化
Redis 是內(nèi)存型數(shù)據(jù)庫(kù),為了保證數(shù)據(jù)在斷電后不會(huì)丟失,需要將內(nèi)存中的數(shù)據(jù)持久化到硬盤上。Redis提供了兩種持久化的方式,分別是RDB(Redis DataBase)和AOF(Append Only File)。
RDB
簡(jiǎn)而言之,就是在不同的時(shí)間點(diǎn),將redis存儲(chǔ)的數(shù)據(jù)生成快照并存儲(chǔ)到磁盤等介質(zhì)上,可以將快照復(fù)制到其他服務(wù)器從而創(chuàng)建具有相同數(shù)據(jù)的服務(wù)器副本。如果系統(tǒng)發(fā)生故障,將會(huì)丟失最后一次創(chuàng)建快照之后的數(shù)據(jù)。如果數(shù)據(jù)量大,保存快照的時(shí)間會(huì)很長(zhǎng)。
AOF
換了一個(gè)角度來實(shí)現(xiàn)持久化,那就是將redis執(zhí)行過的所有寫指令記錄下來,在下次redis重新啟動(dòng)時(shí),只要把這些寫指令從前到后再重復(fù)執(zhí)行一遍,就可以實(shí)現(xiàn)數(shù)據(jù)恢復(fù)了。將寫命令添加到 AOF 文件(append only file)末尾。
使用 AOF 持久化需要設(shè)置同步選項(xiàng),從而確保寫命令同步到磁盤文件上的時(shí)機(jī)。這是因?yàn)閷?duì)文件進(jìn)行寫入并不會(huì)馬上將內(nèi)容同步到磁盤上,而是先存儲(chǔ)到緩沖區(qū),然后由操作系統(tǒng)決定什么時(shí)候同步到磁盤。選項(xiàng)同步頻率always每個(gè)寫命令都同步,eyerysec每秒同步一次,no讓操作系統(tǒng)來決定何時(shí)同步,always 選項(xiàng)會(huì)嚴(yán)重減低服務(wù)器的性能,everysec 選項(xiàng)比較合適,可以保證系統(tǒng)崩潰時(shí)只會(huì)丟失一秒左右的數(shù)據(jù),并且 Redis 每秒執(zhí)行一次同步對(duì)服務(wù)器幾乎沒有任何影響。no 選項(xiàng)并不能給服務(wù)器性能帶來多大的提升,而且會(huì)增加系統(tǒng)崩潰時(shí)數(shù)據(jù)丟失的數(shù)量。隨著服務(wù)器寫請(qǐng)求的增多,AOF 文件會(huì)越來越大。Redis 提供了一種將 AOF 重寫的特性,能夠去除 AOF 文件中的冗余寫命令。
其實(shí)RDB和AOF兩種方式也可以同時(shí)使用,在這種情況下,如果redis重啟的話,則會(huì)優(yōu)先采用AOF方式來進(jìn)行數(shù)據(jù)恢復(fù),這是因?yàn)锳OF方式的數(shù)據(jù)恢復(fù)完整度更高。如果你沒有數(shù)據(jù)持久化的需求,也完全可以關(guān)閉RDB和AOF方式,這樣的話,redis將變成一個(gè)純內(nèi)存數(shù)據(jù)庫(kù)。
2.復(fù)制
Redis為了解決單點(diǎn)數(shù)據(jù)庫(kù)問題,會(huì)把數(shù)據(jù)復(fù)制多個(gè)副本部署到其他節(jié)點(diǎn)上,通過復(fù)制,實(shí)現(xiàn)Redis的高可用性,實(shí)現(xiàn)對(duì)數(shù)據(jù)的冗余備份,保證數(shù)據(jù)和服務(wù)的高度可靠性。Redis有主從和主備兩種方式解決單點(diǎn)問題,主備(keepalived)模式下主機(jī)備機(jī)對(duì)外提供同一個(gè)虛擬IP,客戶端通過虛擬IP進(jìn)行數(shù)據(jù)操作,正常期間主機(jī)一直對(duì)外提供服務(wù),宕機(jī)后VIP自動(dòng)漂移到備機(jī)上。 主從模式下當(dāng)Master宕機(jī)后,通過選舉算法(Paxos、Raft)從slave中選舉出新Master繼續(xù)對(duì)外提供服務(wù),主機(jī)恢復(fù)后以slave的身份重新加入,此模式下可以使用讀寫分離,如果數(shù)據(jù)量比較大,不希望過多浪費(fèi)機(jī)器,還希望在宕機(jī)后,做一些自定義的措施,比如報(bào)警、記日志、數(shù)據(jù)遷移等操作,推薦使用主從方式,因?yàn)楹椭鲝拇钆涞囊话氵€有個(gè)管理監(jiān)控中心(哨兵)。
①?gòu)臄?shù)據(jù)庫(kù)向主數(shù)據(jù)庫(kù)發(fā)送sync(數(shù)據(jù)同步)命令。
②主數(shù)據(jù)庫(kù)接收同步命令后,會(huì)保存快照,創(chuàng)建一個(gè)RDB文件。
③當(dāng)主數(shù)據(jù)庫(kù)執(zhí)行完保持快照后,會(huì)向從數(shù)據(jù)庫(kù)發(fā)送RDB文件,而從數(shù)據(jù)庫(kù)會(huì)接收并載入該文件。
④主數(shù)據(jù)庫(kù)將緩沖區(qū)的所有寫命令發(fā)給從服務(wù)器執(zhí)行。
⑤以上處理完之后,之后主數(shù)據(jù)庫(kù)每執(zhí)行一個(gè)寫命令,都會(huì)將被執(zhí)行的寫命令發(fā)送給從數(shù)據(jù)庫(kù)。可以同步發(fā)送也可以異步發(fā)送,同步發(fā)送可以不用每臺(tái)都同步,可以配置一臺(tái)master,一臺(tái)slave,同時(shí)這臺(tái)salve又作為其他slave的master。異步方式無法保證數(shù)據(jù)的完整性,比如在異步同步過程中主機(jī)突然宕機(jī)了,也稱這種方式為數(shù)據(jù)弱一致性。
注意:在Redis2.8之后,主從斷開重連后會(huì)根據(jù)斷開之前最新的命令偏移量進(jìn)行增量復(fù)制。
3.哨兵
哨兵是Redis集群架構(gòu)中非常重要的一個(gè)組件,哨兵的出現(xiàn)主要是解決了主從復(fù)制出現(xiàn)故障時(shí)需要人為干預(yù)的問題。
1.Redis哨兵主要功能
(1)集群監(jiān)控:負(fù)責(zé)監(jiān)控Redis master和slave進(jìn)程是否正常工作
(2)消息通知:如果某個(gè)Redis實(shí)例有故障,那么哨兵負(fù)責(zé)發(fā)送消息作為報(bào)警通知給管理員
(3)故障轉(zhuǎn)移:如果master node掛掉了,會(huì)自動(dòng)轉(zhuǎn)移到slave node上
(4)配置中心:如果故障轉(zhuǎn)移發(fā)生了,通知client客戶端新的master地址
2.Redis哨兵的高可用
原理:當(dāng)主節(jié)點(diǎn)出現(xiàn)故障時(shí),由Redis Sentinel自動(dòng)完成故障發(fā)現(xiàn)和轉(zhuǎn)移,并通知應(yīng)用方,實(shí)現(xiàn)高可用性。哨兵機(jī)制建立了多個(gè)哨兵節(jié)點(diǎn)(進(jìn)程),共同監(jiān)控?cái)?shù)據(jù)節(jié)點(diǎn)的運(yùn)行狀況。同時(shí)哨兵節(jié)點(diǎn)之間也互相通信,交換對(duì)主從節(jié)點(diǎn)的監(jiān)控狀況。每隔1秒每個(gè)哨兵會(huì)向整個(gè)集群:Master主服務(wù)器+Slave從服務(wù)器+其他Sentinel(哨兵)進(jìn)程,發(fā)送一次ping命令做一次心跳檢測(cè)。這個(gè)就是哨兵用來判斷節(jié)點(diǎn)是否正常的重要依據(jù),涉及兩個(gè)新的概念:主觀下線和客觀下線。一個(gè)哨兵節(jié)點(diǎn)判定主節(jié)點(diǎn)down掉是主觀下線,只有半數(shù)哨兵節(jié)點(diǎn)都主觀判定主節(jié)點(diǎn)down掉,此時(shí)多個(gè)哨兵節(jié)點(diǎn)交換主觀判定結(jié)果,才會(huì)判定主節(jié)點(diǎn)客觀下線?;旧夏膫€(gè)哨兵節(jié)點(diǎn)最先判斷出這個(gè)主節(jié)點(diǎn)客觀下線,就會(huì)在各個(gè)哨兵節(jié)點(diǎn)中發(fā)起投票機(jī)制Raft算法(選舉算法),最終被投為領(lǐng)導(dǎo)者的哨兵節(jié)點(diǎn)完成主從自動(dòng)化切換的過程。
4.集群
至少部署兩臺(tái)Redis服務(wù)器構(gòu)成一個(gè)小的集群,主要有2個(gè)目的:
高可用性:在主機(jī)掛掉后,自動(dòng)故障轉(zhuǎn)移,使前端服務(wù)對(duì)用戶無影響。
讀寫分離:將主機(jī)讀壓力分流到從機(jī)上。
可在客戶端組件上實(shí)現(xiàn)負(fù)載均衡,根據(jù)不同服務(wù)器的運(yùn)行情況,分擔(dān)不同比例的讀請(qǐng)求壓力。

緩存數(shù)據(jù)量不斷增加時(shí),單機(jī)內(nèi)存不夠使用,需要把數(shù)據(jù)切分不同部分,分布到多臺(tái)服務(wù)器上。 可在客戶端對(duì)數(shù)據(jù)進(jìn)行分片,數(shù)據(jù)分片算法詳見一致性Hash詳解、虛擬桶分片。
當(dāng)數(shù)據(jù)量持續(xù)增加時(shí),應(yīng)用可根據(jù)不同場(chǎng)景下的業(yè)務(wù)申請(qǐng)對(duì)應(yīng)的分布式集群。 這塊最關(guān)鍵的是緩存治理這塊,其中最重要的部分是加入了代理服務(wù)(Codis和Twemproxy)。 應(yīng)用通過代理訪問真實(shí)的Redis服務(wù)器進(jìn)行讀寫,這樣做的好處是避免越來越多的客戶端直接訪問Redis服務(wù)器難以管理,而造成風(fēng)險(xiǎn),在代理這一層可以做對(duì)應(yīng)的安全措施,比如限流、授權(quán)、分片,避免客戶端越來越多的邏輯代碼,不但臃腫升級(jí)還比較麻煩。代理這層無狀態(tài)的,可任意擴(kuò)展節(jié)點(diǎn),對(duì)于客戶端來說,訪問代理跟訪問單機(jī)Redis一樣。

Redis Cluster是Redis官網(wǎng)給出的集群架構(gòu)

客戶端與Redis節(jié)點(diǎn)直連,不需要中間Proxy層,直接連接任意一個(gè)Master節(jié)點(diǎn),根據(jù)公式HASH_SLOT=CRC16(key) mod 16384,計(jì)算出映射到哪個(gè)分片上,然后Redis會(huì)去相應(yīng)的節(jié)點(diǎn)進(jìn)行操作
具有如下優(yōu)點(diǎn):
(1)無需Sentinel哨兵監(jiān)控,如果Master掛了,Redis Cluster內(nèi)部自動(dòng)將Slave切換Master
(2)可以進(jìn)行水平擴(kuò)容
(3)支持自動(dòng)化遷移,當(dāng)出現(xiàn)某個(gè)Slave宕機(jī)了,那么就只有Master了,這時(shí)候的高可用性就無法很好的保證了,萬一Master也宕機(jī)了,咋辦呢? 針對(duì)這種情況,如果說其他Master有多余的Slave ,集群自動(dòng)把多余的Slave遷移到?jīng)]有Slave的Master 中。
缺點(diǎn):
(1)批量操作是個(gè)坑,不同的key會(huì)劃分到不同的slot中,因此直接使用mset或者mget等操作是行不通的。如果執(zhí)行的key數(shù)量比較少,就不用mget了,就用串行g(shù)et操作。如果真的需要執(zhí)行的key很多,就使用Hashtag保證這些key映射到同一臺(tái)Redis節(jié)點(diǎn)上。
(2)資源隔離性較差,容易出現(xiàn)相互影響的情況。
五.Redis高并發(fā)及熱key解決之道
1.并發(fā)設(shè)置key及分布式鎖
Redis是一種單線程機(jī)制的nosql數(shù)據(jù)庫(kù),基于key-value,數(shù)據(jù)可持久化落盤。由于單線程所以Redis本身并沒有鎖的概念,多個(gè)客戶端連接并不存在競(jìng)爭(zhēng)關(guān)系,但是利用jedis等客戶端對(duì)Redis進(jìn)行并發(fā)訪問時(shí)會(huì)出現(xiàn)問題。比如多客戶端同時(shí)并發(fā)寫一個(gè)key,一個(gè)key的值是1,本來按順序修改為2,3,4,最后是4,但是順序變成了4,3,2,最后變成了2。使用分布式鎖防止并發(fā)設(shè)置Key的原理及代碼見:使用Redis實(shí)現(xiàn)分布式鎖及其優(yōu)化,另外一種方式是使用消息隊(duì)列,把并行讀寫進(jìn)行串行化。
2.熱key問題
熱key問題說來也很簡(jiǎn)單,就是瞬間有幾十萬的請(qǐng)求去訪問redis上某個(gè)固定的key,從而壓垮緩存服務(wù)的情情況。其實(shí)生活中也是有不少這樣的例子。比如XX明星結(jié)婚。那么關(guān)于XX明星的Key就會(huì)瞬間增大,就會(huì)出現(xiàn)熱數(shù)據(jù)問題。那么如何發(fā)現(xiàn)熱KEY呢:
1.憑借業(yè)務(wù)經(jīng)驗(yàn),進(jìn)行預(yù)估哪些是熱key
2.在客戶端進(jìn)行收集
3.在Proxy層做收集
4.用redis自帶命令(monitor命令、hotkeys參數(shù))
5.自己抓包評(píng)估
解決方案:
1.利用二級(jí)緩存,比如利用ehcache,或者一個(gè)HashMap都可以。在你發(fā)現(xiàn)熱key以后,把熱key加載到系統(tǒng)的JVM中。
2.備份熱key,不要讓key走到同一臺(tái)redis上。我們把這個(gè)key,在多個(gè)redis上都存一份。可以用HOTKEY加上一個(gè)隨機(jī)數(shù)(N,集群分片數(shù))組成一個(gè)新key。
3.熱點(diǎn)數(shù)據(jù)盡量不要設(shè)置過期時(shí)間,在數(shù)據(jù)變更時(shí)同步寫緩存,防止高并發(fā)下重建緩存的資源損耗??梢杂胹etnx做分布式鎖保證只有一個(gè)線程在重建緩存,其他線程等待重建緩存的線程執(zhí)行完,重新從緩存獲取數(shù)據(jù)即可。
3.緩存穿透
緩存穿透是指查詢一個(gè)根本不存在的數(shù)據(jù),緩存層和存儲(chǔ)層都不會(huì)命中,但是出于容錯(cuò)的考慮,如果從存儲(chǔ)層查不到數(shù)據(jù)則不寫入緩存層。緩存穿透將導(dǎo)致不存在的數(shù)據(jù)每次請(qǐng)求都要到存儲(chǔ)層去查詢,失去了緩存保護(hù)后端存儲(chǔ)的意義。造成緩存穿透的基本有兩個(gè)。第一,業(yè)務(wù)自身代碼或者數(shù)據(jù)出現(xiàn)問題,第二,一些惡意攻擊、爬蟲等造成大量空命中,下面我們來看一下如何解決緩存穿透問題。解決緩存穿透的兩種方案:
1)緩存空對(duì)象
緩存空對(duì)象會(huì)有兩個(gè)問題:
第一,空值做了緩存,意味著緩存層中存了更多的鍵,需要更多的內(nèi)存空間 ( 如果是攻擊,問題更嚴(yán)重 ),比較有效的方法是針對(duì)這類數(shù)據(jù)設(shè)置一個(gè)較短的過期時(shí)間,讓其自動(dòng)剔除。
第二,緩存層和存儲(chǔ)層的數(shù)據(jù)會(huì)有一段時(shí)間窗口的不一致,可能會(huì)對(duì)業(yè)務(wù)有一定影響。例如過期時(shí)間設(shè)置為 5 分鐘,如果此時(shí)存儲(chǔ)層添加了這個(gè)數(shù)據(jù),那此段時(shí)間就會(huì)出現(xiàn)緩存層和存儲(chǔ)層數(shù)據(jù)的不一致,此時(shí)可以利用消息系統(tǒng)或者其他方式清除掉緩存層中的空對(duì)象。
2)布隆過濾器攔截
如下圖所示,在訪問緩存層和存儲(chǔ)層之前,將存在的 key 用布隆過濾器提前保存起來,做第一層攔截。如果布隆過濾器認(rèn)為該用戶 ID 不存在,那么就不會(huì)訪問存儲(chǔ)層,在一定程度保護(hù)了存儲(chǔ)層。有關(guān)布隆過濾器的相關(guān)知識(shí),可以參考:?布隆過濾器,可以利用 Redis 的 Bitmaps 實(shí)現(xiàn)布隆過濾器,GitHub 上已經(jīng)開源了類似的方案,讀者可以進(jìn)行參考:redis bitmaps實(shí)現(xiàn)布隆過濾器
緩存空對(duì)象和布隆過濾器方案對(duì)比
4.緩存雪崩
數(shù)據(jù)未加載到緩存中,或者緩存同一時(shí)間大面積的失效,從而導(dǎo)致所有請(qǐng)求都去查數(shù)據(jù)庫(kù),導(dǎo)致數(shù)據(jù)庫(kù)CPU和內(nèi)存負(fù)載過高,甚至宕機(jī)。
可以從以下幾個(gè)方面防止緩存雪崩:
1)保證緩存層服務(wù)高可用性
和飛機(jī)都有多個(gè)引擎一樣,如果緩存層設(shè)計(jì)成高可用的,即使個(gè)別節(jié)點(diǎn)、個(gè)別機(jī)器、甚至是機(jī)房宕掉,依然可以提供服務(wù),例如前面介紹過的 Redis Sentinel 和 Redis Cluster 都實(shí)現(xiàn)了高可用。
2)Redis備份和快速預(yù)熱
Redis備份保證master出問題切換為slave迅速能夠承擔(dān)線上實(shí)際流量,快速預(yù)熱保證緩存及時(shí)被寫入緩存,防止穿透到庫(kù)。
3)依賴隔離組件為后端限流并降級(jí)
無論是緩存層還是存儲(chǔ)層都會(huì)有出錯(cuò)的概率,可以將它們視同為資源。作為并發(fā)量較大的系統(tǒng),假如有一個(gè)資源不可用,可能會(huì)造成線程全部 hang 在這個(gè)資源上,造成整個(gè)系統(tǒng)不可用。降級(jí)在高并發(fā)系統(tǒng)中是非常正常的:比如推薦服務(wù)中,如果個(gè)性化推薦服務(wù)不可用,可以降級(jí)補(bǔ)充熱點(diǎn)數(shù)據(jù),不至于造成前端頁(yè)面是開天窗。
在實(shí)際項(xiàng)目中,我們需要對(duì)重要的資源 ( 例如 Redis、 MySQL、 Hbase、外部接口 ) 都進(jìn)行隔離,讓每種資源都單獨(dú)運(yùn)行在自己的線程池中,即使個(gè)別資源出現(xiàn)了問題,對(duì)其他服務(wù)沒有影響。但是線程池如何管理,比如如何關(guān)閉資源池,開啟資源池,資源池閥值管理,這些做起來還是相當(dāng)復(fù)雜的,這里推薦一個(gè) Java 依賴隔離工具Hystrix(https://github.com/Netflix/Hystrix),如下圖所示。
4)提前演練
在項(xiàng)目上線前,演練緩存層宕掉后,應(yīng)用以及后端的負(fù)載情況以及可能出現(xiàn)的問題,在此基礎(chǔ)上做一些預(yù)案設(shè)定。
5.緩存預(yù)熱
緩存預(yù)熱就是系統(tǒng)上線前,將相關(guān)的緩存數(shù)據(jù)直接加載到緩存系統(tǒng)。這樣就可以避免上線后在用戶請(qǐng)求的時(shí)候,先查詢數(shù)據(jù)庫(kù),然后再將數(shù)據(jù)緩存的問題!用戶直接查詢事先被預(yù)熱的緩存數(shù)據(jù)!
六.Redis內(nèi)存模型
Redis為什么這么快?一文深入了解Redis內(nèi)存模型!
參考文章:
Redis緩存的設(shè)計(jì)、性能、應(yīng)用與數(shù)據(jù)集群同步