天天看點

如何解決Redis緩存擊穿、雪崩、穿透問題

作者:Java碼農之路

1.全面解析Redis-RDB與AOF持久化機制

Redis之是以能夠提供高速讀寫操作是因為資料存儲在記憶體中,但這也帶來了一個風險,即在伺服器當機或斷電的情況下,記憶體中的資料會丢失。為了解決這個問題,Redis提供了持久化機制來確定資料的持久性和可靠性。

Redis持久化機制:

ⅰRDB(Redis Data Base) :記憶體快照

ⅱAOF(Append Only File): 增量日志

混合持久化:RDB + AOF

RDB持久化

在指定的時間間隔内将記憶體中的資料集快照寫入磁盤,RDB是記憶體快照(記憶體資料的二進制序列化形式)的方式持久化,每次都是從Redis中生成一個快照進行資料的全量備份。

RDB持久化流程:

2.繼續處理用戶端請求3.處理新的寫操作主程序記憶體副本COW機制1.FORK子程序備份子程序共享記憶體2.将共享記憶體資料寫到臨時RDB檔案3.完成臨時檔案寫入,替換舊RDB檔案子程序退出RDB持久化流程

如何解決Redis緩存擊穿、雪崩、穿透問題

RDB持久化方案進行備份時,Redis會單獨fork一個子程序來進行持久化,會将資料寫入一個臨時檔案中,持久化完成後替換舊的RDB檔案。

在整個持久化過程中,主程序(為用戶端提供服務的程序)不參與IO操作,這樣能確定Redis服務的高性能,RDB持久化機制适合對資料完整性要求不高但追求高效恢複的使用場景。

RDB觸發規則

手動觸發

save:

阻塞目前 Redis程序,直到RDB持久化過程完成,如果記憶體執行個體比較大會造成長時間阻塞,盡量不要使用這方式

bgsave:

Redis主程序fork建立子程序,由子程序完成持久化,阻塞時間很短(微秒級)

自動觸發

配置觸發:

●在Redis安裝目錄下的redis.conf配置檔案中搜尋 /snapshot即可快速定位,配置檔案預設注釋了下面三行資料,通過配置規則來觸發RDB的持久化,需要開啟或者根據自己的需求按照規則來配置。

井井THESE EXPLICITLY BY UNCOMMENTING THE FOLLOWING LINE.YOU CAN SET SAVE 3600 1 300 60 10000# BY DEFAULT REDIS WILL STOP ACCEPTING WRITES IF F FERFRDBSNAPSHOTS ARE ENABLED

如何解決Redis緩存擊穿、雪崩、穿透問題

save 3600 1 -- 3600 秒内有1個key被修改,觸發RDB save 300 100 -- 300 秒内有100個key被修改,觸發RDB save 60 10000 -- 60 秒内有10000個key被修改,觸發RDB

shutdown觸發:

●shutdown觸發Redis的RDB持久化機制非常簡單,我們在用戶端執行shutdown即可。

[ROOT@LOCALHOST BIN]#RM - RF DUMP.RDB,删除已經存在的RDB檔案BIN]# REDIS-CLI -P 6379[[email protected]:6379>SHUTDOWN關閉REDIS-SERVENNOT CONNECTED> QUIT[ROOT@LOCALHOST BIN]# LLTOTAL 21556重新生成的檔案JUL23:193026CONFDRWXR-XR-X.ROOTROOT12727JUL00:42DUMP.RDB1ROOT-RW-R----.ROOT5204342JUL126-RWXR-XR-X.06:56REDIS - BENCHMARKROOTROOT12JUL1REDIS-SERVER06:5626REDIS - CHECK - AOFL RWXRWXRWX.ROOTROOT12JULREDIS-SERVER2606:56REDIS - CHECK - RDBL RWXRWXRWX.ROOTROOT5421781- CLIJUL06:5626REDI!ROOTROOTRWXR-XR-X.12JULLRWXRWXRWX.06:56REDIS - SENTINELREDIS26SERVERROOTROOTROOT 11438191LNC2606:56REDIS-ROOTS - SERVERRWXR-XR-X.

如何解決Redis緩存擊穿、雪崩、穿透問題

flushall觸發:

