太牛了,值得收藏!7000字22张图,精讲 Redis 知识!
今天我們來(lái)分享 Redis 相關(guān)的知識(shí),文章有點(diǎn)長(zhǎng),一定要看到最后呦!
前言
Redis 在當(dāng)今的計(jì)算機(jī)行業(yè),可以說(shuō)是使用的最為廣泛的內(nèi)存數(shù)據(jù)庫(kù),幾乎所有的后端技術(shù)面試都會(huì)涉及到 Redis 相關(guān)的知識(shí),正所謂知己知彼,百戰(zhàn)百勝。今天咱們就來(lái)盤(pán)一盤(pán) Redis,從基礎(chǔ)面試題到各種特性、功能,做一次一網(wǎng)打盡式的服務(wù)!
什么是 Redis
Redis 是用 C 語(yǔ)言開(kāi)發(fā)的一個(gè)開(kāi)源的高性能鍵值對(duì)(key-value)數(shù)據(jù)庫(kù)。通常建議在 Linux 上運(yùn)行,它通過(guò)提供多種鍵值數(shù)據(jù)類型來(lái)適應(yīng)不同場(chǎng)景下的存儲(chǔ)需求,數(shù)據(jù)存儲(chǔ)在內(nèi)存中,也可持久化到磁盤(pán)中,目前為止 Redis 支持的鍵值數(shù)據(jù)類型如下:
- 字符串類型 
- 散列類型 
- 列表類型 
- 集合類型 
- 有序集合類型 
Redis 特色
Redis 是用 C 語(yǔ)言寫(xiě)的開(kāi)源項(xiàng)目,又由于數(shù)據(jù)都在內(nèi)存中,所以讀寫(xiě)速度非常快
Redis 所有數(shù)據(jù)保存在內(nèi)存中,對(duì)數(shù)據(jù)的更新會(huì)異步地保存到磁盤(pán)上,這樣可以做到斷電不丟失數(shù)據(jù)
Redis 主從復(fù)制可以實(shí)現(xiàn)高可用和分布式
Redis 數(shù)據(jù)結(jié)構(gòu)
字符串
String 是 Redis 當(dāng)中最為基本的數(shù)據(jù)類型,最大512M,是二進(jìn)制安全的
Redis 的字符串是動(dòng)態(tài)字符串,是可以修改的,內(nèi)部結(jié)構(gòu)實(shí)現(xiàn)上類似于 Java 的 ArrayList,采用預(yù)分配冗余空間的方式來(lái)減少內(nèi)存的頻繁分配,內(nèi)存為當(dāng)前字符串實(shí)際分配的空間,一般要高于實(shí)際字符串長(zhǎng)度。當(dāng)字符串長(zhǎng)度小于 1M 時(shí),擴(kuò)容都是加倍現(xiàn)有的空間,如果超過(guò) 1M,擴(kuò)容時(shí)一次只會(huì)多擴(kuò) 1M 的空間
使用場(chǎng)景:記錄用戶頁(yè)面訪問(wèn)量、緩存基本數(shù)據(jù)、分布式 Id 生成器
散列
Redis 當(dāng)中的 Hash 相當(dāng)于 Java 的 HashMap,無(wú)序字典,內(nèi)部實(shí)現(xiàn)是用數(shù)組+鏈表,第一維的數(shù)組出現(xiàn)碰撞,則存到鏈表里面去。Rehash 的時(shí)候,為了不阻塞服務(wù),采用的是漸進(jìn)式的 Rehash,保留2個(gè) Hash,逐漸在指令執(zhí)行或者定時(shí)任務(wù)中,將數(shù)據(jù)從老的 Hash 遷移到新的 Hash
使用場(chǎng)景:購(gòu)物車、頻繁變化的屬性
列表
Redis 的列表按照插入順序排序,相當(dāng)于 Java 語(yǔ)言里面的 LinkedList,注意它是鏈表而不是數(shù)組。這意味著 list 的插入和刪除操作非常快,時(shí)間復(fù)雜度為 O(1),但是索引定位很慢,時(shí)間復(fù)雜度為O(n)。當(dāng)列表彈出了最后一個(gè)元素之后,該數(shù)據(jù)結(jié)構(gòu)自動(dòng)被刪除,內(nèi)存被回收
Redis 的列表結(jié)構(gòu)常用來(lái)做異步隊(duì)列使用,將需要延后處理的任務(wù)結(jié)構(gòu)體序列化成字符串塞進(jìn) Redis 的列表,另一個(gè)線程從這個(gè)列表中輪詢數(shù)據(jù)進(jìn)行處理
列表不同進(jìn)出棧的方式,產(chǎn)生的不同數(shù)據(jù)結(jié)構(gòu)
- LPUSH+LPOP = 棧 
- LPUSH+RPOP = 隊(duì)列 
- LPUSH+ LTRIM = 固定數(shù)量的列表 
- LPUSH +BRPOP = 消息隊(duì)列 
使用場(chǎng)景:異步隊(duì)列
無(wú)序集合
set 相當(dāng)于 Java 的 HashSet,無(wú)序的鍵值對(duì),不過(guò)其值都是NULL,鍵不可以重復(fù)。數(shù)據(jù)量較少且是整數(shù)的時(shí)候用有序數(shù)組,較大的時(shí)候采用散列表
應(yīng)用場(chǎng)景:標(biāo)簽、社交、隨機(jī)數(shù)
有序集合
zset 可能是 Redis 提供的最為特色的數(shù)據(jù)結(jié)構(gòu),它類似于 Java 的 SortedSet 和 HashMap 的結(jié)合體,一方面它是一個(gè) set,保證了內(nèi)部 value 的唯一性,另一方面它可以給每個(gè) value 賦予一個(gè) score,代表這個(gè) value 的排序權(quán)重。
它的內(nèi)部實(shí)現(xiàn)用的是一種叫「跳躍列表」的數(shù)據(jù)結(jié)構(gòu),之所以「跳躍」,是因?yàn)閮?nèi)部的元素可能「身兼數(shù)職」
一個(gè)元素,同時(shí)處于 L0、L1 和 L2 層,可以快速在不同層次之間進(jìn)行「跳躍」,定位插入點(diǎn)時(shí),先在頂層進(jìn)行定位,然后下潛到下一級(jí)定位,一直下潛到最底層找到合適的位置,將新元素插進(jìn)去
應(yīng)用場(chǎng)景:value 為粉絲 ID,score 是關(guān)注時(shí)間,可以按照關(guān)注時(shí)間順序給出粉絲 ID;value 是學(xué)生 ID,score 是其分?jǐn)?shù),可以按照分?jǐn)?shù)排序
Redis 持久化
Redis 提供了兩種持久化的方式,分別是RDB(Redis DataBase)和AOF(Append Only File)
RDB,簡(jiǎn)而言之,就是在不同的時(shí)間點(diǎn),將 Redis 存儲(chǔ)的數(shù)據(jù)生成快照并存儲(chǔ)到磁盤(pán)等介質(zhì)上
AOF,則是換了一個(gè)角度來(lái)實(shí)現(xiàn)持久化,那就是將 Redis 執(zhí)行過(guò)的所有寫(xiě)指令記錄下來(lái),在下次 Redis 重新啟動(dòng)時(shí),只要把這些寫(xiě)指令從前到后再重復(fù)執(zhí)行一遍,就可以實(shí)現(xiàn)數(shù)據(jù)恢復(fù)了
其實(shí) RDB 和 AOF 兩種方式也可以同時(shí)使用,在這種情況下,如果 Redis 重啟的話,則會(huì)優(yōu)先采用 AOF 方式來(lái)進(jìn)行數(shù)據(jù)恢復(fù),這是因?yàn)?AOF 方式的數(shù)據(jù)恢復(fù)完整度更高
AOF 有一個(gè)配置屬性 sync,就是用來(lái)同步命令到磁盤(pán)的,如果我們對(duì)于 Redis 的性能要求不高,則可以在每條寫(xiě)指令時(shí)都 sync 一下磁盤(pán),這樣即使在突然斷電的情況下,也能保證數(shù)據(jù)的最小丟失率
RDB
RDB 是將 Redis 某一時(shí)刻的數(shù)據(jù)持久化到磁盤(pán)中,是一種快照式的持久化方法
Redis 在進(jìn)行數(shù)據(jù)持久化的過(guò)程中,會(huì)先將數(shù)據(jù)寫(xiě)入到一個(gè)臨時(shí)文件中,待持久化過(guò)程都結(jié)束了,才會(huì)用這個(gè)臨時(shí)文件替換上次持久化好的文件。正是這種特性,讓我們可以隨時(shí)來(lái)進(jìn)行備份,因?yàn)榭煺瘴募偸峭暾捎玫?/p>
對(duì)于 RDB 方式,Redis 會(huì)單獨(dú)創(chuàng)建(fork)一個(gè)子進(jìn)程來(lái)進(jìn)行持久化,而主進(jìn)程是不會(huì)進(jìn)行任何 IO 操作的,這樣就確保了 Redis 極高的性能,子進(jìn)程創(chuàng)建后,父子進(jìn)程共享數(shù)據(jù)段,父進(jìn)程繼續(xù)提供讀寫(xiě)服務(wù),寫(xiě)臟的頁(yè)面數(shù)據(jù)會(huì)逐漸和子進(jìn)程分離開(kāi)來(lái)
如果需要進(jìn)行大規(guī)模數(shù)據(jù)的恢復(fù),且對(duì)于數(shù)據(jù)恢復(fù)的完整性不是非常敏感,那 RDB 方式要比 AOF 方式更加的高效
AOF
AOF,英文是 Append Only File,即只允許追加不允許改寫(xiě)的文件
如前面介紹的,AOF 方式是將執(zhí)行過(guò)的寫(xiě)指令記錄下來(lái),在數(shù)據(jù)恢復(fù)時(shí)按照從前到后的順序再將指令都執(zhí)行一遍,就這么簡(jiǎn)單
默認(rèn)的 AOF 持久化策略是每秒鐘 fsync 一次(fsync是指把緩存中的寫(xiě)指令記錄到磁盤(pán)中),因?yàn)樵谶@種情況下,Redis 仍然可以保持很好的處理性能,即使 Redis 故障,也只會(huì)丟失最近1秒鐘的數(shù)據(jù)
RDB 與 AOF 比較
Redis 主從
Redis 是支持主從同步的,而且也支持一主多從以及多級(jí)從結(jié)構(gòu)
主從結(jié)構(gòu),一是為了純粹的冗余備份,二是為了提升讀性能,比如很消耗性能的 sort 就可以由從服務(wù)器來(lái)承擔(dān)
Redis 的主從同步是異步進(jìn)行的,這意味著主從同步不會(huì)影響主邏輯,也不會(huì)降低 Redis 的處理性能
主從架構(gòu)中,可以考慮關(guān)閉主服務(wù)器的數(shù)據(jù)持久化功能,只讓從服務(wù)器進(jìn)行持久化,這樣可以提高主服務(wù)器的處理性能
在主從架構(gòu)中,從服務(wù)器通常被設(shè)置為只讀模式,這樣可以避免從服務(wù)器的數(shù)據(jù)被誤修改。但是從服務(wù)器仍然可以接受 CONFIG 等指令,所以還是不應(yīng)該將從服務(wù)器直接暴露到不安全的網(wǎng)絡(luò)環(huán)境中
舊版復(fù)制功能
Redis 的復(fù)制功能分為同步(sync)和命令傳播(command propagate)兩個(gè)操作:
- 同步操作用于將從服務(wù)器的數(shù)據(jù)庫(kù)狀態(tài)更新至主服務(wù)器當(dāng)前所處的數(shù)據(jù)庫(kù)狀態(tài) 
- 命令傳播操作則用于在主服務(wù)器的數(shù)據(jù)庫(kù)狀態(tài)被修改,導(dǎo)致主從服務(wù)器的數(shù)據(jù)庫(kù)狀態(tài)出現(xiàn)不一致時(shí),讓主從服務(wù)器的數(shù)據(jù)庫(kù)重新回到一致?tīng)顟B(tài) 
同步過(guò)程
當(dāng)客戶端向從服務(wù)器發(fā)送 SLAVEOF 命令,要求從服務(wù)器復(fù)制主服務(wù)器時(shí),從服務(wù)器首先需要執(zhí)行同步操作,也就是將從服務(wù)器的數(shù)據(jù)庫(kù)狀態(tài)更新至主服務(wù)器當(dāng)前所處的數(shù)據(jù)庫(kù)狀態(tài)
從服務(wù)器對(duì)主服務(wù)器的同步操作需要通過(guò)向主服務(wù)器發(fā)送 SYNC 命令來(lái)完成,以下是 SYNC 命令的執(zhí)行步驟:
- 1)從服務(wù)器向主服務(wù)器發(fā)送 SYNC 命令 
- 2)收到 SYNC 命令的主服務(wù)器執(zhí)行 BGSAVE 命令,在后臺(tái)生成一個(gè) RDB 文件,并使用一個(gè)緩沖區(qū)記錄從現(xiàn)在開(kāi)始執(zhí)行的所有寫(xiě)命令 
- 3)當(dāng)主服務(wù)器的 BGSAVE 命令執(zhí)行完畢時(shí),主服務(wù)器會(huì)將 BGSAVE 命令生成的 RDB 文件發(fā)送給從服務(wù)器,從服務(wù)器接收并載入這個(gè) RDB 文件,將自己的數(shù)據(jù)庫(kù)狀態(tài)更新至主服務(wù)器執(zhí)行 BGSAVE 命令時(shí)的數(shù)據(jù)庫(kù)狀態(tài) 
- 4)主服務(wù)器將記錄在緩沖區(qū)里面的所有寫(xiě)命令發(fā)送給從服務(wù)器,從服務(wù)器執(zhí)行這些寫(xiě)命令,將自己的數(shù)據(jù)庫(kù)狀態(tài)更新至主服務(wù)器數(shù)據(jù)庫(kù)當(dāng)前所處的狀態(tài) 
缺點(diǎn)
這種復(fù)制功能,雖然可以很好的完成主備之間的數(shù)據(jù)同步,但是效率確實(shí)非常低的
每次執(zhí)行 SYNC 命令,主從服務(wù)器需要執(zhí)行以下動(dòng)作:
- 1)主服務(wù)器需要執(zhí)行 BGSAVE 命令來(lái)生成 RDB 文件,這個(gè)生成操作會(huì)耗費(fèi)主服務(wù)器大量的 CPU、內(nèi)存和磁盤(pán) I/O 資源 
- 2)主服務(wù)器需要將自己生成的 RDB 文件發(fā)送給從服務(wù)器,這個(gè)發(fā)送操作會(huì)耗費(fèi)主從服務(wù)器大量的網(wǎng)絡(luò)資源(帶寬和流量),并對(duì)主服務(wù)器響應(yīng)命令請(qǐng)求的時(shí)間產(chǎn)生影響 
- 3)接收到 RDB 文件的從服務(wù)器需要載入主服務(wù)器發(fā)來(lái)的 RDB文件,并且在載入期間,從服務(wù)器會(huì)因?yàn)樽枞鴽](méi)辦法處理命令請(qǐng)求 
- 4)BGSAVE 命令產(chǎn)生的 RDB 文件是主服務(wù)器鎖包含的所有數(shù)據(jù),這是一個(gè)全量過(guò)程 
因?yàn)?SYNC 命令是一個(gè)如此耗費(fèi)資源的操作,所以 Redis 有必要 保證在真正有需要時(shí)才執(zhí)行 SYNC 命令
新版復(fù)制功能
為了解決舊版本復(fù)制功能的低效問(wèn)題,Redis 從2.8版本開(kāi)始,使用 PSYNC 命令代替 SYNC 命令來(lái)執(zhí)行復(fù)制時(shí)的同步操作
PSYNC 命令具有完整重同步(full resynchronization)和部分重同步(partial resynchronization)兩種模式:
- 完整重同步用于處理初次復(fù)制情況:完整重同步的執(zhí)行步驟SYN命令的執(zhí)行步驟基本一樣,它們都是通過(guò)讓主服務(wù)器創(chuàng)建并發(fā)RD文件,以及向從服務(wù)器發(fā)送保存在緩沖區(qū)里面的寫(xiě)命令來(lái)進(jìn)行同步 
- 部分重同步則用于處理斷線后重復(fù)制情況:當(dāng)從服務(wù)器在斷線后重新連接主服務(wù)器時(shí),如果條件允許,主服務(wù)器可以將主從服務(wù)器連接斷開(kāi)期間執(zhí)行的寫(xiě)命令發(fā)送給從服務(wù)器,從服務(wù)器只要接收并執(zhí)行這些寫(xiě)命令,就可以將數(shù)據(jù)庫(kù)更新至主服務(wù)器當(dāng)前所處的狀態(tài) 
PSYNC 命令的部分重同步模式解決了舊版復(fù)制功能在處理斷線后 重復(fù)制時(shí)出現(xiàn)的低效情況,即采用增量同步的形式,大大節(jié)省了服務(wù)器資源
哨兵與集群
Redis Sentinal 主要用于高可用,在 master 宕機(jī)時(shí)會(huì)自動(dòng)將 slave 提升為 master,繼續(xù)提供服務(wù)
Redis Cluster 則側(cè)重于擴(kuò)展性,在單個(gè) Redis 內(nèi)存不足時(shí),使用 Cluster 進(jìn)行分片存儲(chǔ)
Redis Sentinal
Sentinel(哨崗、哨兵)是 Redis 的高可用性(high availability)解決方案:由一個(gè)或多個(gè) Sentinel 實(shí)例組成的 Sentinel 系統(tǒng)可以監(jiān)視任意多個(gè)主服務(wù)器,以及這些主服務(wù)器屬下的所有從服務(wù)器,并在被監(jiān)視的主服務(wù)器進(jìn)入下線狀態(tài)時(shí),自動(dòng)將下線主服務(wù)器屬下的某個(gè)從服務(wù)器升級(jí)為新的主服務(wù)器,然后由新的主服務(wù)器代替已下線的主服務(wù)器繼續(xù)處理命令請(qǐng)求
初始狀態(tài)下,Server1 為主服務(wù)器,其余為從服務(wù)器
假設(shè)這時(shí),主服務(wù)器 Server1 進(jìn)入下線狀態(tài),那么從服務(wù)器 Server2、Server3、Server4 對(duì)主服務(wù)器的復(fù)制操作將被中止,并且 Sentinel 系統(tǒng)會(huì)察覺(jué)到 Server1 已下線
當(dāng) Server1 的下線時(shí)長(zhǎng)超過(guò)用戶設(shè)定的下線時(shí)長(zhǎng)上限時(shí),Sentinel 系統(tǒng)就會(huì)對(duì) Server1 執(zhí)行故障轉(zhuǎn)移操作:
- 首先,Sentinel 系統(tǒng)會(huì)挑選 Server1 屬下的其中一個(gè)從服務(wù)器,并將這個(gè)被選中的從服務(wù)器升級(jí)為新的主服務(wù)器 
- 之后,Sentinel 系統(tǒng)會(huì)向 Server1 屬下的所有從服務(wù)器發(fā)送新的復(fù)制指令,讓它們成為新的主服務(wù)器的從服務(wù)器,當(dāng)所有從服務(wù)器都開(kāi)始復(fù)制新的主服務(wù)器時(shí),故障轉(zhuǎn)移操作執(zhí)行完畢 
- 另外,Sentinel 還會(huì)繼續(xù)監(jiān)視已下線的 Server1,并在它重新上線時(shí),將它設(shè)置為新的主服務(wù)器的從服務(wù)器 
選取新主
老主恢復(fù),降為從服務(wù)器
Redis Cluster
Redis 集群是 Redis 提供的分布式數(shù)據(jù)庫(kù)方案,集群通過(guò)分片(sharding)來(lái)進(jìn)行數(shù)據(jù)共享,并提供復(fù)制和故障轉(zhuǎn)移功能
Redis Cluser 采用虛擬槽分區(qū),所有的鍵根據(jù)哈希函數(shù)映射到0~16383整數(shù)槽內(nèi),計(jì)算公式:slot=CRC16(key)&16383
每一個(gè)節(jié)點(diǎn)負(fù)責(zé)維護(hù)一部分槽以及槽所映射的鍵值數(shù)據(jù)
Redis 虛擬槽分區(qū)解耦了數(shù)據(jù)與節(jié)點(diǎn)之間的關(guān)系,簡(jiǎn)化了節(jié)點(diǎn)擴(kuò)容和收縮的難度
當(dāng)然 Redis 集群也有很多功能上的限制
- 1)key 批量操作支持有限。如 mset、mget,目前只支持具有相同 slot 值的 key 執(zhí)行批量操作。對(duì)于映射為不同 slot 值的 key 由于執(zhí)行 mget、mget 等操作可能存在于多個(gè)節(jié)點(diǎn)上因此不被支持 
- 2)key 事務(wù)操作支持有限。同理只支持多 key 在同一節(jié)點(diǎn)上的事務(wù)操作,當(dāng)多個(gè) key 分布在不同的節(jié)點(diǎn)上時(shí)無(wú)法使用事務(wù)功能 
- 3)key 作為數(shù)據(jù)分區(qū)的最小粒度,因此不能將一個(gè)大的鍵值對(duì)象如 hash、list 等映射到不同的節(jié)點(diǎn) 
- 4)不支持多數(shù)據(jù)庫(kù)空間。單機(jī)下的 Redis 可以支持16個(gè)數(shù)據(jù)庫(kù),集群模式下只能使用一個(gè)數(shù)據(jù)庫(kù)空間,即db0 
- 5)復(fù)制結(jié)構(gòu)只支持一層,從節(jié)點(diǎn)只能復(fù)制主節(jié)點(diǎn),不支持嵌套樹(shù)狀復(fù)制結(jié)構(gòu) 
更新策略
緩存中的數(shù)據(jù)通常都是有生命周期的,需要在指定時(shí)間后被刪除或更 新,這樣可以保證緩存空間在一個(gè)可控的范圍。但是緩存中的數(shù)據(jù)會(huì)和數(shù)據(jù)源中的真實(shí)數(shù)據(jù)有一段時(shí)間窗口的不一致,需要利用某些策略進(jìn)行更新。
下面介紹 Redis 常用的三種緩存更新策略
LRU/LFU/FIFO 算法剔除
剔除算法通常用于緩存使用量超過(guò)了預(yù)設(shè)的最大值的時(shí)候,如何對(duì)現(xiàn)有的數(shù)據(jù)進(jìn)行剔除,例如使用 maxmemory-policy 這個(gè)配置作為內(nèi)存最大值后對(duì)于數(shù)據(jù)的剔除策略
超時(shí)刪除
超時(shí)剔除通過(guò)給緩存數(shù)據(jù)設(shè)置過(guò)期時(shí)間,讓其在過(guò)期時(shí)間后自動(dòng)刪除,例如 Redis 提供的 expire 命令。如果業(yè)務(wù)可以容忍一段時(shí)間內(nèi),緩存層數(shù)據(jù)和存儲(chǔ)層數(shù)據(jù)不一致,那么可以為其設(shè)置過(guò)期時(shí)間。在數(shù)據(jù)過(guò)期后,再?gòu)恼鎸?shí)數(shù)據(jù)源獲取數(shù)據(jù),重新放到緩存并設(shè)置過(guò)期時(shí)間
主動(dòng)更新
應(yīng)用方對(duì)于數(shù)據(jù)的一致性要求高,需要在真實(shí)數(shù)據(jù)更新后,立即更新緩存數(shù)據(jù)。例如可以利用消息系統(tǒng)或者其他方式通知緩存更新
更新策略對(duì)比
從上面的橫向?qū)Ρ葦?shù)據(jù),我們可以得出如下建議配置
- 低一致性業(yè)務(wù)建議配置最大內(nèi)存和淘汰策略的方式 
- 高一致性業(yè)務(wù)可以結(jié)合使用超時(shí)剔除和主動(dòng)更新,這樣即使主動(dòng)更新出了問(wèn)題,也能保證數(shù)據(jù)過(guò)期時(shí)間后刪除臟數(shù)據(jù) 
Redis 緩存雪崩、擊穿和穿透
這是三個(gè) Redis 最為常見(jiàn)的三個(gè)問(wèn)題,我們逐一來(lái)了解下
1.雪崩
什么是雪崩
由于緩存層承載著大量請(qǐng)求,有效地保護(hù)了存儲(chǔ)層,但是如果緩存層由于某些原因不能提供服務(wù),于是所有的請(qǐng)求都會(huì)達(dá)到存儲(chǔ)層,存儲(chǔ)層的調(diào)用量會(huì)暴增,造成存儲(chǔ)層也會(huì)級(jí)聯(lián)宕機(jī)的情況
預(yù)防和解決辦法
保證緩存層服務(wù)高可用性
出現(xiàn)服務(wù)不可用的情況,我們第一時(shí)間想到的肯定是高可用,甚至是異地容災(zāi)等機(jī)制
依賴隔離組件為后端限流并降級(jí)
無(wú)論是緩存層還是存儲(chǔ)層都會(huì)有出錯(cuò)的概率,可以將它們視同為資源。作為并發(fā)量較大的系統(tǒng),假如有一個(gè)資源不可用,可能會(huì)造成線程全部阻塞(hang)在這個(gè)資源上,造成整個(gè)系統(tǒng)不可用。降級(jí)機(jī)制在高并發(fā)系統(tǒng)中是非常普遍的:比如推薦服務(wù)中,如果個(gè)性化推薦服務(wù)不可用,可以降級(jí)補(bǔ)充熱點(diǎn)數(shù)據(jù),不至于造成前端頁(yè)面開(kāi)天窗
數(shù)據(jù)預(yù)熱
針對(duì)大量緩存同時(shí)過(guò)期的情況,可以通過(guò)緩存 reload 機(jī)制,預(yù)選去更新緩存,在即將發(fā)生大并發(fā)訪問(wèn)前手動(dòng)觸發(fā)加載緩存不同的 key,設(shè)置不同的過(guò)期時(shí)間,讓緩存失效的時(shí)間點(diǎn)盡量均勻
2.擊穿
如果緩存中的某個(gè)熱點(diǎn)數(shù)據(jù)過(guò)期了,此時(shí)大量的請(qǐng)求訪問(wèn)了該熱點(diǎn)數(shù)據(jù),就無(wú)法從緩存中讀取,直接訪問(wèn)數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)很容易就被高并發(fā)的請(qǐng)求沖垮,這就是緩存擊穿的問(wèn)題
擊穿其實(shí)可以看做是雪崩的一個(gè)子集,解決方法一般有兩種,設(shè)置熱點(diǎn)數(shù)據(jù)永不過(guò)期和設(shè)置互斥鎖
所謂的互斥鎖,就是保證同一時(shí)間只有一個(gè)業(yè)務(wù)線程更新緩存,對(duì)于沒(méi)有獲取互斥鎖的請(qǐng)求,要么等待鎖釋放后重新讀取緩存,要么就返回空值或者默認(rèn)值
3.穿透
緩存穿透是指查詢一個(gè)根本不存在的數(shù)據(jù),緩存層和存儲(chǔ)層都不會(huì)命 中,導(dǎo)致請(qǐng)求在訪問(wèn)緩存時(shí),發(fā)現(xiàn)緩存缺失,再去訪問(wèn)數(shù)據(jù)庫(kù)時(shí),發(fā)現(xiàn)數(shù)據(jù)庫(kù)中也沒(méi)有要訪問(wèn)的數(shù)據(jù),沒(méi)辦法構(gòu)建緩存數(shù)據(jù),來(lái)服務(wù)后續(xù)的請(qǐng)求。那么當(dāng)有大量這樣的請(qǐng)求到來(lái)時(shí),數(shù)據(jù)庫(kù)的壓力驟增,產(chǎn)生穿透問(wèn)題
緩存穿透問(wèn)題可能會(huì)使后端存儲(chǔ)負(fù)載加大,由于很多后端存儲(chǔ)不具備高 并發(fā)性,甚至可能造成后端存儲(chǔ)宕掉
解決方案
緩存空對(duì)象
當(dāng)存儲(chǔ)層不命中后,仍然將空對(duì)象保留到緩存層中,之后再訪問(wèn)這個(gè)數(shù)據(jù)將會(huì)從緩存中獲取,這樣就保護(hù)了后端數(shù)據(jù)源
當(dāng)然緩存空對(duì)象會(huì)有兩個(gè)問(wèn)題:
第一,空值做了緩存,意味著緩存層中存了更多的鍵,需要更多的內(nèi)存空間(如果是攻擊,問(wèn)題更嚴(yán)重),比較有效的方法是針對(duì)這類數(shù)據(jù)設(shè)置一個(gè)較短的過(guò)期時(shí)間,讓其自動(dòng)剔除
第二,緩存層和存儲(chǔ)層的數(shù)據(jù)會(huì)有一段時(shí)間窗口的不一致,可能會(huì)對(duì)業(yè)務(wù)有一定影響。例如過(guò)期時(shí)間設(shè)置為5分鐘,如果此時(shí)存儲(chǔ)層添加了這個(gè)數(shù)據(jù),那此段時(shí)間就會(huì)出現(xiàn)緩存層和存儲(chǔ)層數(shù)據(jù)的不一致,此時(shí)可以利用消息系統(tǒng)或者其他方式清除掉緩存層中的空對(duì)象
布隆過(guò)濾器攔截
在訪問(wèn)緩存層和存儲(chǔ)層之前,將存在的 key 用布隆過(guò)濾器提前保存起來(lái),做第一層攔截,即使發(fā)生了緩存穿透,大量請(qǐng)求只會(huì)查詢 Redis 和布隆過(guò)濾器,而不會(huì)查詢數(shù)據(jù)庫(kù),保證了數(shù)據(jù)庫(kù)能正常運(yùn)行
這種方法適用于數(shù)據(jù)命中不高、數(shù)據(jù)相對(duì)固定、實(shí)時(shí)性低(通常是數(shù)據(jù) 集較大)的應(yīng)用場(chǎng)景,代碼維護(hù)較為復(fù)雜,但是緩存空間占用少
緩存空對(duì)象與布隆過(guò)濾器比較
好了,redis的功能還是非常強(qiáng)大的,尤其是在一些秒殺的系統(tǒng)或者是需要高速訪問(wèn)的地方,用mysql會(huì)比較慢,我們上次寫(xiě)了一個(gè)簡(jiǎn)單的小應(yīng)用,里面就用到redis.(女友想買(mǎi)個(gè)手機(jī)!我用Python做了個(gè)比價(jià)機(jī)器人了!)
這就是今天的內(nèi)容,如果你覺(jué)得對(duì)你有所幫助,就點(diǎn)贊+在看支持一下吧~
也歡迎大家關(guān)注我們的視頻號(hào),里面有很多好玩的視頻。
推薦閱讀: 入門(mén):?最全的零基礎(chǔ)學(xué)Python的問(wèn)題? |?零基礎(chǔ)學(xué)了8個(gè)月的Python??|?實(shí)戰(zhàn)項(xiàng)目?|學(xué)Python就是這條捷徑 干貨:爬取豆瓣短評(píng),電影《后來(lái)的我們》?|?38年NBA最佳球員分析?|? ?從萬(wàn)眾期待到口碑撲街!唐探3令人失望? |?笑看新倚天屠龍記?|?燈謎答題王?|用Python做個(gè)海量小姐姐素描圖?|碟中諜這么火,我用機(jī)器學(xué)習(xí)做個(gè)迷你推薦系統(tǒng)電影 趣味:彈球游戲? |?九宮格? |?漂亮的花?|?兩百行Python《天天酷跑》游戲! AI:?會(huì)做詩(shī)的機(jī)器人?|?給圖片上色?|?預(yù)測(cè)收入?|?碟中諜這么火,我用機(jī)器學(xué)習(xí)做個(gè)迷你推薦系統(tǒng)電影 小工具:?Pdf轉(zhuǎn)Word,輕松搞定表格和水印!?|?一鍵把html網(wǎng)頁(yè)保存為pdf!|??再見(jiàn)PDF提取收費(fèi)!?|?用90行代碼打造最強(qiáng)PDF轉(zhuǎn)換器,word、PPT、excel、markdown、html一鍵轉(zhuǎn)換?|?制作一款釘釘?shù)蛢r(jià)機(jī)票提示器!?|60行代碼做了一個(gè)語(yǔ)音壁紙切換器天天看小姐姐!|年度爆款文案
- 1).臥槽!Pdf轉(zhuǎn)Word用Python輕松搞定! 
- 2).學(xué)Python真香!我用100行代碼做了個(gè)網(wǎng)站,幫人PS旅行圖片,賺個(gè)雞腿吃 
- 3).首播過(guò)億,火爆全網(wǎng),我分析了《乘風(fēng)破浪的姐姐》,發(fā)現(xiàn)了這些秘密? 
- 4).80行代碼!用Python做一個(gè)哆來(lái)A夢(mèng)分身? 
- 5).你必須掌握的20個(gè)python代碼,短小精悍,用處無(wú)窮? 
- 6).30個(gè)Python奇淫技巧集? 
- 7).我總結(jié)的80頁(yè)《菜鳥(niǎo)學(xué)Python精選干貨.pdf》,都是干貨? 
- 8).再見(jiàn)Python!我要學(xué)Go了!2500字深度分析! 
- 9).發(fā)現(xiàn)一個(gè)舔狗福利!這個(gè)Python爬蟲(chóng)神器太爽了,自動(dòng)下載妹子圖片 
點(diǎn)閱讀原文,看200個(gè)Python案例!
總結(jié)
以上是生活随笔為你收集整理的太牛了,值得收藏!7000字22张图,精讲 Redis 知识!的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
 
                            
                        - 上一篇: 用Python执行SQL、Excel常见
- 下一篇: 什么是智能硬件开发
