前言
文本已收錄至我的GitHub倉庫,歡迎Star:https://github.com/bin392328206/six-finger
種一棵樹最好的時間是十年前,其次是現(xiàn)在
Tips
面試指南系列,很多情況下不會去深挖細(xì)節(jié),是小六六以被面試者的角色去回顧知識的一種方式,所以我默認(rèn)大部分的東西,作為面試官的你,肯定是懂的。
https://www.processon.com/view/link/600ed9e9637689349038b0e4
上面的是腦圖地址
叨絮
這個系列也寫了幾篇了,今天我們來看看redis,前面的可以去github上看看。
然后下面是前面的文章匯總
- 2021-Java后端工程師面試指南-(引言)
- 2021-Java后端工程師面試指南-(Java基礎(chǔ)篇)
- 2021-Java后端工程師面試指南-(并發(fā)-多線程)
- 2021-Java后端工程師面試指南-(JVM)
- 2021-Java后端工程師面試指南-(MySQL)
說說什么是redis吧
Redis是一個開放源代碼(BSD許可)的內(nèi)存中數(shù)據(jù)結(jié)構(gòu)存儲,用作數(shù)據(jù)庫,緩存和消息代理。它支持?jǐn)?shù)據(jù)結(jié)構(gòu),例如字符串,哈希,列表,集合,帶范圍查詢的排序集合,位圖,超日志,帶有半徑查詢和流的地理空間索引。Redis具有內(nèi)置的復(fù)制,Lua腳本,LRU逐出,事務(wù)和不同級別的磁盤持久性,并通過Redis Sentinel和Redis Cluster自動分區(qū)提供了高可用性。
說說Redis有哪些優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
- 性能優(yōu)異, Redis能讀的速度是110000次/s,寫的速度是81000次/s。
- 數(shù)據(jù)持久化,支持AOF和RDB兩種持久化方式。
- 事務(wù),Redis的所有操作都是原子性的,同時Redis還支持對幾個操作合并后的原子性執(zhí)行。
- 結(jié)構(gòu)豐富,除了支持string類型的value外還支持hash、set、zset、list等數(shù)據(jù)結(jié)構(gòu)。
- 主從復(fù)制,主機(jī)會自動將數(shù)據(jù)同步到從機(jī),可以進(jìn)行讀寫分離。
缺點(diǎn)
- 庫容量受到物理內(nèi)存的限制,不能用作海量數(shù)據(jù)的高性能讀寫,因此Redis適合的場景主要局限在較小數(shù)據(jù)量的高性能操作和運(yùn)算上。
- 宕機(jī),宕機(jī)前有部分?jǐn)?shù)據(jù)未能及時同步到從機(jī),切換IP后還會引入數(shù)據(jù)不一致的問題,降低了系統(tǒng)的可用性。
- redis 較難支持在線擴(kuò)容,在集群容量達(dá)到上限時在線擴(kuò)容會變得很復(fù)雜。為避免這一問題,運(yùn)維人員在系統(tǒng)上線時必須確保有足夠的空間,這對資源造成了很大的浪費(fèi)。
說說為啥要用緩存
主要是為了提高系統(tǒng)的吞吐量,應(yīng)對高并發(fā),高性能場景
為什么要用 Redis 而不用 map/guava 做緩存?
- Java實現(xiàn)的Map是本地緩存,如果有多臺實例(機(jī)器)的話,每個實例都需要各自保存一份緩存,緩存不具有一致性
- Redis實現(xiàn)的是分布式緩存,如果有多臺實例(機(jī)器)的話,每個實例都共享一份緩存,緩存具有一致性。
- Java實現(xiàn)的Map不是專業(yè)做緩存的,JVM內(nèi)存太大容易掛掉的。一般用做于容器來存儲臨時數(shù)據(jù),緩存的數(shù)據(jù)隨著JVM銷毀而結(jié)束。Map所存儲的數(shù)據(jù)結(jié)構(gòu),緩存過期機(jī)制等等是需要程序員自己手寫的。
- Redis是專業(yè)做緩存的,可以用幾十個G內(nèi)存來做緩存。Redis一般用作于緩存,可以將緩存數(shù)據(jù)保存在硬盤中,Redis重啟了后可以將其恢復(fù)。原生提供豐富的數(shù)據(jù)結(jié)構(gòu)、緩存過期機(jī)制等等簡單好用的功能。
Redis為什么這么快
1、完全基于內(nèi)存,絕大部分請求是純粹的內(nèi)存操作,非常快速。數(shù)據(jù)存在內(nèi)存中,類似于HashMap,HashMap的優(yōu)勢就是查找和操作的時間復(fù)雜度都是O(1);
2、數(shù)據(jù)結(jié)構(gòu)簡單,對數(shù)據(jù)操作也簡單,Redis中的數(shù)據(jù)結(jié)構(gòu)是專門進(jìn)行設(shè)計的;
3、采用單線程,避免了不必要的上下文切換和競爭條件,也不存在多進(jìn)程或者多線程導(dǎo)致的切換而消耗 CPU,不用去考慮各種鎖的問題,不存在加鎖釋放鎖操作,沒有因為可能出現(xiàn)死鎖而導(dǎo)致的性能消耗(絕大多數(shù)的瓶頸不在cpu)
4、使用多路I/O復(fù)用模型,非阻塞IO;
5、使用底層模型不同,它們之間底層實現(xiàn)方式以及與客戶端之間通信的應(yīng)用協(xié)議不一樣,Redis直接自己構(gòu)建了VM 機(jī)制 ,使用了resp協(xié)議
聊聊resp協(xié)議吧
Redis是Redis序列化協(xié)議,Redis客戶端RESP協(xié)議與Redis服務(wù)器通信。Redis協(xié)議在以下幾點(diǎn)之間做出了折衷:
- 簡單的實現(xiàn)
- 快速地被計算機(jī)解析
- 簡單得可以能被人工解析
其實就是一個二進(jìn)制的序列化協(xié)議,舉幾個簡單的例子哈
在RESP中,某些數(shù)據(jù)的類型取決于第一個字節(jié):
“+”代表簡單字符串Simple Strings
“+”代表錯誤類型
“:”代表整數(shù)
基于這種協(xié)議的話,其實我們可以自己去實現(xiàn)一個redis的客戶端,以后有機(jī)會給大家寫寫。
如果萬一CPU成為你的Redis瓶頸了,或者,你就是不想讓服務(wù)器其他核閑置,那怎么辦?
那也很簡單,你多起幾個Redis進(jìn)程就好了。Redis是key-value數(shù)據(jù)庫,又不是關(guān)系數(shù)據(jù)庫,數(shù)據(jù)之間沒有約束。只要客戶端分清哪些key放在哪個Redis進(jìn)程上就可以了。redis-cluster可以幫你做的更好。
說說 Redis的基本數(shù)據(jù)結(jié)構(gòu)
- String 整數(shù),浮點(diǎn)數(shù)或者字符串
- Set 集合
- Zset 有序集合
- Hash 散列表
- List 列表
那說說有序集合的實現(xiàn)方式是哪種數(shù)據(jù)結(jié)構(gòu)?
跳躍表。
- 當(dāng)數(shù)據(jù)較少時,sorted set是由一個ziplist來實現(xiàn)的。
- 當(dāng)數(shù)據(jù)多的時候,sorted set是由一個dict + 一個skiplist來實現(xiàn)的。簡單來講,dict用來查詢數(shù)據(jù)到分?jǐn)?shù)的對應(yīng)關(guān)系,而skiplist用來根據(jù)分?jǐn)?shù)查詢數(shù)據(jù)(可能是范圍查找)。
說說redis的底層數(shù)據(jù)結(jié)構(gòu)
sds
Redis的字符串,不是 C 語言中的字符串(即以空字符’\0’結(jié)尾的字符數(shù)組),它是自己構(gòu)建了一種名為 簡單動態(tài)字符串(simple dynamic string,SDS)的抽象類型,并將 SDS 作為 Redis的默認(rèn)字符串表示。
1、len 保存了SDS保存字符串的長度
2、buf[] 數(shù)組用來保存字符串的每個元素
3、free j記錄了 buf 數(shù)組中未使用的字節(jié)數(shù)量
鏈表
鏈表是一種常用的數(shù)據(jù)結(jié)構(gòu),C 語言內(nèi)部是沒有內(nèi)置這種數(shù)據(jù)結(jié)構(gòu)的實現(xiàn),所以Redis自己構(gòu)建了鏈表的實現(xiàn)
字典
字典又稱為符號表或者關(guān)聯(lián)數(shù)組、或映射(map),是一種用于保存鍵值對的抽象數(shù)據(jù)結(jié)構(gòu)。字典中的每一個鍵 key 都是唯一的,通過 key 可以對值來進(jìn)行查找或修改。C 語言中沒有內(nèi)置這種數(shù)據(jù)結(jié)構(gòu)的實現(xiàn),所以字典依然是 Redis自己構(gòu)建的。
跳躍表
跳躍表(skiplist)是一種有序數(shù)據(jù)結(jié)構(gòu),它通過在每個節(jié)點(diǎn)中維持多個指向其它節(jié)點(diǎn)的指針,從而達(dá)到快速訪問節(jié)點(diǎn)的目的。具有如下性質(zhì):
1、由很多層結(jié)構(gòu)組成;
2、每一層都是一個有序的鏈表,排列順序為由高層到底層,都至少包含兩個鏈表節(jié)點(diǎn),分別是前面的head節(jié)點(diǎn)和后面的nil節(jié)點(diǎn);
3、最底層的鏈表包含了所有的元素;
4、如果一個元素出現(xiàn)在某一層的鏈表中,那么在該層之下的鏈表也全都會出現(xiàn)(上一層的元素是當(dāng)前層的元素的子集);
5、鏈表中的每個節(jié)點(diǎn)都包含兩個指針,一個指向同一層的下一個鏈表節(jié)點(diǎn),另一個指向下一層的同一個鏈表節(jié)點(diǎn);
壓縮列表
壓縮列表(ziplist)是Redis為了節(jié)省內(nèi)存而開發(fā)的,是由一系列特殊編碼的連續(xù)內(nèi)存塊組成的順序型數(shù)據(jù)結(jié)構(gòu),一個壓縮列表可以包含任意多個節(jié)點(diǎn)(entry),每個節(jié)點(diǎn)可以保存一個字節(jié)數(shù)組或者一個整數(shù)值。
壓縮列表的原理:壓縮列表并不是對數(shù)據(jù)利用某種算法進(jìn)行壓縮,而是將數(shù)據(jù)按照一定規(guī)則編碼在一塊連續(xù)的內(nèi)存區(qū)域,目的是節(jié)省內(nèi)存。
說說緩存雪崩
一個緩存雪崩發(fā)過程
- redis集群大面積故障
- 緩存失效,但依然大量請求訪問緩存服務(wù)redis
- redis大量失效后,大量請求轉(zhuǎn)向到mysql數(shù)據(jù)庫
- mysql的調(diào)用量暴增,很快就扛不住了,甚至直接宕機(jī)
- 由于大量的應(yīng)用服務(wù)依賴mysql和redis的服務(wù),這個時候很快會演變成各服務(wù)器集群的雪崩,最后網(wǎng)站徹底崩潰。
如何解決緩存雪崩
第一種方案: 緩存層設(shè)計成高可用,防止緩存大面積故障。即使個別節(jié)點(diǎn)、個別機(jī)器、甚至是機(jī)房宕掉,依然可以提供服務(wù),例如 Redis Sentinel 和 Redis Cluster 都實現(xiàn)了高可用。
第二種方案:在批量往Redis存數(shù)據(jù)的時候,把每個Key的失效時間都加個隨機(jī)值就好了,這樣可以保證數(shù)據(jù)不會在同一時間大面積失效,我相信,Redis這點(diǎn)流量還是頂?shù)米〉摹?/p>
那你聊聊緩存擊穿
我個人理解 擊穿 就是正面剛 比如我是矛 你是盾 我直接把你的盾擊穿, 就是比如 幾個熱點(diǎn)Key 同時幾百萬并發(fā)直接把redis 干掉了, 然后數(shù)據(jù)全部打到數(shù)據(jù)庫的情況,或者是redis的這幾個熱點(diǎn)數(shù)據(jù)失效的情景下,同時全部的并發(fā)查這個熱數(shù)據(jù),導(dǎo)致最后打到數(shù)據(jù)庫的情況 這個就是緩存擊穿。
如何解決緩存擊穿
還是分布式鎖 哈哈 因為分布式鎖能控制到數(shù)據(jù)庫的最后一到防線
redis做集群 哨兵
正常來說一般系統(tǒng)的qps都有一個峰值,一般我們使用能抗住這個峰值的內(nèi)存去做這個緩存
那你說說緩存穿透
緩存穿透是指緩存和數(shù)據(jù)庫中都沒有的數(shù)據(jù),而用戶不斷發(fā)起請求,如發(fā)起為id為“-1”的數(shù)據(jù)或id為特別大不存在的數(shù)據(jù)。這時的用戶很可能是攻擊者,攻擊會導(dǎo)致數(shù)據(jù)庫壓力過大。
如何解決緩存穿透
第一種方案 和上面的雙重鎖一樣 如果是拿到數(shù)據(jù)庫為空 那么就給這個key 設(shè)置一個null值 時間設(shè)置短一點(diǎn) 30s, 這樣下次并發(fā)進(jìn)來就不會說把數(shù)據(jù)打到我們的數(shù)據(jù)庫上了
還有就是我們寫代碼的時候 要對一些非法的請求參數(shù)校驗 我相信大家都是這樣做的。
第二種方案 采用我們第一篇中學(xué)到的一個高級用法 bitMap,查詢的時候先查bitmap確定是否含有這個key
說說你是怎么解決緩存一致性問題的
幾種方式緩存不一致的原因和解決方案
方案一 先更新數(shù)據(jù)庫,再刪緩存
這個方案的問題是什么呢? 就是假設(shè)我們更新數(shù)據(jù)成功了 然后去刪除緩存的時候失敗了 這就導(dǎo)致了緩存中是老數(shù)據(jù),會造成緩存不一致
那我們就要保證刪除一定要成功,我們可以在最后刪除的時候 多刪除幾次,第二個就是用一個中間件canal 去兼聽mysql的binlog 然后 從binlong中解析出要刪除的字段 然后 繼續(xù)上面第一個的方式(這個方式的好處 全程也算是異步的跟業(yè)務(wù)代碼是沒有關(guān)系的)
方案二 先更新數(shù)據(jù)庫,再更新緩存
這個操作 問題更多感覺 首先 更新數(shù)據(jù)成功 更新緩存失敗,或者是開始更新數(shù)據(jù)庫成功 然后更新緩存成功 然后事務(wù)回滾,也是緩存不一致。
方案三 刪除緩存 再更新數(shù)據(jù)庫
看起來好像最好 我反正是刪除緩存了 就算更新失敗 下次去讀也是最新的數(shù)據(jù)(一切看起來很美好),其實不然,試想2個并發(fā)一個更新 一個查詢 你先更新的時候 刪除了緩存 但是此時 查詢發(fā)現(xiàn)沒有緩存 然后吧數(shù)據(jù)緩存到了數(shù)據(jù)庫 就會去查數(shù)據(jù)庫 但是此時更新的又更新成功,最后就會再很長的一個時間內(nèi) 緩存和數(shù)據(jù)庫是不一致的,所以這種是方案是不可取的
綜上所訴,我覺得最好的方式先查再刪除 然后再配合訂閱binlong 來做多重刪除的方式是不錯的,可能我接觸的不是很多,希望各位大佬有更好的方式提出
說說Redis的淘汰策略
- noeviction:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時,新寫入操作會報錯。
- allkeys-lru:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時,在鍵空間中,移除最近最少使用的key。
- allkeys-random:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時,在鍵空間中,隨機(jī)移除某個key。
- volatile-lru:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時,在設(shè)置了過期時間的鍵空間中,移除最近最少使用的key。
- volatile-random:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時,在設(shè)置了過期時間的鍵空間中,隨機(jī)移除某個key。
- volatile-ttl:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時,在設(shè)置了過期時間的鍵空間中,有更早過期時間的key優(yōu)先移除。
其實我覺得用volatile-lru就好了 畢竟報錯是完全沒有必要的 還有就是設(shè)置一個報警裝置 如果不夠了 就搞主從 哈哈
聊聊redis的持久化策略
Redis為持久化提供了兩種方式:
- RDB:在指定的時間間隔能對你的數(shù)據(jù)進(jìn)行快照存儲。
- AOF:記錄每次對服務(wù)器寫的操作,當(dāng)服務(wù)器重啟的時候會重新執(zhí)行這些命令來恢復(fù)原始的數(shù)據(jù)。
聊聊RDB
rdb是默認(rèn)的持久化方式,打個比方,你可以設(shè)置比如 90s內(nèi) 有一次寫入,就持久化一次,30s內(nèi)5次寫入就持久化一次等等,當(dāng)然如果你想要禁用RDB配置,也是非常容易的,只需要在save的最后一行寫上:save ""。
在Redis中RDB持久化的觸發(fā)分為兩種:自己手動觸發(fā)與Redis定時觸發(fā)。
針對RDB方式的持久化,手動觸發(fā)可以使用:
- save:會阻塞當(dāng)前Redis服務(wù)器,直到持久化完成,線上應(yīng)該禁止使用。
- bgsave:該觸發(fā)方式會fork一個子進(jìn)程,由子進(jìn)程負(fù)責(zé)持久化過程,因此阻塞只會發(fā)生在fork子進(jìn)程的時候。
說說AOF
appendonly yes 首先是要開啟aof。
appendfsync everysec 它其實有三種模式:
- always:把每個寫命令都立即同步到aof,很慢,但是很安全
- everysec:每秒同步一次,是折中方案(默認(rèn)也是這個)
- no:redis不處理交給OS來處理,非常快,但是也最不安全
AOF的整個流程大體來看可以分為兩步,一步是命令的實時寫入(如果是 appendfsync everysec 配置,會有1s損耗),第二步是對aof文件的重寫。
對于增量追加到文件這一步主要的流程是:命令寫入=》追加到aof_buf =》同步到aof磁盤。那么這里為什么要先寫入buf在同步到磁盤呢?如果實時寫入磁盤會帶來非常高的磁盤IO,影響整體性能。
如何恢復(fù)redis的數(shù)據(jù)呢
啟動時會先檢查AOF文件是否存在,如果不存在就嘗試加載RDB。那么為什么會優(yōu)先加載AOF呢?因為AOF保存的數(shù)據(jù)更完整,通過上面的分析我們知道AOF基本上最多損失1s的數(shù)據(jù)。
說說持久化的性能實戰(zhàn)
一些線上經(jīng)驗
- 如果Redis中的數(shù)據(jù)并不是特別敏感或者可以通過其它方式重寫生成數(shù)據(jù),可以關(guān)閉持久化,如果丟失數(shù)據(jù)可以通過其它途徑補(bǔ)回;
- 自己制定策略定期檢查Redis的情況,然后可以手動觸發(fā)備份、重寫數(shù)據(jù);
- 可以加入主從機(jī)器,利用一臺從機(jī)器進(jìn)行備份處理,其它機(jī)器正常響應(yīng)客戶端的命令;
聊聊Redis中的Master-Slave模式
主從架構(gòu)的特點(diǎn)
- 主服務(wù)器負(fù)責(zé)接收寫請求
- 從服務(wù)器負(fù)責(zé)接收讀請求
- 從服務(wù)器的數(shù)據(jù)由主服務(wù)器復(fù)制過去。主從服務(wù)器的數(shù)據(jù)是一致的
主從架構(gòu)的好處
- 讀寫分離(主服務(wù)器負(fù)責(zé)寫,從服務(wù)器負(fù)責(zé)讀)
- 高可用(某一臺從服務(wù)器掛了,其他從服務(wù)器還能繼續(xù)接收請求,不影響服務(wù))
- 處理更多的并發(fā)量(每臺從服務(wù)器都可以接收讀請求,讀QPS就上去了)
說說主從同步唄
主從架構(gòu)的特點(diǎn)之一:主服務(wù)器和從服務(wù)器的數(shù)據(jù)是一致的。
主從同步的2種情況
完整的同步
- 從服務(wù)器向主服務(wù)器發(fā)送PSYNC命令
- 收到PSYNC命令的主服務(wù)器執(zhí)行BGSAVE命令,在后臺生成一個RDB文件。并用一個緩沖區(qū)來記錄從現(xiàn)在開始執(zhí)行的所有寫命令。
- 當(dāng)主服務(wù)器的BGSAVE命令執(zhí)行完后,將生成的RDB文件發(fā)送給從服務(wù)器,從服務(wù)器接收和載入RBD文件。將自己的數(shù)據(jù)庫狀態(tài)更新至與主服務(wù)器執(zhí)行BGSAVE命令時的狀態(tài)。
- 主服務(wù)器將所有緩沖區(qū)的寫命令發(fā)送給從服務(wù)器,從服務(wù)器執(zhí)行這些寫命令,達(dá)到數(shù)據(jù)最終一致性。
部分重同步
- 主從服務(wù)器的復(fù)制偏移量 主服務(wù)器每次傳播N個字節(jié),就將自己的復(fù)制偏移量加上N
- 從服務(wù)器每次收到主服務(wù)器的N個字節(jié),就將自己的復(fù)制偏移量加上N
- 通過對比主從復(fù)制的偏移量,就很容易知道主從服務(wù)器的數(shù)據(jù)是否處于一致性的狀態(tài)!
那你說說redis的高可用方案唄
Redis 一般以主/從方式部署(這里討論的應(yīng)用從實例主要用于備份,主實例提供讀寫)該方式要實現(xiàn) HA 主要有如下幾種方案:
- keepalived: 通過 keepalived 的虛擬 IP,提供主從的統(tǒng)一訪問,在主出現(xiàn)問題時, 通過 keepalived 運(yùn)行腳本將從提升為主,待主恢復(fù)后先同步后自動變?yōu)橹?,該方案的好處是主從切換后,應(yīng)用程序不需要知道(因為訪問的虛擬 IP 不變),壞處是引入 keepalived 增加部署復(fù)雜性,在有些情況下會導(dǎo)致數(shù)據(jù)丟失
- zookeeper: 通過 zookeeper 來監(jiān)控主從實例, 維護(hù)最新有效的 IP, 應(yīng)用通過 zookeeper 取得 IP,對 Redis 進(jìn)行訪問,該方案需要編寫大量的監(jiān)控代碼
- sentinel: 通過 Sentinel 監(jiān)控主從實例,自動進(jìn)行故障恢復(fù),該方案有個缺陷:因為主從實例地址( IP & PORT )是不同的,當(dāng)故障發(fā)生進(jìn)行主從切換后,應(yīng)用程序無法知道新地址,故在 Jedis2.2.2 中新增了對 Sentinel 的支持,應(yīng)用通過 redis.clients.jedis.JedisSentinelPool.getResource() 取得的 Jedis 實例會及時更新到新的主實例地址
那你說說Redis哈希槽的概念? 一致性hash和哈希槽的概念和區(qū)別
這個問題其實就是問 再集群環(huán)境下, redis 不同的key 存儲到哪個節(jié)點(diǎn)的問題 ,
Redis 集群中內(nèi)置了 16384 個哈希槽,當(dāng)需要在 Redis 集群中放置一個 key-value
時,redis 先對 key 使用 crc16 算法算出一個結(jié)果,然后把結(jié)果對 16384 求余數(shù),
這樣每個 key 都會對應(yīng)一個編號在 0-16383 之間的哈希槽,redis 會根據(jù)節(jié)點(diǎn)數(shù)量大
致均等的將哈希槽映射到不同的節(jié)點(diǎn)。
Redis Cluster是自己做的crc16的簡單hash算法,沒有用一致性hash。Redis的作者認(rèn)為它的crc16(key) mod 16384的效果已經(jīng)不錯了,雖然沒有一致性hash靈活,但實現(xiàn)很簡單,節(jié)點(diǎn)增刪時處理起來也很方便。
聊聊分布式鎖
這個話題基本上是分布式系統(tǒng)開發(fā)的一個必問的題目了
問你分布式鎖是怎么實現(xiàn)的,然后大家可能就搭用它的 set NX EX命令 然后用lua腳本做成一個原子性操作來實現(xiàn)分布式鎖。其實這么搭也可以吧,然后我們一般在生產(chǎn)環(huán)境的話,可能會用一些開源框架,你不如說Redisson來實現(xiàn)分布式鎖。
聊聊Redisson是怎么實現(xiàn)分布式鎖的
- 第一步先嘗試去加鎖,返回過期時間,如果為空則可以獲得鎖 (返回獲取鎖成功)(,在lua腳本里面會判斷你的key和value是不是已經(jīng)持有鎖了,如果是,就是給你重試次數(shù)加,然后獲取鎖也是失敗)
- 如果第一次加鎖失敗之后,就會去判斷你最大等待時間,如果走到這的時候已經(jīng)超過最大等待時間(直接返回獲取鎖失敗,)
- 接下來就是說我要去訂閱redis解鎖這個事件,一旦有人把鎖釋放就會繼續(xù)通知所有的線程去競爭鎖(減少cpu的損耗)
- 然后是一個死循環(huán)的去獲取鎖,當(dāng)時每次執(zhí)行這個循環(huán)的時候,每次去獲取鎖之前都要去判斷當(dāng)前是否已經(jīng)超過最大的等待時間,如果超過了就直接釋放鎖。只有當(dāng)獲得鎖,或者是最大的等待時間超過之后才會返回是否成功獲取鎖的標(biāo)志。(里面也是需要被通知才繼續(xù)循環(huán))
- 通過 Redisson 實現(xiàn)分布式可重入鎖,比純自己通過set key value px milliseconds nx +lua 實現(xiàn)(實現(xiàn)一)的效果更好些,雖然基本原理都一樣,因為通過分析源碼可知,RedissonLock
- 是可重入的,并且考慮了失敗重試,可以設(shè)置鎖的最大等待時間, 在實現(xiàn)上也做了一些優(yōu)化,減少了無效的鎖申請,提升了資源的利用率。
結(jié)束
redis就這些吧,接下來復(fù)習(xí)下es吧
日常求贊
好了各位,以上就是這篇文章的全部內(nèi)容了,能看到這里的人呀,都是真粉。
創(chuàng)作不易,各位的支持和認(rèn)可,就是我創(chuàng)作的最大動力,我們下篇文章見
微信 搜 "六脈神劍的程序人生" 回復(fù)888 有我找的許多的資料送給大家