●flushall清空Redis所有資料庫的資料(16個庫資料都會被删除)(等同于删庫跑路)

[ROOT@LOCALHOST BIN]# RM -RF DUMP.RDB删除RDB檔案REDIS-CLI -P 6379BIN]#[[email protected],0,1:6379>DBSIZEREDIS中存在7條資料(INTEGER) 7FLUSHALL127.0.0.1:6379>OK執行FLUSHALL指令127.0.0.1:6379>QUIT[ROOT@LOCALHOST BIN]# LL21556TOTAL30LUL2623:19CONFDRWXR-XR-X, 2ROOTROOT8927JUL00:46DUMP. RDBRW-R-R--. 1ROOTROOTXXSCSSREDIS - BENCHMARK5204342JUL06:561ROOTROOTRWXR-XR-X.JUL12REDIS - CHECK - AOFREDIS.06:561SERVERROOTL RWXRWXRWX.ROOT12JULREDIS - CHECK- RDBREDIS- SERVER06:56L RWXRWXRWX,ROOTROOT5421781JULREDIS-CLI06:56ROO1ROOTRWXR-XR-X.62212REDIS - SERVERREDIS-SENTINEL06:56L RWXRWXRWX,ROO1ROOTJULROOT 1143819106:56REDISSERVERROOTRWXR-XR-X.

如何解決Redis緩存擊穿、雪崩、穿透問題

優點:

●性能高:RDB持久化是通過生成一個快照檔案來儲存資料,是以在恢複資料時速度非常快。

●檔案緊湊:RDB檔案是二進制格式的資料庫檔案,相對于AOF檔案來說,檔案體積較小。

缺點:

●可能丢失資料:由于RDB是定期生成的快照檔案,如果Redis意外當機,最近一次的修改可能會丢失。

TIPS

Redis持久化預設開啟為RDB持久化

AOF持久化

AOF持久化需要手動修改conf配置開啟。

AOF持久化流程:

AOF日志重寫啟動時讀取AOF日志檔案恢複資料用戶端寫請求AOF日志檔案AOF緩沖區REDIS程序

如何解決Redis緩存擊穿、雪崩、穿透問題

AOF持久化方案進行備份時,用戶端所有請求的寫指令都會被追加到AOF緩沖區中,緩沖區中的資料會根據Redis配置檔案中配置的同步政策來同步到磁盤上的AOF檔案中,同時當AOF的檔案達到重寫政策配置的門檻值時,Redis會對AOF日志檔案進行重寫,給AOF日志檔案瘦身。Redis服務重新開機的時候,通過加載AOF日志檔案來恢複資料。

AOF配置:

AOF預設不開啟,預設為appendonly no,開啟則需要修改為appendonly yes

APPENDONLY YES

如何解決Redis緩存擊穿、雪崩、穿透問題

關閉AOF+RDB混合模式,設為no:

AOF-USE-RDB-PREAMBLEOU

如何解決Redis緩存擊穿、雪崩、穿透問題

AOF同步政策:

APPENDFSYNC#ALWAYSLAPPENDFSYNC EVERYSEC#APPENDFSYNC NO

如何解決Redis緩存擊穿、雪崩、穿透問題

appendfsync always:

○每次Redis寫操作,都寫入AOF日志,非常耗性能的。

appendfsync everysec

○每秒重新整理一次緩沖區中的資料到AOF檔案,這個Redis配置檔案中預設的政策,相容了性能和資料完整性的折中方案,這種配置,理論上丢失的資料在一秒鐘左右

appendfsync no

○Redis程序不會主動的去重新整理緩沖區中的資料到AOF檔案中,而是直接交給作業系統去判斷,這種操作也是不推薦的,丢失資料的可能性非常大。

AOF修複功能:

redis 7版本,AOF檔案存儲在appendonlydir檔案下,base是基準檔案,incr是追加資料。

JUL0APPENDONLY.AOF.1.BASE.AOFROOTROOTRW-R--R--.102:58.AOF.1.INCR.AOF0APPENDONLY.ROOTROOTRW-R--R--.1ROOT 88 JUL 27 0702:58ROOTAPPENDONLY.AOF.MANIFEST-RW-R--R--.

