Redis
基礎
redis為什么這么快 (高)
[!NOTE]
- 最首要的是Redis是純內存操作, 比磁盤要快3個數量級
- 同時在與內存操作中采用了非阻塞I/O多路復用機制來提高并發量
- 并且基于Redis的IO密集型,采用單線程操作, 免去了線程切換開銷
- Redis 內置了多種優化過后的數據結構實現. 如跳表
14分鐘掌握Redis底層原理(IO多路復用、文件描述符、內核空間)_嗶哩嗶哩_bilibili
redis高性能怎么實現的?
性能一般基于兩個方面, 一個是計算操作, 一個是讀寫操作.
-
計算操作, 由于redis是基于內存的, 所以計算操作(命令執行)很快, 然后單線程執行命令比較快. 所以redis的性能瓶頸不在計算操作, 而在于網絡io
-
單線程執行為啥快, 那是因為單線程執行的話你就不需要加鎖來控制了, 鎖是很重的很耗費資源.
-
多線程的話, 也需要線程上下文切換, 這樣的話性能也會降低.
-
讀寫操作, 無非就是網絡io和磁盤io
-
磁盤io優化: rdb持久化時, 會創建一個子進程來生成RDB文件, 這樣可以避免主線程的阻塞. 這也是一種寫入時復制的思想
-
網絡io優化:
-
io多路復用: select epoll, 可以同時監聽多個socket連接請求
-
事件派發機制: 有很多不同性質的socket, redis有不同的handler來處理這些socket事件, redis6.0使用多線程來處理這些handler
Redis常用的數據結構有哪些?(高)
- 5種基礎數據類型:String(字符串)、List(列表)、Set(集合)、Hash(散列)、Zset(有序集合)
- 4種特殊數據類型:HyperLogLogs(基數統計)、Bitmap(位存儲)、geo(地理位置)、stream(消息隊列)
redis每種數據類型的使用場景(高)
-
String: 最常規的 set/get 操作,Value 可以是 String 也可以是數字。一般做一些復雜的計數功能的緩存。
-
Hash: 這里 Value 存放的是結構化的對象,比較方便的就是操作其中的某個字段。如果單純做對象的存儲,那么直接使用string即可,如果需要對對象中的字段做操作,那么用hash.
-
List:list支持兩端存取,不能從中間取。若從一側存取,則是棧。若從異側存取,則是隊列。- 使用 List 的數據結構,可以做簡單的消息隊列的功能。另外,可以利用 Irange 命令,做基于 Redis 的分頁功能,性能極佳,用戶體驗好
-
Set: 因為 Set 堆放的是一堆不重復值的集合。所以可以做全局去重的功能。我們的系統一般都是集群部署,使用 JVM 自帶的 Set 比較麻煩。另外,就是利用交集、并集、差集等操作,交集就可以計算共同喜好,共同關注,共同好友,共同粉絲等功能。差集就可以實現好友推薦,音樂推薦功能.
-
Sorted Set: Sorted Set 多了一個權重參數 Score,集合中的元素能夠按 Score 進行排列。可以做排行榜應用,取 TOP(N) 操作。也可以做優先級任務隊列。
-
geo: 地理位置計算
-
bitmap: 可以用來做布隆過濾器,或者統計簽到次數等
-
HyperLogLog: 統計頁面 UV
-
Stream: 做一個簡單的消息隊列
本地緩存和Redis緩存的區別,優缺點?(低)
本地緩存是指將數據存儲在本地應用程序或服務器上,通常用于加速數據訪問和提高響應速度。本地緩存通常使用內存作為存儲介質,利用內存的高速讀寫特性來提高數據訪問速度。
優點:
- 訪問速度快:本地緩存存儲在本地內存中,訪問速度非常快
- 減輕網絡壓力:本地緩存能夠降低對遠程服務器的訪問次數,從而減輕網絡壓力
- 低延遲:本地緩存位于本地設備上,能夠提供低延遲的訪問速度,適用于對實時性要求較高的應用場景
缺點:
- 可擴展性有限:本地緩存的可擴展性受到硬件資源的限制,無法支持大規模的數據存儲和訪問
分布式緩存(Redis)是指將數據存儲在多個分布式節點上,通過協同工作來提供高性能的數據訪問服務。分布式緩存通常使用集群方式進行部署,利用多臺服務器來分擔數據存儲和訪問的壓力
優點:
-
可擴展性強:分布式緩存的節點可以動態擴展,能夠支持大規模的數據存儲和訪問需求。
-
易于維護:分布式緩存通常采用自動化管理方式,能夠降低維護成本和管理的復雜性。
缺點: -
訪問速度相對較慢:相對于本地緩存,分布式緩存的訪問速度相對較慢,因為數據需要從多個節點進行訪問和協同。但是這也比訪問數據庫要快的多
-
網絡開銷大:分布式緩存需要通過網絡進行數據傳輸和協同操作,相對于本地緩存來說,網絡開銷較大
redis的常見應用場景/你平時會用redis做什么(中)
- 緩存:通過將熱點數據存儲在內存中,可以極大地提高訪問速度,減輕數據庫壓力
- 排行榜:Redis的有序集合結構非常適合用于實現排行榜和排名系統,可以方便地進行數據排序和排名
- 分布式鎖:Redis的特性可以用來實現分布式鎖,確保多個進程或服務之間的數據操作的原子性和一致性
- 計數器:由于Redis的原子操作和高性能,它非常適合用于實現計數器和統計數據的存儲,如網站訪問量統計、點贊數統計等
- 消息隊列:Redis的發布訂閱功能使其作為一個輕量級的消息隊列,可以用來實現發布和訂閱模式。(Redis高版本新增Stream數據結構,可以直接作為一個輕量級MQ使用)
線程模型
Redis 為什么設計成單線程的 (中)
- 多線程處理會涉及到鎖,并且多線程處理會涉及到線程切換而消耗 CPU。采用單線程,避免了不必要的上下文切換和競爭條件
- 其次 CPU 不是 Redis 的瓶頸,Redis 的瓶頸最有可能是機器內存或者網絡帶寬。所以 redis 網絡 io 操作采用了多線程.
Redis一定是單線程的嗎 (中)
- Redis 單線程指的是執行命令是由一個主線程來完成的
- 但是 Redis 在磁盤 io (持久化操作) 和網絡 io 會使用多線程來處理,是因為這些任務的操作都是很耗時的,如果把這些任務都放在主線程來處理,那么 Redis 主線程就很容易發生阻塞,這樣就無法處理后續的請求了
Redis 6.0 之后為什么引入了多線程? (中)
Redis執行命令一直是單線程模型
因為 Redis 的性能瓶頸有時會出現在網絡 I/O 的處理上,故在 Redis 6.0 版本之后,采用了多個 I/O 線程來處理網絡請求,提高網絡 I/O 的并行度
但是對于命令的執行,Redis 仍然使用單線程來處理
內存與持久化
redis的過期策略(高)
每當我們對一個key設置了過期時間時,Redis會把該key帶上過期時間存儲到一個過期字典(expires dict)中,也就是說「過期字典」保存了數據庫中所有key的過期時間
過期策略:定期刪除+惰性刪除
- 定期刪除就是Redis默認每隔100ms就隨機抽取一些設置了過期時間的key,檢測這些key是否過期,如果過期了就將其刪除
- 惰性刪除是在你要獲取某個key的時候,redis會先去檢測一下這個key是否已經過期,如果沒有過期則返回給你,如果已經過期了,那么redis會刪除這個key,不會返回給你
但是,僅僅通過給key設置過期時間還是有問題的。因為還是可能存在定期刪除和惰性刪除漏掉了很多過期key的情況。這樣就導致大量過期key堆積在內存里,然后就Out of memory了。
怎么解決這個問題呢?Redis內存淘汰機制
為什么Key過期不立即刪除?(高)
在過期key比較多的情況下,刪除過期key可能會占用相當一部分CPU時間,在內存不緊張但CPU時間緊張的情況下,將CPU時間用于刪除和當前任務無關的過期鍵上,會對服務器的響應時間和吞吐量造成影響
內存淘汰機制(高)
1#noeviction ?>- >?> 當內存不夠時,不淘汰任何數據,只是拋出異常(一般不使用)
2#yvolatile-lru ?>- >?> 從設置過期時間的數據中,淘汰最近最長時間沒有被使用的數據
3#allkeys-lru ?>- >?> 從所有數據中,淘汰最近最長時間沒有被使用的數據
4#volatile-lfu ?>- >?> 從設置過期時間的數據中,淘汰一段時間內使用次數最少數據
5#allkeys-lfu ?>- >?> 從所有數據中,淘汰一段時間內使用次數最少的數據
6#volatile-random ?>- >?> 從設置過期時間的數據中,隨機淘汰一批數據
7#allkeys-random ?>- >?> 從所有數據中隨機淘汰一批數據
8#volatile-ttl ?>- >?> 淘汰快超時的數據
名詞解釋
- LRU [Least Recently Used]: 最近最久未使用; LRU是淘汰最長時間沒有被使用的數據。
- LFU [Least Frequently Used]: 最近最少使用; LFU是淘汰一段時間內,使用次數最少的數據。
- TTL: time to live, 過期時間
Redis持久化機制(高)
RDB: 按照一定的時間周期策略把內存的數據以快照的形式保存到硬盤的二進制文件。即Snapshot快照存儲,對應產生的數據文件為dump.rdb.
Redis提供了兩個命令來生成RDB文件,分別是save和bgsave,他們的區別就在于是否在「主線程」里執行:
- 執行了save命令,就會在主線程生成RDB文件,由于和執行操作命令在同一個線程,所以如果寫入RDB文件的時間太長,會阻塞主線程;
- 執行了bgsave命令,會創建一個子進程來生成RDB文件,這樣可以避免主線程的阻塞;執行bgsave過程中,Redis依然可以繼續處理操作命令的,也就是數據是能被修改的。寫時復制技術(Copy-On-Write)
AOF: Redis會將每一個收到的寫命令追加到文件最后,類似于MySQL的binlog。當Redis重啟是會通過重新執行文件中保存的寫命令來在內存中重建整個數據庫的內容。
- AOF保存的數據更加完整,但是性能相比RDB稍差
在Redis的配置文件中存在三種不同的AOF持久化方式
1 appendfsync always #每次有數據修改發生時都會寫入AOF文件,這樣會嚴重降低Redis的速度
2 appendfsync everysec #每次寫操作命令執行完后,先將命令寫入到AOF文件的內核緩沖區,然后每隔一秒將緩沖區里的內容寫回到硬盤
3 appendfsync no #讓操作系統決定何時進行同步
-
為了兼顧數據和寫入性能,可以用appendfsync everysec,讓Redis每秒同步一次AOF文件,對Redis性能影響不大.
-
即使出現系統崩潰,用戶最多只會丟失一秒之內產生的數據
混合持久化
Redis 4.0 提出混合使用 AOF 日志和內存快照,也叫混合持久化。
- 當開啟了混合持久化時,在 AOF 重寫日志時,fork 出來的重寫子進程會先將與主線程共享的內存數據以 RDB 方式寫入到 AOF 文件,然后主線程處理的操作命令會被記錄在重寫緩沖區里,重寫緩沖區里的增量命令會以 AOF 方式寫入到 AOF 文件,寫入完成后通知主進程將新的含有 RDB 格式和 AOF 格式的 AOF 文件替換舊的的 AOF 文件。
- 使用了混合持久化,AOF 文件的前半部分是 RDB 格式的全量數據,后半部分是 AOF 格式的增量數據。
- 好處在于,重啟 Redis 加載數據的時候,由于前半部分是 RDB 內容,這樣加載的時候速度會很快
- 加載完 RDB 的內容后,才會加載后半部分的 AOF 內容,這里的內容是 Redis 后臺子進程重寫 AOF 期間,主線程處理的操作命令,可以使得數據更少的丟失。
故一般都會用混合持久化方式
緩存問題(常考)
緩存擊穿 (高)
一個被高并發訪問并且緩存重建業務較復雜的key突然失效了,無數的請求訪問會在瞬間給數據庫帶來巨大的沖擊。
無論是哪種方案,其目標都是保證某個key過期,大量請求訪問它,但是只有一個請求能訪問數據庫,重建緩存。
- 互斥鎖方案:
- 如果 redis 中沒查到數據,緩存未命中。則獲取互斥鎖(setnx),獲取失敗則休眠重試,獲取成功則再次查詢緩存是否存在(雙重檢測),沒查到則查詢數據庫進行緩存重建
也就是說能拿到鎖的線程去做緩存重建,沒拿到鎖的線程阻塞等待。待緩存重建完成,沒拿到鎖的線程直接查redis即可。互斥鎖注重一致性,但是性能較差,需要數據庫與緩存強一致性選擇互斥鎖。
- 邏輯過期方案:
需要在將要緩存的數據中加上時間屬性作為邏輯過期時間,然后在redis中設置永不過期。查詢時從redis中獲取數據,對比邏輯過期時間看否過期,未過期直接返回數據。過期則獲取互斥鎖。獲取鎖失敗則說明已經有人進行緩存重建了,那么直接返回舊數據。獲取鎖成功(需要雙重檢測)則開啟新線程進行緩存重建,原線程直接返回保證效率。也就是說,拿到鎖的線程去做緩存重建,沒拿到鎖的線程返回舊數據(因為redis中沒設置過期時間,所以總能拿到舊數據)。邏輯過期性能好,但是一致性差,需要高性能選擇邏輯過期。
緩存穿透(高)
請求的數據在數據庫和緩存中都不存在,那么請求就會直接訪問數據庫。
方案一:可以緩存空對象。
- 優點是實現簡單,維護方便
- 缺點是空對象會帶來額外的內存消耗,有可能出現短暫的數據不一致問題,當每次的key都不一樣時失效
方案二:使用布隆過濾器。
- 優點是不會有多余key,內存占用少
- 缺點是實現復雜,且有誤判的可能
緩存雪崩 (高)
同一時間大量的key都過期了,或者redis宕機了,導致大量請求訪問數據庫。
- 如果是大量key同一時間過期,則給key添加固定的過期時間的基礎上,再增加一個隨機的過期時間。
- 如果是redis宕機,那么就使用主從或者集群保證可用性。
- 萬一真的發生服務雪崩,應該做好服務限流降級熔斷的處理。
緩存預熱 (中)
緩存預熱就是系統上線后,將相關的緩存數據直接加載到緩存系統。
這樣就可以避免在用戶請求的時候,先查詢數據庫,然后再將數據緩存的問題!用戶直接查詢事先被預熱的緩存數據!
緩存讀寫策略 (高)
1. CacheAside(旁路緩存)策略
核心思想是應用程序直接操作緩存
讀優先讀緩存,緩存中沒有數據,讀數據庫,在由應用程序寫緩存。
- 寫優先更新數據庫,確保數據持久化后,刪除緩存。
我們業務中常見的Redis緩存方案就是旁路緩存。區別于讀穿/寫穿 策略
2. Read/Write Through(讀穿/寫穿)策略
Read/Write Through(讀穿/寫穿)策略Read/Write Through(讀穿/寫穿)策略原則是應用程序只和緩存交互,不再和數據庫交互,而是由緩存和數據庫交互,相當于更新數據庫的操作由緩存自己代理了
Read Through 策略:
- 先查詢緩存中數據是否存在,如果存在則直接返回
- 如果不存在,則由緩存組件負責從數據庫查詢數據,并將結果寫入到緩存組件,最后緩存組件將數據返回給應用
Write Through 策略:
- 當有數據更新的時候,先查詢要寫入的數據在緩存中是否已經存在:
- 如果緩存中數據已經存在,則更新緩存中的數據,并且由緩存組件同步更新到數據庫中,然后緩存組件告知應用程序更新完成。
- 如果緩存中數據不存在,直接更新數據庫,然后返回;
WriteBack(寫回)策略:
在更新數據的時候,只更新緩存,同時將緩存數據設置為臟的,然后立馬返回,并不會更新數據庫。對于數據庫的更新,會通過批量異步更新的方式進行
WriteBack策略特別適合寫多的場景,但是帶來的問題是,數據不是強一致性的,而且會有數據丟失的風險
WriteBack是計算機體系結構中的設計,比如CPU的緩存、操作系統中文件系統的緩存都采用了WriteBack(寫回)策略。電腦在突然斷電之后,之前寫入的文件會有部分丟失,就是因為 Page Cache 還沒有來得及刷盤造成的。
緩存與數據庫一致性的問題(高)
先更新數據庫,后更新緩存(不推薦)
- 操作失敗的情況:
如果數據庫更新成功,緩存更新失敗,那么此時數據庫中是新值,緩存中是舊值。出現不一致
- 高并發的情況:
假設現在有線程A和線程B同時要進行更新操作,那么可能會這樣:
(1)線程A更新了數據庫;(2)線程B更新了數據庫;(3)線程B更新了緩存;(4)線程A更新了緩存;
此時數據庫是線程B更新的值,緩存中是線程A的舊值。出現不一致
先刪除緩存,后更新數據庫(不推薦)
高并發的情況下會出問題
假設同時有一個請求A進行更新操作,另一個請求B進行查詢操作。那么可能會這樣:
(1)請求A進行寫操作,刪除緩存;
(2) 請求B查詢發現緩存不存在;(3) 請求B去數據庫查詢得到舊值;(4) 請求B將舊值寫入緩存;(5) 請求A將新值寫入數據庫;緩存和數據庫不一致
延遲雙刪(一般推薦)
先刪除緩存,再更新數據庫,等過一段時間后再對緩存進行一次刪除,比如5s之后。
讀寫分離場景,可能產生問題如下一個請求A進行更新操作,另一個請求B進行查詢操作。
(1)請求A進行寫操作,刪除緩存;(2)請求A將數據寫入數據庫了;(3)請求B查詢緩存發現,緩存沒有值;(4)請求B去從庫查詢,這時還沒有完成主從同步,因此查詢到的是舊值;(5)請求B將舊值寫入緩存;(6)數據庫完成主從同步,從庫變為新值;出現緩存不一致
延遲刪除緩存的時間到底設置要多久才合適呢?
- 問題1:延遲時間要大于「主從復制」的延遲時間- 問題2:延遲時間要大于線程B讀取數據庫 + 寫入緩存的時間但是,這個時間在分布式和高并發場景下,其實是很難評估的。
很多時候,我們都是憑借經驗大致估算這個延遲時間,例如延遲1- 5s,只能盡可能地降低不一致的概率。
采用這種方案,也只是盡可能保證一致性而已,極端情況下,還是有可能發生不一致。
先更新數據庫,后刪除緩存(配合mq/監聽binlog重試才推薦)
- 操作失敗的情況:
如果數據庫更新成功,緩存刪除失敗。用戶讀取數據時會先從緩存中讀取,而緩存中存儲的是舊的數據。出現不一致
- 高并發的情況:
一個請求A做查詢操作,一個請求B做更新操作。
(1)緩存剛好失效;
(2)請求A查詢數據庫,得一個舊值;
(3)請求B將新值寫入數據庫;
(4)請求B刪除緩存;
(5)請求A將查到的舊值寫入緩存;
這種情況下確實也會產生臟數據,不過這種情況發生的概率是很小的。為什么呢?
- 要出現以上情況,步驟(3)的寫數據庫操作比步驟(2)的讀數據庫操作耗時更短,才有可能使得步驟(4)先于步驟(5)。但是寫操作基本不會快于讀操作,所以出現不一致的概率很低。
那么如何解決由操作失敗導致的不一致性呢?重試!
-
消息隊列重試(推薦):將刪除緩存的操作放入mq中。
-
更新數據庫,發mq消息,mq接受到消息后,異步刪除緩存。刪除失敗可以重試:mq可以保證消息可靠性。
-
消息隊列保證可靠性:寫到隊列中的消息,成功消費之前不會丟失(重啟項目也不擔心)- 消息隊列保證消息成功投遞:下游從隊列拉取消息,成功消費后才會刪除消息,否則還會繼續投遞消息給消費者(符合重試的需求)
-
訂閱binlog日志重試(推薦):監聽binlog來刪除緩存。
-
使用類似于阿里的canal的中間件,監聽mysql binlog。更新數據庫后,監聽到binlog變化,刪除緩存。- 無需考慮寫消息隊列失敗情況:只要寫MySQL成功,Binlog肯定會有- binlog訂閱者(消費者)直接獲取變更的數據,然后刪除緩存。
總結:想要保證數據庫和緩存一致性,推薦采用「先更新數據庫,再刪除緩存」方案,并配合「消息隊列重試」或「監聽binlog重試」的方式來做,但是即便是這樣也不能完全保證一致性,所以完全保證緩存和數據庫一致性是很難的。
分布式鎖 (高)
基本的分布式鎖實現
基于Redis的分布式鎖實現思路:
- 利用setnx獲取鎖,并設置過期時間,保存線程標識- 釋放鎖時先判斷線程標識是否與自己一致,一致則刪除鎖。判斷鎖是否為自己的然后再釋放,這個過程必須是原子性的否則有可能釋放別人的鎖,這就需要lua腳本。
特性:
- 利用setnx滿足互斥性
- -利用setex保證故障時鎖依然能釋放,避免死鎖,提高安全性
- 利用Redis集群保證高可用和高并發特性
釋放錯鎖的問題
- 線程1獲取鎖后阻塞,鎖超時時間一到,鎖自動釋放
- 線程2獲取鎖,開始執行線程2的業務。此時線程1執行完業務釋放鎖,就會釋放掉線程2的鎖。
- 線程3又獲取了鎖,執行業務。此時線程2執行完畢又釋放了線程3的鎖。
因此,釋放鎖之前需要判斷鎖是否是自己的
獲取鎖的值,判斷鎖是否是自己的,釋放鎖,這三步不具有原子性。所以會產生如下問題
-
線程1執行完業務準備釋放鎖,從redis中查到了鎖的值,判斷鎖是自己的。此時,線程1阻塞。然后鎖過期了。
-
線程2獲取鎖, 開始執行業務. 線程1又醒來開始釋放鎖, 此時就釋放了線程2的鎖.
所以釋放鎖的邏輯必須寫在lua腳本中, 保證原子性.
如何實現可重入鎖/redisson可重入鎖原理
由于自己寫的鎖使用了string類型的 setnx, 只要方法1上鎖, 方法1調用的方法二需要再上鎖是無法做到的, 會直接返回false
而redisson的可重入鎖, 使用了hash類型, key為lock, field為線程名, value為數字.
-
加鎖
- 方法1加鎖后, value=1, 方法1調用方法2
- 方法2加鎖, redisson判斷鎖標識是否是自己(field字段是否為同一線程), 若是則value++, value為2
- 同理, 方法3加鎖, value=3
-
釋放鎖
- 方法3執行完畢, 釋放鎖, value–, value=2
- 方法2釋放鎖, value=1
- 方法1釋放鎖, value=0, 此時刪除redis的該數據, 鎖完全釋放.
所以使用hash結構, hsetnx就可以實現可重入鎖
如何實現鎖的可重試
redis的發布訂閱機制實現等待、喚醒, 獲取鎖失敗的重試機制
- 先直接獲取鎖, 如果獲取失敗, 并不是直接重試, 因為現在立即重試大概率其他線程正在執行業務. 獲取鎖失敗會先訂閱一下, 然后等待
- -獲取鎖成功的線程在釋放鎖時會發布一條消息.
- 當其他線程得到該消息時, 就會重新獲取鎖, 如果再次獲取鎖失敗, 就會再次等待.- 但是不是無限制的等待, 因為他會有一個等待時間, 超過該時間則不重試直接返回false
業務線程沒執行完, 鎖超時釋放了怎么辦/如何實現超時續約
超時續約:利用watchDog看門狗機制, 每隔一段時間, 重置鎖的超時時間
- 看門狗機制會創建一個守護線程, 當鎖快到期但是業務線程沒執行完時為鎖增加時間 (續命).
- 當然看門狗也不會無限地增加超時時間, redisson有一個參數用來設置加鎖的時間, 超過這個時間后鎖便自動解開了, 不會延長鎖的有效期
鎖的主從一致性問題
redis為了保證高可用, 使用主從架構. 一主多從
- 獲取鎖時, 往redis主節點setnx. 主節點加鎖成功后宕機, 此時數據未同步到從節點.- 某個從節點成為新主節點. 但是新主節點沒有鎖信息. 此時其他線程還可以加鎖.- 這就產生了主從架構鎖丟失問題.
解決方案:redisson聯鎖機制,使用多個獨立的Redis主節點,多主,或者多主多從
獲取鎖時,往每一個redis主節點都寫入key.即便其中一臺redis宕機,其他redis依舊有鎖信息并且必須在所有節點都獲取到鎖,才算獲取鎖成功Redison分布式聯鎖RedisonMultiLock對象可以將多個RLock鎖對象關聯為一個聯鎖,可以把一組鎖當作一個鎖來加鎖和釋放。
redlock紅鎖
聯鎖機制配合redis多主節點還存在問題:
聯鎖必須在所有節點都獲取到鎖,才算獲取鎖成功.那么當某個redis主節點網絡較慢,就導致加鎖時間會很長.很容易出現加鎖超時失敗的情況如果某個redis主節點宕機,也會加鎖失敗
而redlock規定,只需要半數以上節點加鎖成功,就算這次加鎖成功并且規定了嚴格的加鎖時間,一定時間內無法加鎖成功,則直接返回
避免了某個redis主節點網絡較慢,就導致加鎖時間會很長的問題只需要半數以上節點加鎖成功就可以,避免了頻繁加鎖失敗問題
但是redlock對redis部署的要求很高,需要多主部署,或者部署多個獨立的redis集群.并且redlock復雜度和成本都很高,所以其實redlock并不推薦使用.哈哈哈哈看了這么久以為redlock是最牛逼的最終解決方案,結果并不推薦使用.別生氣,雖然它不推薦使用,但是可以用來給面試官吹牛逼,畢竟很多面試官對分布式鎖的了解都沒有深入到redlock,懂了把
高可用
如何保證Redis服務高可用?(中)
主從或者集群
什么是主從復制 (中)
- 主從復制就是將一臺Redis主節點的數據復制到其他的Redis從節點中,盡最大可能保證Redis主節點和從節點的數據是一致的。
- 主從復制是Redis高可用的基石,Redis Sentinel以及RedisCluster都依賴于主從復制。- 主從復制這種方案不僅保障了Redis服務的高可用,還實現了讀寫分離,提高了系統的并發量,尤其是讀并發量。
主從復制與Sentinel哨兵機制 (中)
- 主從復制方案下,master發生啟機的話可以手動將某一臺slave升級為master,Redis服務可用性提高。
- slave可以分擔讀請求,讀吞吐量大幅提高。
- 但其缺陷也很明顯,一旦master容機,我們需要從slave中手動選擇一個新的master,同時需要修改應用方的主節點地址,還需要命令所有從節點去復制新的主節點,整個過程需要手動處理,很麻煩,很容易出問題。
- redis的Sentinel哨兵機制可以自動幫選主。
Sentinel(哨兵) 有什么作用? (中)
- 監控Redis節點的運行狀態并自動實現故障轉移。
- 當master節點出現故障的時候,Sentinel會自動根據一定的規則選出一個slave升級為master,確保整個Redis系統的可用性。整個過程完全自動,不需要人工介入
Redis緩存的數據量太大怎么辦? (中)
分片集群,數據被分散到集群不同的redis節點上,避免了單個redis緩存的數據量過大問題。
RedisCluster的作用?(中)
高并發場景下,使用Redis主要會遇到的兩個問題:
緩存的數據量太大:實際緩存的數據量可以達到幾十G,甚至是成百上千G;并發量要求太大:Redis號稱單機可以支持10w并發,但是并發超過10w怎么辦?
主從復制和Redis Sentinel這兩種方案本質都是通過增加slave數量的方式來提高Redis服務的整體可用性和讀吞吐量,不支持橫向擴展來緩解寫壓力以及解決緩存數據量過大的問題。
這時候,redis分片集群就可以解決這個問題.通過部署多臺Redis主節點(master),然后將key分散到不同的主節點上,客戶端的請求通過路由規則轉發到目標master上,這就是分片集群.
注意,這些節點之間平等,并沒有主從之說,同時對外提供讀/寫服務。
分片集群很方便拓展,只需要增加redis主節點即可
Redis Cluster 是如何分片的?/Redis Cluster中的數據是如何分布的?/怎么知道key應該在哪個哈希槽中?(中)
- Redis Cluster并沒有使用一致性哈希,采用的是哈希槽分區,每一個鍵值對都屬于一個哈希槽
- Redis Cluster通常有16384個哈希槽,要計算給定key應該分布到哪個哈希槽中,我們只需要先對每個key計算CRC-16(XMODEM)校驗碼,然后再對這個校驗碼對16384(哈希槽的總數)取模,得到的值即是key對應的哈希槽