1. 什么是緩存雪崩?怎么解決?
通常,我們會(huì)使用緩存用于緩沖對(duì) DB 的沖擊,如果緩存宕機(jī),所有請(qǐng)求將直接打在 DB,造成 DB 宕機(jī)——從而導(dǎo)致整個(gè)系統(tǒng)宕機(jī)。
如何解決呢?
2 種策略(同時(shí)使用):
- 對(duì)緩存做高可用,防止緩存宕機(jī)。
- 使用斷路器,如果緩存宕機(jī),為了防止系統(tǒng)全部宕機(jī),限制部分流量進(jìn)入 DB,保證部分可用,其余的請(qǐng)求返回?cái)嗦菲鞯哪J(rèn)值。
2. 什么是緩存穿透?怎么解決?
解釋 1 緩存查詢一個(gè)沒(méi)有的 key,同時(shí)數(shù)據(jù)庫(kù)也沒(méi)有,如果黑客大量的使用這種方式,那么就會(huì)導(dǎo)致 DB 宕機(jī)。
解決方案:我們可以使用一個(gè)默認(rèn)值來(lái)防止,例如,當(dāng)訪問(wèn)一個(gè)不存在的 key,然后再去訪問(wèn)數(shù)據(jù)庫(kù),還是沒(méi)有,那么就在緩存里放一個(gè)占位符,下次來(lái)的時(shí)候,檢查這個(gè)占位符,如果發(fā)生時(shí)占位符,就不去數(shù)據(jù)庫(kù)查詢了,防止 DB 宕機(jī)。
解釋 2 大量請(qǐng)求查詢一個(gè)剛剛失效的 key,導(dǎo)致 DB 壓力倍增,可能導(dǎo)致宕機(jī),但實(shí)際上,查詢的都是相同的數(shù)據(jù)。
解決方案:可以在這些請(qǐng)求代碼加上雙重檢查鎖。但是那個(gè)階段的請(qǐng)求會(huì)變慢。不過(guò)總比 DB 宕機(jī)好。
3. 什么是緩存并發(fā)競(jìng)爭(zhēng)?怎么解決?
解釋 多個(gè)客戶端寫一個(gè) key,如果順序錯(cuò)了,數(shù)據(jù)就不對(duì)了。但是順序我們無(wú)法控制。
解決方案 使用分布式鎖,例如 zk,同時(shí)加入數(shù)據(jù)的時(shí)間戳。同一時(shí)刻,只有搶到鎖的客戶端才能寫入,同時(shí),寫入時(shí),比較當(dāng)前數(shù)據(jù)的時(shí)間戳和緩存中數(shù)據(jù)的時(shí)間戳。
4.什么是緩存和數(shù)據(jù)庫(kù)雙寫不一致?怎么解決?
解釋:連續(xù)寫數(shù)據(jù)庫(kù)和緩存,但是操作期間,出現(xiàn)并發(fā)了,數(shù)據(jù)不一致了。
通常,更新緩存和數(shù)據(jù)庫(kù)有以下幾種順序:
- 先更新數(shù)據(jù)庫(kù),再更新緩存。
- 先刪緩存,再更新數(shù)據(jù)庫(kù)。
- 先更新數(shù)據(jù)庫(kù),再刪除緩存。
三種方式的優(yōu)劣來(lái)看一下:
先更新數(shù)據(jù)庫(kù),再更新緩存。
這么做的問(wèn)題是:當(dāng)有 2 個(gè)請(qǐng)求同時(shí)更新數(shù)據(jù),那么如果不使用分布式鎖,將無(wú)法控制最后緩存的值到底是多少。也就是并發(fā)寫的時(shí)候有問(wèn)題。
先刪緩存,再更新數(shù)據(jù)庫(kù)。
這么做的問(wèn)題:如果在刪除緩存后,有客戶端讀數(shù)據(jù),將可能讀到舊數(shù)據(jù),并有可能設(shè)置到緩存中,導(dǎo)致緩存中的數(shù)據(jù)一直是老數(shù)據(jù)。
有 2 種解決方案:
- 使用“雙刪”,即刪更刪,最后一步的刪除作為異步操作,就是防止有客戶端讀取的時(shí)候設(shè)置了舊值。
- 使用隊(duì)列,當(dāng)這個(gè) key 不存在時(shí),將其放入隊(duì)列,串行執(zhí)行,必須等到更新數(shù)據(jù)庫(kù)完畢才能讀取數(shù)據(jù)。
總的來(lái)講,比較麻煩。
先更新數(shù)據(jù)庫(kù),再刪除緩存
這個(gè)實(shí)際是常用的方案,但是有很多人不知道,這里介紹一下,這個(gè)叫 Cache Aside Pattern,老外發(fā)明的。如果先更新數(shù)據(jù)庫(kù),再刪除緩存,那么就會(huì)出現(xiàn)更新數(shù)據(jù)庫(kù)之前有瞬間數(shù)據(jù)不是很及時(shí)。
同時(shí),如果在更新之前,緩存剛好失效了,讀客戶端有可能讀到舊值,然后在寫客戶端刪除結(jié)束后再次設(shè)置了舊值,非常巧合的情況。
有 2 個(gè)前提條件:緩存在寫之前的時(shí)候失效,同時(shí),在寫客戶度刪除操作結(jié)束后,放置舊數(shù)據(jù) —— 也就是讀比寫慢。設(shè)置有的寫操作還會(huì)鎖表。
所以,這個(gè)很難出現(xiàn),但是如果出現(xiàn)了怎么辦?使用雙刪!?。∮涗浉缕陂g有沒(méi)有客戶端讀數(shù)據(jù)庫(kù),如果有,在更新完數(shù)據(jù)庫(kù)之后,執(zhí)行延遲刪除。
還有一種可能,如果執(zhí)行更新數(shù)據(jù)庫(kù),準(zhǔn)備執(zhí)行刪除緩存時(shí),服務(wù)掛了,執(zhí)行刪除失敗怎么辦???
這就坑了?。?!不過(guò)可以通過(guò)訂閱數(shù)據(jù)庫(kù)的 binlog 來(lái)刪除。
緩存與數(shù)據(jù)庫(kù)一致性其實(shí)是一個(gè)非常龐大的專題,這里只做簡(jiǎn)要的概括
其他問(wèn)題
- 基礎(chǔ)數(shù)據(jù)結(jié)構(gòu):字符串String、字典Hash、列表List、集合Set、有序集合SortedSet。
- 高級(jí)數(shù)據(jù)結(jié)構(gòu): HyperLogLog、Geo、Pub/Sub。
- BloomFilter,RedisSearch,Redis-ML
- Redis緩存穿透、緩存雪崩和緩存擊穿
Redis緩存穿透:緩存穿透,是指查詢一個(gè)數(shù)據(jù)庫(kù)一定不存在的數(shù)據(jù)。
解決方案:BloomFilter布隆過(guò)濾器,key獲取value值為空時(shí)鎖上
緩存雪崩:緩存雪崩,是指在某一個(gè)時(shí)間段,緩存集中過(guò)期失效
解決方案:失效時(shí)間錯(cuò)開(kāi)
緩存擊穿:是指一個(gè)key非常熱點(diǎn),在不停的扛著大并發(fā),大并發(fā)集中對(duì)這一個(gè)點(diǎn)進(jìn)行訪問(wèn),當(dāng)這個(gè)key在失效的瞬間,持續(xù)的大并發(fā)就穿破緩存,直接請(qǐng)求數(shù)據(jù)庫(kù),就像在一個(gè)屏障上鑿開(kāi)了一個(gè)洞。
解決方案:直接設(shè)為永不過(guò)期
補(bǔ)充方案:接口限流與熔斷、降級(jí)
- Redis 支持 Lua 腳本并保證其原子性,使用 Lua 腳本實(shí)現(xiàn)鎖校驗(yàn)與釋放,并使用 Redis 的 eval() 函數(shù)執(zhí)行 Lua 腳本。
Redis的三種集群方式
redis有三種集群方式:主從復(fù)制,哨兵模式和集群。
1.主從復(fù)制
主從復(fù)制原理:
- 從服務(wù)器連接主服務(wù)器,發(fā)送SYNC命令;
- 主服務(wù)器接收到SYNC命名后,開(kāi)始執(zhí)行BGSAVE命令生成RDB文件并使用緩沖區(qū)記錄此后執(zhí)行的所有寫命令;
- 主服務(wù)器BGSAVE執(zhí)行完后,向所有從服務(wù)器發(fā)送快照文件,并在發(fā)送期間繼續(xù)記錄被執(zhí)行的寫命令;
- 從服務(wù)器收到快照文件后丟棄所有舊數(shù)據(jù),載入收到的快照;
- 主服務(wù)器快照發(fā)送完畢后開(kāi)始向從服務(wù)器發(fā)送緩沖區(qū)中的寫命令;
- 從服務(wù)器完成對(duì)快照的載入,開(kāi)始接收命令請(qǐng)求,并執(zhí)行來(lái)自主服務(wù)器緩沖區(qū)的寫命令;(從服務(wù)器初始化完成)
- 主服務(wù)器每執(zhí)行一個(gè)寫命令就會(huì)向從服務(wù)器發(fā)送相同的寫命令,從服務(wù)器接收并執(zhí)行收到的寫命令(從服務(wù)器初始化完成后的操作)
主從復(fù)制優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
- 支持主從復(fù)制,主機(jī)會(huì)自動(dòng)將數(shù)據(jù)同步到從機(jī),可以進(jìn)行讀寫分離
- 為了分載Master的讀操作壓力,Slave服務(wù)器可以為客戶端提供只讀操作的服務(wù),寫服務(wù)仍然必須由Master來(lái)完成
- Slave同樣可以接受其它Slaves的連接和同步請(qǐng)求,這樣可以有效的分載Master的同步壓力。
- Master Server是以非阻塞的方式為Slaves提供服務(wù)。所以在Master-Slave同步期間,客戶端仍然可以提交查詢或修改請(qǐng)求。
- Slave Server同樣是以非阻塞的方式完成數(shù)據(jù)同步。在同步期間,如果有客戶端提交查詢請(qǐng)求,Redis則返回同步之前的數(shù)據(jù)
缺點(diǎn)
- Redis不具備自動(dòng)容錯(cuò)和恢復(fù)功能,主機(jī)從機(jī)的宕機(jī)都會(huì)導(dǎo)致前端部分讀寫請(qǐng)求失敗,需要等待機(jī)器重啟或者手動(dòng)切換前端的IP才能恢復(fù)。
- 主機(jī)宕機(jī),宕機(jī)前有部分?jǐn)?shù)據(jù)未能及時(shí)同步到從機(jī),切換IP后還會(huì)引入數(shù)據(jù)不一致的問(wèn)題,降低了系統(tǒng)的可用性。
- Redis較難支持在線擴(kuò)容,在集群容量達(dá)到上限時(shí)在線擴(kuò)容會(huì)變得很復(fù)雜。
2.哨兵模式
當(dāng)主服務(wù)器中斷服務(wù)后,可以將一個(gè)從服務(wù)器升級(jí)為主服務(wù)器,以便繼續(xù)提供服務(wù),但是這個(gè)過(guò)程需要人工手動(dòng)來(lái)操作。 為此,Redis 2.8中提供了哨兵工具來(lái)實(shí)現(xiàn)自動(dòng)化的系統(tǒng)監(jiān)控和故障恢復(fù)功能。
哨兵的作用就是監(jiān)控Redis系統(tǒng)的運(yùn)行狀況。它的功能包括以下兩個(gè)。
(1)監(jiān)控主服務(wù)器和從服務(wù)器是否正常運(yùn)行。
(2)主服務(wù)器出現(xiàn)故障時(shí)自動(dòng)將從服務(wù)器轉(zhuǎn)換為主服務(wù)器。
哨兵的工作方式
- 每個(gè)Sentinel(哨兵)進(jìn)程以每秒鐘一次的頻率向整個(gè)集群中的Master主服務(wù)器,Slave從服務(wù)器以及其他Sentinel(哨兵)進(jìn)程發(fā)送一個(gè) PING 命令。
- 如果一個(gè)實(shí)例(instance)距離最后一次有效回復(fù) PING 命令的時(shí)間超過(guò) down-after-milliseconds 選項(xiàng)所指定的值, 則這個(gè)實(shí)例會(huì)被 Sentinel(哨兵)進(jìn)程標(biāo)記為主觀下線(SDOWN)
- 如果一個(gè)Master主服務(wù)器被標(biāo)記為主觀下線(SDOWN),則正在監(jiān)視這個(gè)Master主服務(wù)器的所有 Sentinel(哨兵)進(jìn)程要以每秒一次的頻率確認(rèn)Master主服務(wù)器的確進(jìn)入了主觀下線狀態(tài)
- 當(dāng)有足夠數(shù)量的 Sentinel(哨兵)進(jìn)程(大于等于配置文件指定的值)在指定的時(shí)間范圍內(nèi)確認(rèn)Master主服務(wù)器進(jìn)入了主觀下線狀態(tài)(SDOWN), 則Master主服務(wù)器會(huì)被標(biāo)記為客觀下線(ODOWN)
- 在一般情況下, 每個(gè) Sentinel(哨兵)進(jìn)程會(huì)以每 10 秒一次的頻率向集群中的所有Master主服務(wù)器、Slave從服務(wù)器發(fā)送 INFO 命令。
- 當(dāng)Master主服務(wù)器被 Sentinel(哨兵)進(jìn)程標(biāo)記為客觀下線(ODOWN)時(shí),Sentinel(哨兵)進(jìn)程向下線的 Master主服務(wù)器的所有 Slave從服務(wù)器發(fā)送 INFO 命令的頻率會(huì)從 10 秒一次改為每秒一次。
- 若沒(méi)有足夠數(shù)量的 Sentinel(哨兵)進(jìn)程同意 Master主服務(wù)器下線, Master主服務(wù)器的客觀下線狀態(tài)就會(huì)被移除。若 Master主服務(wù)器重新向 Sentinel(哨兵)進(jìn)程發(fā)送 PING 命令返回有效回復(fù),Master主服務(wù)器的主觀下線狀態(tài)就會(huì)被移除。
哨兵模式的優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
- 哨兵模式是基于主從模式的,所有主從的優(yōu)點(diǎn),哨兵模式都具有。
- 主從可以自動(dòng)切換,系統(tǒng)更健壯,可用性更高。
缺點(diǎn)
- Redis較難支持在線擴(kuò)容,在集群容量達(dá)到上限時(shí)在線擴(kuò)容會(huì)變得很復(fù)雜。
3. Redis-Cluster集群
redis的哨兵模式基本已經(jīng)可以實(shí)現(xiàn)高可用,讀寫分離 ,但是在這種模式下每臺(tái)redis服務(wù)器都存儲(chǔ)相同的數(shù)據(jù),很浪費(fèi)內(nèi)存,所以在redis3.0上加入了cluster模式,實(shí)現(xiàn)的redis的分布式存儲(chǔ),也就是說(shuō)每臺(tái)redis節(jié)點(diǎn)上存儲(chǔ)不同的內(nèi)容。
Redis-Cluster采用無(wú)中心結(jié)構(gòu),它的特點(diǎn)如下:
- 所有的redis節(jié)點(diǎn)彼此互聯(lián)(PING-PONG機(jī)制),內(nèi)部使用二進(jìn)制協(xié)議優(yōu)化傳輸速度和帶寬。
- 節(jié)點(diǎn)的fail是通過(guò)集群中超過(guò)半數(shù)的節(jié)點(diǎn)檢測(cè)失效時(shí)才生效。
- 客戶端與redis節(jié)點(diǎn)直連,不需要中間代理層.客戶端不需要連接集群所有節(jié)點(diǎn),連接集群中任何一個(gè)可用節(jié)點(diǎn)即可。
工作方式
在redis的每一個(gè)節(jié)點(diǎn)上,都有這么兩個(gè)東西,一個(gè)是插槽(slot),它的的取值范圍是:0-16383。還有一個(gè)就是cluster,可以理解為是一個(gè)集群管理的插件。當(dāng)我們的存取的key到達(dá)的時(shí)候,redis會(huì)根據(jù)crc16的算法得出一個(gè)結(jié)果,然后把結(jié)果對(duì) 16384 求余數(shù),這樣每個(gè) key 都會(huì)對(duì)應(yīng)一個(gè)編號(hào)在 0-16383 之間的哈希槽,通過(guò)這個(gè)值,去找到對(duì)應(yīng)的插槽所對(duì)應(yīng)的節(jié)點(diǎn),然后直接自動(dòng)跳轉(zhuǎn)到這個(gè)對(duì)應(yīng)的節(jié)點(diǎn)上進(jìn)行存取操作。
為了保證高可用,redis-cluster集群引入了主從模式,一個(gè)主節(jié)點(diǎn)對(duì)應(yīng)一個(gè)或者多個(gè)從節(jié)點(diǎn),當(dāng)主節(jié)點(diǎn)宕機(jī)的時(shí)候,就會(huì)啟用從節(jié)點(diǎn)。當(dāng)其它主節(jié)點(diǎn)ping一個(gè)主節(jié)點(diǎn)A時(shí),如果半數(shù)以上的主節(jié)點(diǎn)與A通信超時(shí),那么認(rèn)為主節(jié)點(diǎn)A宕機(jī)了。如果主節(jié)點(diǎn)A和它的從節(jié)點(diǎn)A1都宕機(jī)了,那么該集群就無(wú)法再提供服務(wù)了。
文章參考自:
如何保障mysql和redis之間的數(shù)據(jù)一致性
redis如何保證數(shù)據(jù)一致性
如何解決Redis緩存和MySQL數(shù)據(jù)一致性的問(wèn)題
Redis怎么保持緩存與數(shù)據(jù)庫(kù)一致性
redis緩存與數(shù)據(jù)庫(kù)一致性問(wèn)題
緩存與數(shù)據(jù)庫(kù)一致性系列
如何保證Redis和數(shù)據(jù)庫(kù)雙寫一致性
redis的三種集群方式