如何解決Redis緩存擊穿、雪崩、穿透問題

先存入三條資料,然後破壞incr結尾的檔案内容,末尾加上baili

[ROOT@LOCALHOSTREDIS-SERVER CONF/REDIS-CONFIG.CONFBIN]#[ROOT@LOCALHOSTBIN]#REDIS-CLI -P 6379127.0.0.1:6379>SETAA AAOK127.0.0.1:6379> SET BB BBOK127.0.0.1:6379> SET CC CCOK

如何解決Redis緩存擊穿、雪崩、穿透問題

SET$2CC2$CC百裡

如何解決Redis緩存擊穿、雪崩、穿透問題

重新啟動報錯:

[ROOT@LOCALHOST BIN]#D1N]# REDIS-CLI -P 6379CONNECTION REFUSEDREDIS AT 127.0.0.1:6379:COULDTO REDNOT CONNECTCONNECTED>NOT

如何解決Redis緩存擊穿、雪崩、穿透問題

使用redis-check-aof --fix appendonlydir/appendonly.aof.1.incr.aof 對AOF日志檔案進行修複

REDIS-SERVER CONF/REDIS-CONFIG.CONF[ROOT@LOCALHOST BIN]#6379[ROOT@LOCALHOST BIN]#REDIS-CLI127.0.0.1:6379>GET CC(NIL)*127.0.0.1:6379> KEYSBB"

如何解決Redis緩存擊穿、雪崩、穿透問題

觀察資料可以知道,丢失了cc-key值。這種丢失是被允許的。

AOF重寫

重寫其實是針對AOF存儲的重複性備援指令進行整理,比如有些key反複修改,又或者key反複修改後最終被删除,這些過程中的指令都是備援且不需要存儲的。

自動重寫:

當AOF日志檔案達到門檻值時會觸發自動重寫。

重寫門檻值配置:

AUTO-AOF-REWRITE-PERCENTAGE100AUTO-AOF-REWRITE-64MB- SIZE- MIN

如何解決Redis緩存擊穿、雪崩、穿透問題

●auto-aof-rewrite-percentage 100:當AOF檔案體積達到上次重寫之後的體積的100%時,會觸發AOF重寫。

●auto-aof-rewrite-min-size 64mb:當AOF檔案體積超過這個門檻值時,會觸發AOF重寫。

當AOF檔案的體積達到或超過上次重寫之後的比例,并且超過了最小體積門檻值時,Redis會自動觸發AOF重寫操作,生成一個新的AOF檔案。

手動重寫:bgrewriteaof

正常啟動後存在三個檔案:

JUL0APPENDONLY.AOF.1.BASE.AOFROOTROOTRW-R--R--.102:58.AOF.1.INCR.AOF0APPENDONLY.ROOTROOTRW-R--R--.1ROOT 88 JUL 27 0702:58ROOTAPPENDONLY.AOF.MANIFEST-RW-R--R--.

如何解決Redis緩存擊穿、雪崩、穿透問題

通過set指令存儲三條資料,最後在修改aa資料,然後手動重寫:

BIN]# REDIS-CLI[[email protected]:6379>SETAA AAOK127.0.0.1:6379> SET AA AALAA1OK127.0.0.1:6379> SET AA AA2OK127.0.0.1:6379>SET AA BAILIOK127.0.0.1:6379>BGREWRITEAOFBACKGROUNDSTARTEDONLY FILEREWRITINGAPPEND

如何解決Redis緩存擊穿、雪崩、穿透問題

觀察結果可以得知key值aa曆史軌迹已經被删除

$6SELECT$1*3$3SET$2AA$5BAILI

如何解決Redis緩存擊穿、雪崩、穿透問題

優點:

●資料更加可靠:AOF持久化記錄了每個寫指令的操作,是以在出現故障時,可以通過重新執行AOF檔案來保證資料的完整性。

●可以保留寫指令曆史:AOF檔案是一個追加日志檔案,可以用于回放過去的寫操作。

缺點:

●檔案較大:由于記錄了每個寫指令,AOF檔案體積通常比RDB檔案要大。

●恢複速度較慢:當AOF檔案較大時,Redis重新開機時需要重新執行整個AOF檔案,恢複速度相對較慢。

