為什么使用緩存
-
在程序內(nèi)部使用緩存,比如使用map等數(shù)據(jù)結(jié)構(gòu)作為內(nèi)部緩存,可以快速獲取對(duì)象。通過(guò)將經(jīng)常使用的數(shù)據(jù)存儲(chǔ)在緩存中,可以減少對(duì)數(shù)據(jù)庫(kù)的頻繁訪問(wèn),從而提高系統(tǒng)的響應(yīng)速度和性能。緩存可以將數(shù)據(jù)保存在內(nèi)存中,讀取速度更快,能夠大大縮短數(shù)據(jù)訪問(wèn)的時(shí)間,提升用戶體驗(yàn)。
-
在業(yè)界中,通常在數(shù)據(jù)庫(kù)之前添加一層Redis緩存,這樣可以避免數(shù)據(jù)庫(kù)的性能被大量的請(qǐng)求耗費(fèi)。當(dāng)有大量的并發(fā)請(qǐng)求時(shí),數(shù)據(jù)庫(kù)可能會(huì)成為瓶頸,而使用緩存可以有效地緩解數(shù)據(jù)庫(kù)的壓力。Redis作為一種高效的緩存解決方案,可以將熱門(mén)數(shù)據(jù)存儲(chǔ)在內(nèi)存中,以快速響應(yīng)用戶的請(qǐng)求。這種緩存層的引入不僅可以提高系統(tǒng)的性能和吞吐量,還可以提高系統(tǒng)的可靠性和穩(wěn)定性,因?yàn)榧词箶?shù)據(jù)庫(kù)出現(xiàn)故障,緩存仍然可以提供部分服務(wù)。
-
緩存還可以減少網(wǎng)絡(luò)傳輸?shù)呢?fù)載,特別是在分布式系統(tǒng)中。通過(guò)將計(jì)算結(jié)果或頻繁訪問(wèn)的數(shù)據(jù)緩存起來(lái),可以避免重復(fù)計(jì)算和重復(fù)訪問(wèn)數(shù)據(jù)庫(kù),節(jié)省了網(wǎng)絡(luò)帶寬和服務(wù)器的資源消耗。這對(duì)于海量數(shù)據(jù)的查找和計(jì)算密集型任務(wù)尤為重要,可以大大提升系統(tǒng)的效率和可擴(kuò)展性。
總之,使用緩存可以優(yōu)化系統(tǒng)的性能、提高響應(yīng)速度、降低數(shù)據(jù)庫(kù)負(fù)載、節(jié)省網(wǎng)絡(luò)傳輸和服務(wù)器資源,從而提升用戶體驗(yàn)和系統(tǒng)的可靠性。
緩存穿透、擊穿、雪崩
緩存穿透:
緩存穿透指的是當(dāng)一個(gè)請(qǐng)求查詢的數(shù)據(jù)不在緩存中,也不在數(shù)據(jù)庫(kù)中,導(dǎo)致每次請(qǐng)求都直接訪問(wèn)數(shù)據(jù)庫(kù),增加了數(shù)據(jù)庫(kù)的負(fù)載。這可能是由于惡意攻擊或者異常情況導(dǎo)致的。為了解決緩存穿透問(wèn)題,可以采取以下措施:
- 在緩存中存儲(chǔ)一個(gè)空值或者默認(rèn)值,且設(shè)置成一定過(guò)期時(shí)間,以避免重復(fù)的無(wú)效查詢,但是這種方案有缺陷就是redis會(huì)多出無(wú)用的key,浪費(fèi)內(nèi)存資源;
- 使用布隆過(guò)濾器等技術(shù)來(lái)過(guò)濾掉無(wú)效的請(qǐng)求,將可能不存在的數(shù)據(jù)快速過(guò)濾掉,布隆過(guò)濾器可以有效防止不存在的key進(jìn)入業(yè)務(wù)調(diào)用數(shù)據(jù)庫(kù),但是需要提前將數(shù)據(jù)庫(kù)數(shù)據(jù)預(yù)熱到布隆過(guò)濾器中,并且他也有一種缺陷就是由于他的數(shù)據(jù)結(jié)構(gòu)和算法導(dǎo)致無(wú)法刪除熱鍵,只能新增;
緩存擊穿
緩存擊穿指的是當(dāng)某個(gè)熱點(diǎn)數(shù)據(jù)過(guò)期或者被刪除時(shí),大量的請(qǐng)求同時(shí)涌入,導(dǎo)致數(shù)據(jù)庫(kù)負(fù)載過(guò)高。這通常發(fā)生在高并發(fā)環(huán)境下。為了避免緩存擊穿問(wèn)題,可以采取以下措施:
- 第一種就是將熱點(diǎn)數(shù)據(jù)永久緩存進(jìn)redis,并另起一個(gè)線程定時(shí)的去更新這個(gè)熱點(diǎn)數(shù)據(jù),那么就熱點(diǎn)數(shù)據(jù)永遠(yuǎn)不會(huì)失效,但是缺陷是在定時(shí)任務(wù)啟動(dòng)前可能存在數(shù)據(jù)錯(cuò)誤的情況;
- 第二種情況那么就是加鎖,使用互斥鎖或者分布式鎖來(lái)保護(hù)對(duì)數(shù)據(jù)庫(kù)的訪問(wèn),確保只有一個(gè)請(qǐng)求能夠重新加載數(shù)據(jù)到緩存中。但是這種雖然解決了數(shù)據(jù)庫(kù)問(wèn)題,但同時(shí)也帶來(lái)了性能下降;
緩存雪崩
緩存雪崩指的是當(dāng)緩存中大量的數(shù)據(jù)同時(shí)過(guò)期時(shí),導(dǎo)致大量的請(qǐng)求直接訪問(wèn)數(shù)據(jù)庫(kù),造成數(shù)據(jù)庫(kù)負(fù)載過(guò)高。這通常是由于緩存服務(wù)器故障、網(wǎng)絡(luò)故障或者緩存數(shù)據(jù)過(guò)期時(shí)間設(shè)置不合理等原因?qū)е碌?。為了避免緩存雪崩?wèn)題,可以采取以下措施:
- 就是在給緩存數(shù)據(jù)設(shè)置過(guò)期時(shí)間的時(shí)候請(qǐng)加一個(gè)隨機(jī)值使用不同的過(guò)期時(shí)間來(lái)分散緩存的失效時(shí)間,避免大量數(shù)據(jù)同時(shí)過(guò)期。
- 使用熱點(diǎn)數(shù)據(jù)預(yù)加載技術(shù),在緩存數(shù)據(jù)即將過(guò)期之前,提前加載數(shù)據(jù)到緩存中,確保數(shù)據(jù)的可用性。
如何保證緩存與數(shù)據(jù)庫(kù)之間的數(shù)據(jù)一致性
保證緩存與數(shù)據(jù)庫(kù)之間的強(qiáng)一致性是一個(gè)相對(duì)復(fù)雜的問(wèn)題。盡管沒(méi)有絕對(duì)的解決方案,但可以采取一些策略來(lái)盡可能地提高數(shù)據(jù)一致性。以下是幾種常見(jiàn)的策略:
第一種就是先刪除緩存還是先寫(xiě)數(shù)據(jù)庫(kù),這兩種都一樣,我就說(shuō)下先刪除緩存帶來(lái)的問(wèn)題,先刪除緩存確實(shí)可以在寫(xiě)完數(shù)據(jù)庫(kù)后后續(xù)的操作都會(huì)更新緩存值,但是扛不住并發(fā)高,如果刪除完緩存后還沒(méi)來(lái)得及寫(xiě)入又被另一個(gè)線程讀取了舊值更新緩存,那么這緩存白刪除了,
第二種就是先寫(xiě)數(shù)據(jù)庫(kù)呢?如果數(shù)據(jù)庫(kù)寫(xiě)完后,一是在刪除緩存之前的讀操作讀取的仍然是舊值,二是,如果寫(xiě)操作完成后,緩存刪除操作由于網(wǎng)絡(luò)原因丟失了怎么辦,以后讀取操作都是舊值了;
第三種也就是業(yè)界最常用的延時(shí)雙刪;但同時(shí)他也無(wú)法一定保證數(shù)據(jù)的一致性
- 在操作數(shù)據(jù)庫(kù)之前先刪除緩存:首先,你需要先刪除緩存中對(duì)應(yīng)的數(shù)據(jù),確保下一次讀取請(qǐng)求不會(huì)命中舊的緩存數(shù)據(jù)。
- 更新數(shù)據(jù)庫(kù):然后,你可以更新數(shù)據(jù)庫(kù)中的數(shù)據(jù),確保數(shù)據(jù)庫(kù)中的數(shù)據(jù)是最新的。
- 再次刪除緩存:最后,在延時(shí)之后,再次刪除緩存中的數(shù)據(jù)。這樣可以確保在延時(shí)結(jié)束后,讀操作仍然可以從緩存中獲取最新的數(shù)據(jù)。
如果寫(xiě)操作很頻繁,那么缺陷就很明顯:很容易產(chǎn)生臟數(shù)據(jù)并且也無(wú)法滿足緩存與數(shù)據(jù)庫(kù)之間的一致性;
第四種:引入MQ,當(dāng)我們有兩個(gè)消費(fèi)者的時(shí)候,一個(gè)消費(fèi)者只管消息的數(shù)據(jù)庫(kù)操作,一個(gè)消費(fèi)者只管消息的緩存操作,這樣可以確保操作是原子操作。確保了不會(huì)刪除緩存失敗的問(wèn)題。
但是以上四種都無(wú)法保證緩存與數(shù)據(jù)庫(kù)之間的強(qiáng)一致性,只能保證數(shù)據(jù)庫(kù)與緩存之間的最終一致性;
如何設(shè)計(jì)分布式鎖?如何對(duì)鎖性能進(jìn)行優(yōu)化?
首先分布式鎖主要應(yīng)用場(chǎng)景就是應(yīng)對(duì)多節(jié)點(diǎn)部署下如何控制資源的并發(fā)保護(hù),那么單純的jvm鎖已經(jīng)無(wú)法滿足需求,所以引入了分布式鎖,那么常見(jiàn)的有數(shù)據(jù)庫(kù)、zookeeper、redis;通常分布式鎖的要求的就是性能高、與業(yè)務(wù)無(wú)關(guān);設(shè)計(jì)分布式鎖時(shí),常見(jiàn)的選擇是使用Redis作為分布式鎖的存儲(chǔ)介質(zhì)。下面將介紹如何設(shè)計(jì)分布式鎖,并對(duì)鎖性能進(jìn)行優(yōu)化。
首先,我們需要掌握Redis的基本命令:
-
SETNX:設(shè)置鍵值對(duì),如果鍵不存在則返回1,如果鍵已存在則返回0。
-
EXPIRE:設(shè)置鍵的過(guò)期時(shí)間。
-
GETSET:先獲取舊值,然后將新值設(shè)置進(jìn)去;如果鍵不存在,則返回null。
-
DEL:刪除一個(gè)鍵。
接下來(lái),我們將討論幾種常見(jiàn)的分布式鎖設(shè)計(jì)方式:
- 使用SETNX和DEL操作:在當(dāng)前業(yè)務(wù)執(zhí)行完畢后,使用DEL操作刪除鎖。但是如果獲取鎖的進(jìn)程執(zhí)行失敗,它將永遠(yuǎn)不會(huì)主動(dòng)解鎖,導(dǎo)致鎖被死鎖。
- 使用SETNX和EXPIRE操作:這是最常見(jiàn)的分布式鎖設(shè)計(jì)方式。但是存在一個(gè)問(wèn)題,如果在設(shè)置過(guò)期時(shí)間之前節(jié)點(diǎn)掛掉,其他服務(wù)將永遠(yuǎn)無(wú)法獲取到鎖,因?yàn)镾ETNX和EXPIRE不是原子操作。
- 使用SETNX和GETSET操作:在設(shè)置鎖時(shí),將過(guò)期時(shí)間作為值存儲(chǔ)在Redis中。當(dāng)其他線程爭(zhēng)取鎖失敗時(shí),可以通過(guò)GETSET操作檢查當(dāng)前鎖是否已經(jīng)失效。如果鎖已失效,則可以使用自己的過(guò)期時(shí)間來(lái)替換舊的值,并與之前的過(guò)期時(shí)間進(jìn)行比較,以確定是否成功獲取鎖。下面給出偽代碼示例:
public boolean tryLock(RedisConnection conn) {
long nowTime = System.currentTimeMillis();
long expireTime = nowTime + 1000;
if (conn.SETNX("mykey", expireTime) == 1) {
conn.EXPIRE("mykey", 1000);
return true;
} else {
long oldValue = conn.get("mykey");
if (oldValue != null && oldValue < nowTime) {
long currentValue = conn.GETSET("mykey", expireTime);
if (oldValue == currentValue) {
conn.EXPIRE("mykey", 1000);
return true;
}
return false;
}
return false;
}
}
上述代碼實(shí)現(xiàn)了一種比較高效的分布式鎖。然而,上述優(yōu)化的根本問(wèn)題在于SETNX和EXPIRE兩個(gè)指令無(wú)法保證原子性。為此,Redis 2.6版本引入了執(zhí)行Lua腳本的功能,通過(guò)Lua腳本可以保證原子性。Redission工具就是基于此原理提供的分布式鎖工具。
如何設(shè)置過(guò)期時(shí)間,實(shí)現(xiàn)原理是什么?
redis有兩種命令可以進(jìn)行對(duì)key設(shè)置過(guò)期時(shí)間:expire和setex。這兩種命令都可以用來(lái)給key設(shè)置過(guò)期時(shí)間。
實(shí)現(xiàn)過(guò)期時(shí)間的原理可以分為兩個(gè)部分。
首先是主動(dòng)刪除。Redis會(huì)有一個(gè)定時(shí)任務(wù),定期檢查數(shù)據(jù)庫(kù)中的key是否已經(jīng)過(guò)期。如果發(fā)現(xiàn)某個(gè)key已經(jīng)過(guò)期,那么Redis會(huì)直接將其刪除。
其次是被動(dòng)刪除。當(dāng)應(yīng)用程序嘗試獲取一個(gè)已經(jīng)設(shè)置了過(guò)期時(shí)間的key時(shí),Redis會(huì)檢查該key是否已經(jīng)過(guò)期。如果已經(jīng)過(guò)期,Redis會(huì)在返回結(jié)果之前將該key刪除。
這樣,通過(guò)主動(dòng)刪除和被動(dòng)刪除的組合,Redis實(shí)現(xiàn)了對(duì)key的過(guò)期時(shí)間的管理。這種混合實(shí)現(xiàn)的方式可以保證Redis中的數(shù)據(jù)始終是最新的,并且不會(huì)出現(xiàn)過(guò)期的數(shù)據(jù)。
需要注意的是,Redis并不會(huì)為每個(gè)key都啟動(dòng)一個(gè)單獨(dú)的定時(shí)任務(wù)去檢查過(guò)期時(shí)間。相反,Redis會(huì)根據(jù)實(shí)際情況動(dòng)態(tài)調(diào)整定時(shí)任務(wù)的執(zhí)行頻率,以提高性能和效率。這種設(shè)計(jì)可以有效地減少對(duì)系統(tǒng)資源的占用,提高Redis的性能和穩(wěn)定性。
海量數(shù)據(jù)下,如何快速查找一條記錄?
當(dāng)前這道題目考驗(yàn)的是對(duì)redis整體的理解,所以也要全方位考慮,可以考慮以下優(yōu)化策略:
-
使用布隆過(guò)濾器:布隆過(guò)濾器是一種概率型數(shù)據(jù)結(jié)構(gòu),可以用于判斷某個(gè)元素是否存在于集合中。在海量數(shù)據(jù)下,可以先使用布隆過(guò)濾器將不存在的key過(guò)濾掉,這樣可以減少部分請(qǐng)求,提高查詢效率。
-
合理選擇存儲(chǔ)結(jié)構(gòu):在緩存記錄時(shí),可以考慮使用適合的存儲(chǔ)結(jié)構(gòu)。如果存儲(chǔ)的是大對(duì)象,使用key+value(json)形式,那么key可能會(huì)很大,不建議使用。而如果使用hash結(jié)構(gòu)存儲(chǔ),可以充分利用Redis的哈希表特性,提高存儲(chǔ)效率。此外,可以根據(jù)實(shí)際情況選擇其他存儲(chǔ)結(jié)構(gòu),如列表、有序集合等。
-
查詢優(yōu)化:如果Redis是集群部署的,數(shù)據(jù)根據(jù)槽位進(jìn)行分配。如果我們自己對(duì)key進(jìn)行了定位,可以直接訪問(wèn)對(duì)應(yīng)的Redis節(jié)點(diǎn),而不需要通過(guò)集群路由。這樣可以減少Redis集群的機(jī)器計(jì)算,提高查詢性能。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-634619.html
總結(jié)
本文提供了一些保證數(shù)據(jù)一致性和設(shè)計(jì)分布式鎖的策略。這些策略可以在實(shí)際應(yīng)用中幫助開(kāi)發(fā)人員解決相關(guān)的問(wèn)題,確保系統(tǒng)的數(shù)據(jù)一致性和并發(fā)訪問(wèn)的正確性。同時(shí),通過(guò)合理地使用緩存和分布式鎖,可以提高系統(tǒng)的性能和可靠性。希望對(duì)你在面對(duì)Redis相關(guān)面試題時(shí)有所幫助!文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-634619.html
到了這里,關(guān)于緩存面試解析:穿透、擊穿、雪崩,一致性、分布式鎖、Redis過(guò)期,海量數(shù)據(jù)查找的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!