1.全面解析redis-RDB與AOF持久化機制
Redis之所以能夠提供高速讀寫操作是因為數(shù)據(jù)存儲在內(nèi)存中,但這也帶來了一個風險,即在服務(wù)器宕機或斷電的情況下,內(nèi)存中的數(shù)據(jù)會丟失。為了解決這個問題,Redis提供了持久化機制來確保數(shù)據(jù)的持久性和可靠性。
Redis持久化機制:
ⅰ.RDB(Redis Data Base) :內(nèi)存快照
ⅱ.AOF(Append Only File): 增量日志
混合持久化:RDB + AOF
RDB持久化
在指定的時間間隔內(nèi)將內(nèi)存中的數(shù)據(jù)集快照寫入磁盤,RDB是內(nèi)存快照(內(nèi)存數(shù)據(jù)的二進制序列化形式)的方式持久化,每次都是從Redis中生成一個快照進行數(shù)據(jù)的全量備份。
RDB持久化流程:
RDB持久化方案進行備份時,Redis會單獨fork一個子進程來進行持久化,會將數(shù)據(jù)寫入一個臨時文件中,持久化完成后替換舊的RDB文件。
在整個持久化過程中,主進程(為客戶端提供服務(wù)的進程)不參與IO操作,這樣能確保Redis服務(wù)的高性能,RDB持久化機制適合對數(shù)據(jù)完整性要求不高但追求高效恢復的使用場景。
RDB觸發(fā)規(guī)則
手動觸發(fā)
save:
阻塞當前 Redis進程,直到RDB持久化過程完成,如果內(nèi)存實例比較大會造成長時間阻塞,盡量不要使用這方式
bgsave:
Redis主進程fork創(chuàng)建子進程,由子進程完成持久化,阻塞時間很短(微秒級)
自動觸發(fā)
配置觸發(fā):
- 在Redis安裝目錄下的redis.conf配置文件中搜索 /snapshot即可快速定位,配置文件默認注釋了下面三行數(shù)據(jù),通過配置規(guī)則來觸發(fā)RDB的持久化,需要開啟或者根據(jù)自己的需求按照規(guī)則來配置。
save 3600 1 -- 3600 秒內(nèi)有1個key被修改,觸發(fā)RDB
save 300 100 -- 300 秒內(nèi)有100個key被修改,觸發(fā)RDB
save 60 10000 -- 60 秒內(nèi)有10000個key被修改,觸發(fā)RDB
shutdown觸發(fā):
- shutdown觸發(fā)Redis的RDB持久化機制非常簡單,我們在客戶端執(zhí)行shutdown即可。
flushall觸發(fā):
- flushall清空Redis所有數(shù)據(jù)庫的數(shù)據(jù)(16個庫數(shù)據(jù)都會被刪除)(等同于刪庫跑路)
優(yōu)點:
- 性能高:RDB持久化是通過生成一個快照文件來保存數(shù)據(jù),因此在恢復數(shù)據(jù)時速度非常快。
- 文件緊湊:RDB文件是二進制格式的數(shù)據(jù)庫文件,相對于AOF文件來說,文件體積較小。
缺點:
- 可能丟失數(shù)據(jù):由于RDB是定期生成的快照文件,如果Redis意外宕機,最近一次的修改可能會丟失。
TIPS
Redis持久化默認開啟為RDB持久化
AOF持久化
AOF持久化需要手動修改conf配置開啟。
AOF持久化流程:
AOF持久化方案進行備份時,客戶端所有請求的寫命令都會被追加到AOF緩沖區(qū)中,緩沖區(qū)中的數(shù)據(jù)會根據(jù)Redis配置文件中配置的同步策略來同步到磁盤上的AOF文件中,同時當AOF的文件達到重寫策略配置的閾值時,Redis會對AOF日志文件進行重寫,給AOF日志文件瘦身。Redis服務(wù)重啟的時候,通過加載AOF日志文件來恢復數(shù)據(jù)。
AOF配置:
AOF默認不開啟,默認為appendonly no,開啟則需要修改為appendonly yes
關(guān)閉AOF+RDB混合模式,設(shè)為no:
AOF同步策略:
appendfsync always:
-
- 每次Redis寫操作,都寫入AOF日志,非常耗性能的。
appendfsync everysec
-
- 每秒刷新一次緩沖區(qū)中的數(shù)據(jù)到AOF文件,這個Redis配置文件中默認的策略,兼容了性能和數(shù)據(jù)完整性的折中方案,這種配置,理論上丟失的數(shù)據(jù)在一秒鐘左右
appendfsync no
-
- Redis進程不會主動的去刷新緩沖區(qū)中的數(shù)據(jù)到AOF文件中,而是直接交給操作系統(tǒng)去判斷,這種操作也是不推薦的,丟失數(shù)據(jù)的可能性非常大。
AOF修復功能:
redis 7版本,AOF文件存儲在appendonlydir文件下,base是基準文件,incr是追加數(shù)據(jù)。
先存入三條數(shù)據(jù),然后破壞incr結(jié)尾的文件內(nèi)容,末尾加上bAIli
重新啟動報錯:
使用redis-check-aof --fix
appendonlydir/appendonly.aof.1.incr.aof 對AOF日志文件進行修復
觀察數(shù)據(jù)可以知道,丟失了cc-key值。這種丟失是被允許的。
AOF重寫
重寫其實是針對AOF存儲的重復性冗余指令進行整理,比如有些key反復修改,又或者key反復修改后最終被刪除,這些過程中的指令都是冗余且不需要存儲的。
自動重寫:
當AOF日志文件達到閾值時會觸發(fā)自動重寫。
重寫閾值配置:
- auto-aof-rewrite-percentage 100:當AOF文件體積達到上次重寫之后的體積的100%時,會觸發(fā)AOF重寫。
- auto-aof-rewrite-min-size 64mb:當AOF文件體積超過這個閾值時,會觸發(fā)AOF重寫。
當AOF文件的體積達到或超過上次重寫之后的比例,并且超過了最小體積閾值時,Redis會自動觸發(fā)AOF重寫操作,生成一個新的AOF文件。
手動重寫:bgrewriteaof
正常啟動后存在三個文件:
通過set命令存儲三條數(shù)據(jù),最后在修改aa數(shù)據(jù),然后手動重寫:
觀察結(jié)果可以得知key值aa歷史軌跡已經(jīng)被刪除
優(yōu)點:
- 數(shù)據(jù)更加可靠:AOF持久化記錄了每個寫命令的操作,因此在出現(xiàn)故障時,可以通過重新執(zhí)行AOF文件來保證數(shù)據(jù)的完整性。
- 可以保留寫命令歷史:AOF文件是一個追加日志文件,可以用于回放過去的寫操作。
缺點:
- 文件較大:由于記錄了每個寫命令,AOF文件體積通常比RDB文件要大。
- 恢復速度較慢:當AOF文件較大時,Redis重啟時需要重新執(zhí)行整個AOF文件,恢復速度相對較慢。
混合持久化
Redis4.0版本開始支持混合持久化,因為RDB雖然加載快但是存在數(shù)據(jù)丟失,AOF數(shù)據(jù)安全但是加載緩慢。
混合持久化通過aof-use-rdb-preamble yes開啟,Redis 4.0以上版本默認開啟
開啟混合持久化之后:appendonlydir文件下存在一個rdb文件與一個aof文件
存入數(shù)據(jù),然后執(zhí)行bgrewriteaof重寫文件。
總結(jié)
- 推薦兩者均開啟
- 如果對數(shù)據(jù)不敏感,可以選單獨用RDB
- 不建議單獨用AOF,因為可能會出現(xiàn)Bug
- 如果只是做純內(nèi)存緩存,可以都不用
2.Redis緩存擊穿、緩存雪崩、緩存穿透
緩存擊穿、緩存雪崩和緩存穿透是我們在日常開發(fā)與手撕面試官過程中必須battle的常見問題,下面我會解釋它們的含義與解決方案。
1.緩存擊穿(Cache Miss)
什么是緩存擊穿?
緩存擊穿是指在高并發(fā)訪問下,一個熱點數(shù)據(jù)失效時,大量請求會直接繞過緩存,直接查詢數(shù)據(jù)庫,導致數(shù)據(jù)庫壓力劇增。
通常情況下,緩存是為了減輕數(shù)據(jù)庫的負載,提高讀取性能而設(shè)置的。當某個特定的緩存鍵(key)失效后,在下一次請求該緩存時,由于緩存中沒有對應(yīng)的數(shù)據(jù),因此會去數(shù)據(jù)庫中查詢,這就是緩存擊穿。
解決方案:
- 合理的過期時間:設(shè)置熱點數(shù)據(jù)永不過期,或者設(shè)置較長的過期時間,以免頻繁失效。
- 使用互斥鎖:保證同一時間只有一個線程來查詢數(shù)據(jù)庫,其他線程等待查詢結(jié)果。
2.緩存雪崩(Cache Avalanche)
什么是緩存雪崩?
緩存雪崩是指在大規(guī)模緩存失效或者緩存宕機的情況下,大量請求同時涌入數(shù)據(jù)庫,導致數(shù)據(jù)庫負載過大甚至崩潰的情況。
正常情況下,緩存中的數(shù)據(jù)會根據(jù)過期時間進行更新,當大量數(shù)據(jù)同時失效時,下一次請求就會直接訪問數(shù)據(jù)庫,給數(shù)據(jù)庫帶來巨大壓力。
解決方案:
- 合理的過期時間:為緩存的過期時間引入隨機值,分散緩存過期時間,避免大規(guī)模同時失效。或者是粗暴的設(shè)置熱點數(shù)據(jù)永不過期
- 多級緩存:使用多級緩存架構(gòu),如本地緩存 + 分布式緩存,提高系統(tǒng)的容錯能力。
- 使用互斥鎖:保證同一時間只有一個線程來查詢數(shù)據(jù)庫,其他線程等待查詢結(jié)果。
- 高可用架構(gòu):使用Redis主從復制或者集群來增加緩存的可用性,避免單點故障導致整個系統(tǒng)無法使用。
3.緩存穿透(Cache P.NETration)
什么是緩存穿透?
緩存穿透是指惡意請求查詢一個不存在于緩存和數(shù)據(jù)庫中的數(shù)據(jù),導致每次請求都直接訪問數(shù)據(jù)庫,從而增加數(shù)據(jù)庫的負載。
攻擊者可以通過故意構(gòu)造不存在的 Key 來進行緩存穿透攻擊。
解決方案:
- 緩存空對象:對于查詢結(jié)果為空的情況,也將其緩存起來,但使用較短的過期時間,防止攻擊者利用同樣的 key 進行頻繁攻擊。
- 參數(shù)校驗:在接收到請求之前進行參數(shù)校驗,判斷請求參數(shù)是否合法。
- 布隆過濾器:判斷請求的參數(shù)是否存在于緩存或數(shù)據(jù)庫中。
3.什么是Redis哨兵機制
為什么需要哨兵機制?
Redis的主從復制主要用于實現(xiàn)數(shù)據(jù)的冗余備份和讀分擔,并不是為了提供高可用性。因此在系統(tǒng)高可用方面,單純的主從架構(gòu)無法很好的保證整個系統(tǒng)高可用。比如說:
- 需要人工介入:需要人工介入進行主節(jié)點切換。當主節(jié)點發(fā)生故障時,主從復制無法自動進行主節(jié)點的切換。需要管理員手動干預(yù),修改配置將一個從節(jié)點提升為新的主節(jié)點。這增加了人工操作的復雜性和潛在的延遲。
- 主節(jié)點寫能力有限:主節(jié)點的寫能力受限于單個節(jié)點。在主從復制中,所有寫操作都必須發(fā)送給主節(jié)點處理,然后再同步到從節(jié)點。這導致主節(jié)點成為寫入瓶頸,其寫能力受限于單個節(jié)點的硬件和性能。如果負載過大,主節(jié)點的響應(yīng)時間可能會增加,影響整體性能。
- 單機節(jié)點存儲能力有限:存儲能力受限于主節(jié)點的容量。在主從復制中,所有數(shù)據(jù)都存儲在主節(jié)點上,從節(jié)點僅用于提供讀服務(wù)。這限制了整個系統(tǒng)的存儲能力,因為主節(jié)點的存儲容量有限。如果數(shù)據(jù)量增長過快或存儲需求增加,主節(jié)點的存儲容量可能會成為瓶頸。
因此通常是使用Redis哨兵機制或Redis集群模式來提高整個系統(tǒng)的可用性、擴展性和負載均衡能力。
哨兵機制(sentinel)的原理
Redis哨兵機制是通過在獨立的哨兵節(jié)點上運行特定的哨兵進程來實現(xiàn)的。這些哨兵進程監(jiān)控主從節(jié)點的狀態(tài),并在發(fā)現(xiàn)故障時自動完成故障發(fā)現(xiàn)和轉(zhuǎn)移,并通知應(yīng)用方,實現(xiàn)高可用性。
以下是哨兵機制的工作原理:
a.哨兵選舉:
在啟動時,每個哨兵節(jié)點會執(zhí)行選舉過程,其中一個哨兵節(jié)點被選為領(lǐng)導者(leader),負責協(xié)調(diào)其他哨兵節(jié)點。
ⅰ.選舉過程:
- 每個在線的哨兵節(jié)點都可以成為領(lǐng)導者,每個哨兵節(jié)點會向其它哨兵發(fā)is-master-down-by-addr命令,征求判斷并要求將自己設(shè)置為領(lǐng)導者;
- 當其它哨兵收到此命令時,可以同意或者拒絕它成為領(lǐng)導者;
- 如果哨兵發(fā)現(xiàn)自己在選舉的票數(shù)大于等于num(sentinels)/2+1時,將成為領(lǐng)導者,如果沒有超過,繼續(xù)選舉。
b.監(jiān)控主從節(jié)點:
哨兵節(jié)點通過發(fā)送命令周期性地檢查主從節(jié)點的健康狀態(tài),包括主節(jié)點是否在線、從節(jié)點是否同步等。如果哨兵節(jié)點發(fā)現(xiàn)主節(jié)點不可用,它會觸發(fā)一次故障轉(zhuǎn)移。
c.故障轉(zhuǎn)移:
一旦主節(jié)點被判定為不可用,哨兵節(jié)點會執(zhí)行故障轉(zhuǎn)移操作。它會從當前的從節(jié)點中選出一個新的主節(jié)點,并將其他從節(jié)點切換到新的主節(jié)點。這樣,系統(tǒng)可以繼續(xù)提供服務(wù)而無需人工介入。
ⅰ.故障轉(zhuǎn)移過程:
-
- 由Sentinel節(jié)點定期監(jiān)控發(fā)現(xiàn)主節(jié)點是否出現(xiàn)了故障: sentinel會向master發(fā)送心跳PING來確認master是否存活,如果master在“一定時間范圍”內(nèi)不回應(yīng)PONG 或者是回復了一個錯誤消息,那么這個sentinel會主觀地(單方面地)認為這個master已經(jīng)不可用了。
- 確認主節(jié)點:
- 過濾掉不健康的(下線或斷線),沒有回復過哨兵ping響應(yīng)的從節(jié)點
- 選擇從節(jié)點優(yōu)先級最高的
- 選擇復制偏移量最大,此指復制最完整的從節(jié)點
當主節(jié)點出現(xiàn)故障, 由領(lǐng)導者負責處理主節(jié)點的故障轉(zhuǎn)移。
d.客戶端重定向:
哨兵節(jié)點會通知客戶端新的主節(jié)點的位置,使其能夠與新的主節(jié)點建立連接并發(fā)送請求。這確保了客戶端可以無縫切換到新的主節(jié)點,繼續(xù)進行操作。
此外,哨兵節(jié)點還負責監(jiān)控從節(jié)點的狀態(tài)。如果從節(jié)點出現(xiàn)故障,哨兵節(jié)點可以將其下線,并在從節(jié)點恢復正常后重新將其加入集群。
客觀下線
當主觀下線的節(jié)點是主節(jié)點時,此時該哨兵3節(jié)點會通過指令sentinel is-masterdown-by-addr尋求其它哨兵節(jié)點對主節(jié)點的判斷,當超過quorum(選舉)個數(shù),此時哨兵節(jié)點則認為該主節(jié)點確實有問題,這樣就客觀下線了,大部分哨兵節(jié)點都同意下線操作,也就說是客觀下線。
總結(jié)
redis哨兵的作用:
- 監(jiān)控主數(shù)據(jù)庫和從數(shù)據(jù)庫是否正常運行。
- 主數(shù)據(jù)庫出現(xiàn)故障時,可以自動將從數(shù)據(jù)庫轉(zhuǎn)換為主數(shù)據(jù)庫,實現(xiàn)自動切換。
4.說一說Redis的過期策略和內(nèi)存淘汰策略
Redis的過期策略
a.惰性刪除(Lazy expiration)
- 當客戶端嘗試訪問某個鍵時,Redis會先檢查該鍵是否設(shè)置了過期時間,并判斷是否過期。
- 如果鍵已過期,則Redis會立即將其刪除。這就是惰性刪除策略。
該策略可以最大化地節(jié)省CPU資源,卻對內(nèi)存非常不友好。極端情況可能出現(xiàn)大量的過期key沒有再次被訪問,從而不會被清除,占用大量內(nèi)存。
b.定期刪除(Active expiration)
- Redis會每隔一段時間(默認100毫秒)隨機檢查一部分設(shè)置了過期時間的鍵。
- 定期過期策略通過使用循環(huán)遍歷方式,逐個檢查鍵是否過期,并刪除已過期的鍵值對。
通過調(diào)整定時掃描的時間間隔和每次掃描的限定耗時,可以在不同情況下使得CPU和內(nèi)存資源達到最優(yōu)的平衡效果
Redis中同時使用了惰性過期和定期過期兩種過期策略。
- 假設(shè)Redis當前存放20萬個key,并且都設(shè)置了過期時間,如果你每隔100ms就去檢查這全部的key,CPU負載會特別高,最后可能會掛掉。
- 因此redis采取的是定期過期,每隔100ms就隨機抽取一定數(shù)量的key來檢查和刪除的。
- 但是呢,最后可能會有很多已經(jīng)過期的key沒被刪除。這時候,redis采用惰性刪除。在你獲取某個key的時候,redis會檢查一下,這個key如果設(shè)置了過期時間并且已經(jīng)過期了,此時就會刪除。
需要注意如果定期刪除漏掉了很多過期的key,然后也沒走惰性刪除。就會有很多過期key積在內(nèi)存中,可能會導致內(nèi)存溢出,或者是業(yè)務(wù)量太大,內(nèi)存不夠用然后溢出了,為了應(yīng)對這個問題,Redis引入了內(nèi)存淘汰策略進行優(yōu)化。
Redis的內(nèi)存淘汰策略
內(nèi)存淘汰策略允許Redis在內(nèi)存資源緊張時,根據(jù)一定的策略主動刪除一些鍵值對,以釋放內(nèi)存空間并保持系統(tǒng)的穩(wěn)定性。
a.noeviction(不淘汰策略)
當內(nèi)存不足以容納新寫入數(shù)據(jù)時,Redis 將新寫入的命令返回錯誤。這個策略確保數(shù)據(jù)的完整性,但會導致寫入操作失敗。
b.volatile-lru(最近最少使用)
從設(shè)置了過期時間的鍵中選擇最少使用的鍵進行刪除。該策略優(yōu)先刪除最久未被訪問的鍵,保留最常用的鍵。
c.volatile-ttl(根據(jù)過期時間優(yōu)先)
從設(shè)置了過期時間的鍵中選擇剩余時間最短的鍵進行刪除。該策略優(yōu)先刪除剩余時間較短的鍵,以盡量保留剩余時間更長的鍵。
d.volatile-random(隨機刪除)
從設(shè)置了過期時間的鍵中隨機選擇一個鍵進行刪除。
e.allkeys-lru(全局最近最少使用)
從所有鍵中選擇最少使用的鍵進行刪除。無論鍵是否設(shè)置了過期時間,都將參與淘汰。
f.allkeys-random(全局隨機刪除)
從所有鍵中隨機選擇一個鍵進行刪除。