混合持久化

Redis4.0版本開始支援混合持久化,因為RDB雖然加載快但是存在資料丢失,AOF資料安全但是加載緩慢。

混合持久化通過aof-use-rdb-preamble yes開啟,Redis 4.0以上版本預設開啟

AOF-USE-RDB-PREAMBLEYES

如何解決Redis緩存擊穿、雪崩、穿透問題

開啟混合持久化之後:appendonlydir檔案下存在一個rdb檔案與一個aof檔案

04:2289APPENDONLY.AOF.L.BASE.RDBROOTROOTRW-R--R--,04:221JULAPPENDONLY.AOF.1.INCR.AOFROOTRW-R--R--ROOT88 JUL 27 04:221APPENDONLY.AOF.MANIFESTROOTROOT-RW-R--R--

如何解決Redis緩存擊穿、雪崩、穿透問題

存入資料,然後執行bgrewriteaof重寫檔案。

6379BIN]#[[email protected]:6379>SETAAAAOK127.0.0.1:6379>SET AA BBOK127.0.0.1:6379>SET AA CCOK127.0.0.1:6379>SET AA BAILIOK127.0.0.1:6379>BGREWRITEAOFBACKGROUNDONLY FILEREWRITING STARTEDAPPEND

如何解決Redis緩存擊穿、雪崩、穿透問題

APPENDONLYDIR]#[ROOT@LOCALHOSTAPPENDONLY.AOF.2.BASE.RDBCATREDIS-VER7.0.12REDIS0010S[ROOT@LOCALHOST APPENDEREDIS-BITSODEQTUSED-MEMAOF-BASEA PO BAILIY

如何解決Redis緩存擊穿、雪崩、穿透問題

總結

●推薦兩者均開啟

●如果對資料不敏感,可以選單獨用RDB

●不建議單獨用AOF,因為可能會出現Bug

●如果隻是做純記憶體緩存,可以都不用

2.Redis緩存擊穿、緩存雪崩、緩存穿透

緩存擊穿、緩存雪崩和緩存穿透是我們在日常開發與手撕面試官過程中必須battle的常見問題,下面我會解釋它們的含義與解決方案。

1緩存擊穿(Cache Miss)

什麼是緩存擊穿?

緩存擊穿是指在高并發通路下,一個熱點資料失效時,大量請求會直接繞過緩存,直接查詢資料庫,導緻資料庫壓力劇增。

通常情況下,緩存是為了減輕資料庫的負載,提高讀取性能而設定的。當某個特定的緩存鍵(key)失效後,在下一次請求該緩存時,由于緩存中沒有對應的資料,是以會去資料庫中查詢,這就是緩存擊穿。

解決方案:

●合理的過期時間:設定熱點資料永不過期,或者設定較長的過期時間,以免頻繁失效。

●使用互斥鎖:保證同一時間隻有一個線程來查詢資料庫,其他線程等待查詢結果。

2緩存雪崩(Cache Avalanche)

什麼是緩存雪崩?

緩存雪崩是指在大規模緩存失效或者緩存當機的情況下,大量請求同時湧入資料庫,導緻資料庫負載過大甚至崩潰的情況。

正常情況下,緩存中的資料會根據過期時間進行更新,當大量資料同時失效時,下一次請求就會直接通路資料庫,給資料庫帶來巨大壓力。

解決方案:

●合理的過期時間:為緩存的過期時間引入随機值,分散緩存過期時間,避免大規模同時失效。或者是粗暴的設定熱點資料永不過期

●多級緩存:使用多級緩存架構,如本地緩存 + 分布式緩存,提高系統的容錯能力。

●使用互斥鎖:保證同一時間隻有一個線程來查詢資料庫,其他線程等待查詢結果。

●高可用架構:使用Redis主從複制或者叢集來增加緩存的可用性,避免單點故障導緻整個系統無法使用。

3緩存穿透(Cache Penetration)

什麼是緩存穿透?

緩存穿透是指惡意請求查詢一個不存在于緩存和資料庫中的資料,導緻每次請求都直接通路資料庫,進而增加資料庫的負載。

攻擊者可以通過故意構造不存在的 Key 來進行緩存穿透攻擊。

解決方案:

