摘要:豐富的特性具有豐富的特性,比如可以用作分布式鎖可以持久化數(shù)據(jù)可以用作消息隊(duì)列排行榜計(jì)數(shù)器還支持通知過期等等。比如利用布隆過濾器,內(nèi)部維護(hù)一系列合法有效的,迅速判斷出請(qǐng)求所攜帶的是否合法有效。
場(chǎng)景:Redis面試
(圖片來源于網(wǎng)絡(luò))
面試官: 我看到你的簡(jiǎn)歷上說你熟練使用Redis,那么你講一下Redis是干嘛用的?小明: (心中竊喜,Redis不就是緩存嗎?)Redis主要用作緩存,通過內(nèi)存高效地存儲(chǔ)非持久化數(shù)據(jù)。
面試官: Redis可以用作持久化的存儲(chǔ)嗎?
小明 :嗯...應(yīng)該可以吧...
面試官: 那Redis怎么進(jìn)行持久化操作呢?
小明:嗯...不是太清楚。
面試官: Redis的內(nèi)存淘汰機(jī)制有哪些?
小明:嗯...沒了解過
面試官:我們還可以用Redis做哪些事情?分別利用了Redis的哪個(gè)指令?
小明:我只知道Redis還可以做分布式鎖、消息隊(duì)列...
面試官:好了,我們進(jìn)入下一個(gè)話題...
思考:很明顯,小明同學(xué)在面試過程中關(guān)于Redis的表現(xiàn)和回答肯定是比較失敗的。Redis是我們工作中每天都會(huì)使用到的東西,為什么一到面試卻變成了丟分項(xiàng)呢?
作為開發(fā)者,我們習(xí)慣了使用大神們已經(jīng)封裝好的東西,以此保障我們能夠更專注于業(yè)務(wù)開發(fā),卻不知道這些常用工具的底層實(shí)現(xiàn)是什么,因此盡管平時(shí)應(yīng)用起來得心應(yīng)手,但一到面試還是無法讓面試官眼前一亮。
本文總結(jié)了一些Redis的知識(shí)點(diǎn),有原理有應(yīng)用,希望可以幫助到大家。
一、Redis是什么REmote DIctionary Server(Redis) 是一個(gè)由Salvatore Sanfilippo寫的key-value存儲(chǔ)系統(tǒng)。Redis是一個(gè)開源的使用ANSI 、C語言編寫、遵守BSD協(xié)議、支持網(wǎng)絡(luò)、可基于內(nèi)存亦可持久化的日志型、Key-Value數(shù)據(jù)庫,并提供多種語言的API。
這里我引用了Redis教程里對(duì)Redis的描述,很官方,但是很標(biāo)準(zhǔn)。
**可基于內(nèi)存亦可持久化的日志型、Key-Value數(shù)據(jù)庫。**
我認(rèn)為這個(gè)描述很貼切很全面。
1.1 Redis的行業(yè)地位Redis是互聯(lián)網(wǎng)技術(shù)領(lǐng)域使用最為廣泛的存儲(chǔ)中間件,因超高的性能、完美的文檔、多方面的應(yīng)用能力以及豐富完善的客戶端支持在存儲(chǔ)方面獨(dú)當(dāng)一面,廣受好評(píng),尤其以其性能和讀取速度而成為了領(lǐng)域中最受青睞的中間件。基本上每一個(gè)軟件公司都會(huì)使用Redis,其中包括很多大型互聯(lián)網(wǎng)公司,比如京東、阿里、騰訊、github等。因此,Redis也成為了后端開發(fā)人員必不可少的技能。
1.2 知識(shí)圖譜在我看來,學(xué)習(xí)每一項(xiàng)技術(shù),都需要有一個(gè)清晰的脈絡(luò)和結(jié)構(gòu),不然你也不知道自己會(huì)了哪些、還有多少?zèng)]學(xué)會(huì)。就像一本書,如果沒有目錄章節(jié),也就失去了靈魂。
因此我試圖總結(jié)出Redis的知識(shí)圖譜,也稱為腦圖,如下圖所示,可能知識(shí)點(diǎn)不是很全,后續(xù)會(huì)不斷更新補(bǔ)充。
本系列文章的知識(shí)點(diǎn)也會(huì)和這個(gè)腦圖基本一致,本文先介紹Redis的基本知識(shí),后續(xù)文章會(huì)詳細(xì)介紹Redis的數(shù)據(jù)結(jié)構(gòu)、應(yīng)用、持久化等多個(gè)方面。
二、Redis優(yōu)點(diǎn) 2.1 速度快作為緩存工具,Redis最廣為人知的特點(diǎn)就是快,到底有多快呢?Redis單機(jī)qps(每秒的并發(fā))可以達(dá)到110000次/s,寫的速度是81000次/s。
那么,Redis為什么這么快呢?
絕大部分請(qǐng)求是純粹的內(nèi)存操作,非??焖?;
使用了很多查找操作都特別快的數(shù)據(jù)結(jié)構(gòu)進(jìn)行數(shù)據(jù)存儲(chǔ),Redis中的數(shù)據(jù)結(jié)構(gòu)是專門設(shè)計(jì)的。如HashMap,查找、插入的時(shí)間復(fù)雜度都是O(1);
采用單線程,避免了不必要的上下文切換和競(jìng)爭(zhēng)條件,也不存在多進(jìn)程或者多線程導(dǎo)致的切換而消耗CPU,不用去考慮各種鎖的問題,不存在加鎖、釋放鎖操作,沒有因?yàn)榭赡艹霈F(xiàn)死鎖而導(dǎo)致的性能消耗;
用到了非阻塞I/O多路復(fù)用機(jī)制。
2.2 豐富的數(shù)據(jù)類型Redis有5種常用的數(shù)據(jù)類型:String、List、Hash、set、zset,每種數(shù)據(jù)類型都有自己的用處。
2.3 原子性,支持事務(wù)Redis支持事務(wù),并且它的所有操作都是原子性的,同時(shí)Redis還支持對(duì)幾個(gè)操作合并后的原子性執(zhí)行。
2.4 豐富的特性Redis具有豐富的特性,比如可以用作分布式鎖;可以持久化數(shù)據(jù);可以用作消息隊(duì)列、排行榜、計(jì)數(shù)器;還支持publish/subscribe、通知、key過期等等。當(dāng)我們要用中間件來解決實(shí)際問題的時(shí)候,Redis總能發(fā)揮出自己的用處。
三、Redis和Memcache對(duì)比Memcache和Redis都是優(yōu)秀的、高性能的內(nèi)存數(shù)據(jù)庫,一般我們說到Redis的時(shí)候,都會(huì)拿Memcache來和Redis做對(duì)比。(為什么要做對(duì)比呢?當(dāng)然是要襯托出Redis有多好,沒有對(duì)比,就沒有傷害~)對(duì)比的方面包括:
3.1 存儲(chǔ)方式Memcache把數(shù)據(jù)全部存在內(nèi)存之中,斷電后會(huì)掛掉,無法做到數(shù)據(jù)的持久化,且數(shù)據(jù)不能超過內(nèi)存大小。
Redis有一部分?jǐn)?shù)據(jù)存在硬盤上,可以做到數(shù)據(jù)的持久性。
3.2 數(shù)據(jù)支持類型Memcache對(duì)數(shù)據(jù)類型支持相對(duì)簡(jiǎn)單,只支持String類型的數(shù)據(jù)結(jié)構(gòu)。
Redis有豐富的數(shù)據(jù)類型,包括:String、List、Hash、Set、Zset。
3.3 使用的底層模型它們之間底層實(shí)現(xiàn)方式以及與客戶端之間通信的應(yīng)用協(xié)議不一樣。
Redis直接自己構(gòu)建了VM機(jī)制 ,因?yàn)橐话愕南到y(tǒng)調(diào)用系統(tǒng)函數(shù),會(huì)浪費(fèi)一定的時(shí)間去移動(dòng)和請(qǐng)求。
3.4 存儲(chǔ)值大小Redis最大可以存儲(chǔ)1GB,而memcache只有1MB。
看到這里,會(huì)不會(huì)覺得Redis特別好,全是優(yōu)點(diǎn),完美無缺?其實(shí)Redis還是有很多缺點(diǎn)的,這些缺點(diǎn)平常我們?cè)撊绾慰朔兀?/p> 四、Redis存在的問題及解決方案 4.1 緩存數(shù)據(jù)庫的雙寫一致性的問題
問題:一致性的問題是分布式系統(tǒng)中很常見的問題。一致性一般分為兩種:強(qiáng)一致性和最終一致性,當(dāng)我們要滿足強(qiáng)一致性的時(shí)候,Redis也無法做到完美無瑕,因?yàn)閿?shù)據(jù)庫和緩存雙寫,肯定會(huì)出現(xiàn)不一致的情況,Redis只能保證最終一致性。
解決:我們?nèi)绾伪WC最終一致性呢?
第一種方式是給緩存設(shè)置一定的過期時(shí)間,在緩存過期之后會(huì)自動(dòng)查詢數(shù)據(jù)庫,保證數(shù)據(jù)庫和緩存的一致性。
如果不設(shè)置過期時(shí)間的話,我們首先要選取正確的更新策略:先更新數(shù)據(jù)庫再刪除緩存。但我們刪除緩存的時(shí)候也可能出現(xiàn)某些問題,所以需要將要?jiǎng)h除的緩存的key放到消息隊(duì)列中去,不斷重試,直到刪除成功為止。
4.2 緩存雪崩問題問題: 我們應(yīng)該都在電影里看到過雪崩,開始很平靜,然后一瞬間就開始崩塌,具有很強(qiáng)的毀滅性。這里也是一樣的,我們執(zhí)行代碼的時(shí)候?qū)⒑芏嗑彺娴膶?shí)效時(shí)間設(shè)定成一樣,接著這些緩存在同一時(shí)間都會(huì)實(shí)效,然后都會(huì)重新訪問數(shù)據(jù)庫更新數(shù)據(jù),這樣會(huì)導(dǎo)致數(shù)據(jù)庫連接數(shù)過多、壓力過大而崩潰。
解決:
設(shè)置緩存過期時(shí)間的時(shí)候加一個(gè)隨機(jī)值。
設(shè)置雙緩存,緩存1設(shè)置緩存時(shí)間,緩存2不設(shè)置,1過期后直接返回緩存2,并且啟動(dòng)一個(gè)進(jìn)程去更新緩存1和2。
4.3 緩存穿透問題問題: 緩存穿透是指一些非正常用戶(黑客)故意去請(qǐng)求緩存中不存在的數(shù)據(jù),導(dǎo)致所有的請(qǐng)求都集中到到數(shù)據(jù)庫上,從而導(dǎo)致數(shù)據(jù)庫連接異常。
解決:
利用互斥鎖。緩存失效的時(shí)候,不能直接訪問數(shù)據(jù)庫,而是要先獲取到鎖,才能去請(qǐng)求數(shù)據(jù)庫。沒得到鎖,則休眠一段時(shí)間后重試。
采用異步更新策略。無論key是否取到值,都直接返回。value值中維護(hù)一個(gè)緩存失效時(shí)間,緩存如果過期,異步起一個(gè)線程去讀數(shù)據(jù)庫,更新緩存。需要做緩存預(yù)熱(項(xiàng)目啟動(dòng)前,先加載緩存)操作。
提供一個(gè)能迅速判斷請(qǐng)求是否有效的攔截機(jī)制。比如利用布隆過濾器,內(nèi)部維護(hù)一系列合法有效的key,迅速判斷出請(qǐng)求所攜帶的Key是否合法有效。如果不合法,則直接返回。
4.4 緩存的并發(fā)競(jìng)爭(zhēng)問題問題:
緩存并發(fā)競(jìng)爭(zhēng)的問題,主要發(fā)生在多線程對(duì)某個(gè)key進(jìn)行set的時(shí)候,這時(shí)會(huì)出現(xiàn)數(shù)據(jù)不一致的情況。
比如Redis中我們存著一個(gè)key為amount的值,它的value是100,兩個(gè)線程同時(shí)都對(duì)value加100然后更新,正確的結(jié)果應(yīng)該是變?yōu)?00。但是兩個(gè)線程拿到這個(gè)值的時(shí)候都是100,最后結(jié)果也就是200,這就導(dǎo)致了緩存的并發(fā)競(jìng)爭(zhēng)問題。
解決
如果多線程操作沒有順序要求的話,我們可以設(shè)置一個(gè)分布式鎖,然后多個(gè)線程去爭(zhēng)奪鎖,誰先搶到鎖誰就可以先執(zhí)行。這個(gè)分布式鎖可以用zookeeper或者Redis本身去實(shí)現(xiàn)。
可以利用Redis的incr命令。
當(dāng)我們的多線程操作需要順序的時(shí)候,我們可以設(shè)置一個(gè)消息隊(duì)列,把需要的操作加到消息隊(duì)列中去,嚴(yán)格按照隊(duì)列的先后執(zhí)行命令。
五、Redis的過期策略Redis隨著數(shù)據(jù)的增多,內(nèi)存占用率會(huì)持續(xù)變高,我們以為一些鍵到達(dá)設(shè)置的刪除時(shí)間就會(huì)被刪除,但是時(shí)間到了,內(nèi)存的占用率還是很高,這是為什么呢?
Redis采用的是定期刪除和惰性刪除的內(nèi)存淘汰機(jī)制。
5.1 定期刪除定期刪除和定時(shí)刪除是有區(qū)別的:
定時(shí)刪除是必須嚴(yán)格按照設(shè)定的時(shí)間去刪除緩存,這就需要我們?cè)O(shè)置一個(gè)定時(shí)器去不斷地輪詢所有的key,判斷是否需要進(jìn)行刪除。但是這樣的話cpu的資源會(huì)被大幅度地占據(jù),資源的利用率變低。所以我們選擇采用定期刪除,。
定期刪除是時(shí)間由我們定,我們可以每隔100ms進(jìn)行檢查,但還是不能檢查所有的緩存,Redis還是會(huì)卡死,只能隨機(jī)地去檢查一部分緩存,但是這樣會(huì)有一些緩存無法在規(guī)定時(shí)間內(nèi)刪除。這時(shí)惰性刪除就派上用場(chǎng)了。
5.2 惰性刪除舉個(gè)簡(jiǎn)單的例子:中學(xué)的時(shí)候,平時(shí)作業(yè)太多,根本做不完,老師說下節(jié)課要講這個(gè)卷子,你們都做完了吧?其實(shí)有很多人沒做完,所以需要在下節(jié)課之前趕緊補(bǔ)上。
惰性刪除也是這個(gè)道理,我們的這個(gè)值按理說應(yīng)該沒了,但是它還在,當(dāng)你要獲取這個(gè)key的時(shí)候,發(fā)現(xiàn)這個(gè)key應(yīng)該過期了,趕緊刪了,然后返回一個(gè)"沒有這個(gè)值,已經(jīng)過期了!"。
現(xiàn)在我們有了定期刪除 + 惰性刪除的過期策略,就可以高枕無憂了嗎?并不是這樣的,如果這個(gè)key一直不訪問,那么它會(huì)一直滯留,也是不合理的,這就需要我們的內(nèi)存淘汰機(jī)制了。
5.3 Redis的內(nèi)存淘汰機(jī)制Redis的內(nèi)存淘汰機(jī)制一般有6種,如下圖所示:
那么我們?nèi)绾稳ヅ渲肦edis的內(nèi)存淘汰機(jī)制呢?
在Redis.conf中我們可以進(jìn)行配置
# maxmemory-policy allkeys-lru六、小結(jié)
本文初探Redis,大概整理出了Redis的知識(shí)圖譜,對(duì)照之下可以發(fā)現(xiàn)Redis居然有這么多的知識(shí)點(diǎn)需要學(xué)習(xí);接著我們分析了Redis的優(yōu)缺點(diǎn),知道了其基于內(nèi)存的高效的讀寫速度和豐富的數(shù)據(jù)類型,也分析了Redis面對(duì)數(shù)據(jù)一致性、緩存穿透、緩存雪崩等問題時(shí)該如何處理;最后我們了解了Redis的過期策略和緩存淘汰機(jī)制。
相信大家已經(jīng)對(duì)Redis有了一些了解,下篇文章我們將分析Redis的數(shù)據(jù)結(jié)構(gòu)、每一種數(shù)據(jù)類型是如何實(shí)現(xiàn)的、對(duì)應(yīng)的命令有哪些。
作者:楊亨
來源:宜信技術(shù)學(xué)院
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/18000.html
摘要:豐富的特性具有豐富的特性,比如可以用作分布式鎖可以持久化數(shù)據(jù)可以用作消息隊(duì)列排行榜計(jì)數(shù)器還支持通知過期等等。比如利用布隆過濾器,內(nèi)部維護(hù)一系列合法有效的,迅速判斷出請(qǐng)求所攜帶的是否合法有效。 showImg(https://segmentfault.com/img/remote/1460000019070847?w=750&h=300); 場(chǎng)景:Redis面試 showImg(http...
閱讀 1538·2021-09-22 15:35
閱讀 2017·2021-09-14 18:04
閱讀 891·2019-08-30 15:55
閱讀 2460·2019-08-30 15:53
閱讀 2689·2019-08-30 12:45
閱讀 1210·2019-08-29 17:01
閱讀 2588·2019-08-29 15:30
閱讀 3523·2019-08-29 15:09