●緩存空對象:對于查詢結果為空的情況,也将其緩存起來,但使用較短的過期時間,防止攻擊者利用同樣的 key 進行頻繁攻擊。

●參數校驗:在接收到請求之前進行參數校驗,判斷請求參數是否合法。

●布隆過濾器:判斷請求的參數是否存在于緩存或資料庫中。

3.什麼是Redis哨兵機制

為什麼需要哨兵機制?

Redis的主從複制主要用于實作資料的備援備份和讀分擔,并不是為了提供高可用性。是以在系統高可用方面,單純的主從架構無法很好的保證整個系統高可用。比如說:

MASTERMYSQLSLAVESLAVE主從複制:多副本

如何解決Redis緩存擊穿、雪崩、穿透問題

●需要人工介入:需要人工介入進行主節點切換。當主節點發生故障時,主從複制無法自動進行主節點的切換。需要管理者手動幹預,修改配置将一個從節點提升為新的主節點。這增加了人工操作的複雜性和潛在的延遲。

●主節點寫能力有限:主節點的寫能力受限于單個節點。在主從複制中,所有寫操作都必須發送給主節點處理,然後再同步到從節點。這導緻主節點成為寫入瓶頸,其寫能力受限于單個節點的硬體和性能。如果負載過大,主節點的響應時間可能會增加,影響整體性能。

●單機節點存儲能力有限:存儲能力受限于主節點的容量。在主從複制中,所有資料都存儲在主節點上,從節點僅用于提供讀服務。這限制了整個系統的存儲能力,因為主節點的存儲容量有限。如果資料量增長過快或存儲需求增加,主節點的存儲容量可能會成為瓶頸。

是以通常是使用Redis哨兵機制或Redis叢集模式來提高整個系統的可用性、擴充性和負載均衡能力。

哨兵機制(sentinel)的原理

Redis哨兵機制是通過在獨立的哨兵節點上運作特定的哨兵程序來實作的。這些哨兵程序監控主從節點的狀态,并在發現故障時自動完成故障發現和轉移,并通知應用方,實作高可用性。

以下是哨兵機制的工作原理:

哨兵叢集SENTINELMASTER監控MYSQLSENTINELSENTINELSLAVESLAVE監控REDIS哨兵高可用架構

如何解決Redis緩存擊穿、雪崩、穿透問題

a哨兵選舉:

在啟動時,每個哨兵節點會執行選舉過程,其中一個哨兵節點被選為上司者(leader),負責協調其他哨兵節點。

ⅰ選舉過程:

●每個線上的哨兵節點都可以成為上司者,每個哨兵節點會向其它哨兵發is-master-down-by-addr指令,征求判斷并要求将自己設定為上司者;

●當其它哨兵收到此指令時,可以同意或者拒絕它成為上司者;

●如果哨兵發現自己在選舉的票數大于等于num(sentinels)/2+1時,将成為上司者,如果沒有超過,繼續選舉。

b監控主從節點:

哨兵節點通過發送指令周期性地檢查主從節點的健康狀态,包括主節點是否線上、從節點是否同步等。如果哨兵節點發現主節點不可用,它會觸發一次故障轉移。

c故障轉移:

一旦主節點被判定為不可用,哨兵節點會執行故障轉移操作。它會從目前的從節點中選出一個新的主節點,并将其他從節點切換到新的主節點。這樣,系統可以繼續提供服務而無需人工介入。

ⅰ故障轉移過程:

○由Sentinel節點定期監控發現主節點是否出現了故障: sentinel會向master發送心跳PING來确認master是否存活,如果master在“一定時間範圍”内不回應PONG 或者是回複了一個錯誤消息,那麼這個sentinel會主觀地(單方面地)認為這個master已經不可用了。

○确認主節點:

■過濾掉不健康的(下線或斷線),沒有回複過哨兵ping響應的從節點

■選擇從節點優先級最高的

■選擇複制偏移量最大,此指複制最完整的從節點

當主節點出現故障, 由上司者負責處理主節點的故障轉移。

d用戶端重定向:

哨兵節點會通知用戶端新的主節點的位置,使其能夠與新的主節點建立連接配接并發送請求。這確定了用戶端可以無縫切換到新的主節點,繼續進行操作。

此外,哨兵節點還負責監控從節點的狀态。如果從節點出現故障,哨兵節點可以将其下線,并在從節點恢複正常後重新将其加入叢集。

客觀下線

當主觀下線的節點是主節點時,此時該哨兵3節點會通過指令sentinel is-masterdown-by-addr尋求其它哨兵節點對主節點的判斷,當超過quorum(選舉)個數,此時哨兵節點則認為該主節點确實有問題,這樣就客觀下線了,大部分哨兵節點都同意下線操作,也就說是客觀下線。

總結

redis哨兵的作用:

●監控主資料庫和從資料庫是否正常運作。

●主資料庫出現故障時,可以自動将從資料庫轉換為主資料庫,實作自動切換。

4.說一說Redis的過期政策和記憶體淘汰政策

allkeys-random(全局随機删除)​noeviction(不淘汰政策)​allkeys-lru(全局最近最少使用)​volatile-lru(最近最少使用)​volatile-random(随機删除)​volatile-ttl(根據過期時間優先)Redis的記憶體淘汰政策​定期過期政策(Active expiration)​惰性過期政策(Lazy expiration)Redis的過期政策Redis的過期政策和記憶體淘汰政策Redis的過期政策

a惰性删除(Lazy expiration)

●當用戶端嘗試通路某個鍵時,Redis會先檢查該鍵是否設定了過期時間,并判斷是否過期。

●如果鍵已過期,則Redis會立即将其删除。這就是惰性删除政策。

該政策可以最大化地節省CPU資源,卻對記憶體非常不友好。極端情況可能出現大量的過期key沒有再次被通路,進而不會被清除,占用大量記憶體。

b定期删除(Active expiration)

●Redis會每隔一段時間(預設100毫秒)随機檢查一部分設定了過期時間的鍵。

●定期過期政策通過使用循環周遊方式,逐個檢查鍵是否過期,并删除已過期的鍵值對。

通過調整定時掃描的時間間隔和每次掃描的限定耗時,可以在不同情況下使得CPU和記憶體資源達到最優的平衡效果

Redis中同時使用了惰性過期和定期過期兩種過期政策。

●假設Redis目前存放20萬個key,并且都設定了過期時間,如果你每隔100ms就去檢查這全部的key,CPU負載會特别高,最後可能會挂掉。

●是以redis采取的是定期過期,每隔100ms就随機抽取一定數量的key來檢查和删除的。

●但是呢,最後可能會有很多已經過期的key沒被删除。這時候,redis采用惰性删除。在你擷取某個key的時候,redis會檢查一下,這個key如果設定了過期時間并且已經過期了,此時就會删除。

需要注意如果定期删除漏掉了很多過期的key,然後也沒走惰性删除。就會有很多過期key積在記憶體中,可能會導緻記憶體溢出,或者是業務量太大,記憶體不夠用然後溢出了,為了應對這個問題,Redis引入了記憶體淘汰政策進行優化。

Redis的記憶體淘汰政策

記憶體淘汰政策允許Redis在記憶體資源緊張時,根據一定的政策主動删除一些鍵值對,以釋放記憶體空間并保持系統的穩定性。

anoeviction(不淘汰政策)

當記憶體不足以容納新寫入資料時,Redis 将新寫入的指令傳回錯誤。這個政策確定資料的完整性,但會導緻寫入操作失敗。

bvolatile-lru(最近最少使用)

從設定了過期時間的鍵中選擇最少使用的鍵進行删除。該政策優先删除最久未被通路的鍵,保留最常用的鍵。

cvolatile-ttl(根據過期時間優先)

從設定了過期時間的鍵中選擇剩餘時間最短的鍵進行删除。該政策優先删除剩餘時間較短的鍵,以盡量保留剩餘時間更長的鍵。

dvolatile-random(随機删除)

從設定了過期時間的鍵中随機選擇一個鍵進行删除。

eallkeys-lru(全局最近最少使用)

從所有鍵中選擇最少使用的鍵進行删除。無論鍵是否設定了過期時間,都将參與淘汰。

fallkeys-random(全局随機删除)

從所有鍵中随機選擇一個鍵進行删除。

繼續閱讀