天天看點

面試官Redis這些必問

分布式緩存

緩存好處:高性能 + 高并發

高性能(常用)

面試官Redis這些必問

資料庫查詢耗費了800ms,其他使用者對同一個資料再次查詢 ,假設該資料在10分鐘以内沒有變化過,并且 10 分鐘之内有 1000 個使用者 都查詢了同一資料,10 分鐘之内,那 1000 個使用者,每個人查詢這個資料都感覺很慢 800ms

比如 :某個商品資訊,在 一天之内都不會改變,但是這個商品每次查詢一次都要耗費2s,一天之内被浏覽 100W次

mysql 單機也就 2000qps,緩存單機輕松幾萬幾十萬qps,單機 承載并發量是 mysql 單機的幾十倍。

高并發

面試官Redis這些必問

在中午高峰期,有 100W 個使用者通路系統 A,每秒有 4000 個請求去查詢資料庫,資料庫承載每秒 4000 個請求會當機,加上緩存後,可以 3000 個請求走緩存 ,1000 個請求走資料庫。

緩存是走記憶體的,記憶體天然可以支撐4w/s的請求,資料庫(基于磁盤)一般建議并發請求不要超過 2000/s。

緩存不良後果

  • 緩存與資料庫雙寫不一緻
  • 緩存雪崩
  • 緩存穿透
  • 緩存并發競争

Redis 線程模型

  • redis 單線程 ,memcached 多線程
  • redis 是單線程 nio 異步線程模型

Redis 和 Memcached 差別

  • Redis 支援伺服器端的資料操作:Redis比Memcached來說,擁有更多的資料結構和并支援更豐富的資料操作,通常在Memcached裡,你需要将資料拿到用戶端來進行類似的修改再set回去。這大大增加了網絡 IO 的次數和資料體積。在Redis中,這些複雜的操作通常和一般的GET/SET一樣高效。是以,如果需要緩存能支援更複雜的結構和操作,那麼Redis會是不錯的選擇。
  • 叢集模式:memcached 沒有原生的叢集模式,需要依靠用戶端來實作往叢集中分片寫入資料,但是 Redis 目前是原生支援 cluster模式的。

Redis 單線程模型

一個線程+一個隊列

面試官Redis這些必問

redis 基于 reactor 模式開發了網絡事件處理器,這個處理器叫做檔案事件處理器,file event handler,這個檔案事件處理器是單線程的,是以redis 是單線程的模型,采用 io多路複用機制同時監聽多個 socket,根據socket上的事件來選擇對應的事件處理器來處理這個事件。

檔案事件處理器包含:多個 socket,io多路複用程式,檔案事件分派器,事件處理器(指令請求處理器、指令回複處理器、連接配接應答處理器)

檔案事件處理器是單線程的,通過 io 多路複用機制監聽多個 socket,實作高性能和線程模型簡單性

被監聽的 socket 準備好執行 accept,read,write,close等操作的時候,會産生對應的檔案事件,調用之前關聯好的時間處理器處理

多個 socket并發操作,産生不同的檔案事件,i/o多路複用會監聽多個socket,将這些 socket放入一個隊列中排隊。事件分派器從隊列中取出socket給對應事件處理器。

一個socket時間處理完後,事件分派器才能從隊列中拿到下一個socket,給對應事件處理器來處理。

檔案事件:

AE_READABLE 對應 socket變得可讀(用戶端對redis執行 write操作)

AE_WRITABLE 對應 socket 變得可寫(用戶端對 redis執行 read操作)

I/O 多路複用可以同時監聽AE_REABLE和 AE_WRITABLE ,如果同時達到則優先處理 AE_REABLE 時間

檔案事件處理器:

連接配接應答處理器 對應 用戶端要連接配接 redis

指令請求處理器 對應 用戶端寫資料到 redis

指令回複處理器 對應 用戶端從 redis 讀資料

流程:

  1. redis 初始化時,會将連接配接應答處理器跟 AE_READABLE事件關聯
  2. 用戶端對 redis 發起連接配接,産生一個 AE_READABLE 事件
  3. 連接配接應答處理器處理用戶端 AE_READABLE 事件,建立用戶端對應的 socket,同時将這個 socket的 AE_READABLE 事件和指令請求處理器關聯
  4. 用戶端對 redis 發起讀請求,會在 socket上産生一個 AE_READABLE 事件
  5. 綁定 AE_READABLE 事件的指令請求處理器會從 socket 中讀取請求相關資料,執行對應操作,當執行完畢後,将 socket的 AE_WRITABLE 事件跟指令回複處理器關聯
  6. 當用戶端這邊準備好讀取響應時,會在 socket上産生一個AE_WRITABLE事件
  7. 綁定 AE_WRITABLE 事件的指令回複處理器将準備好的響應資料寫入 socket,供用戶端來讀取
  8. 指令回複處理器寫完後,删掉 socket的 AE_WRITABLE 事件和指令回複處理器的綁定關系

Redis 單線程模型效率高

一秒鐘可以處理幾萬個請求

  1. 非阻塞 I/O 多路複用機制(不處理事件,隻輪詢請求壓入隊列)
  2. 純記憶體操作(操作隻有幾微秒)
  3. 單線程反而 避免了多線程頻繁上下文切換的問題

Redis 資料類型

  • string

普通的 set,get kv緩存

  • hash

類型 map結構,比如一個對象(沒有嵌套對象)緩存到 redis裡面,然後讀寫緩存的時候,可以直接操作hash的字段(比如把 age 改成 21,其他的不變)

key=150

value = {

"id":150,
"name":"zhangsan",
"age":20

}      
  • list

有序清單 ,元素可以重複

可以通過 list 存儲一些清單型資料結構,類似粉絲清單,文章評論清單。

例如:微信大 V的粉絲,可以以 list 的格式放在 redis 裡去緩存

key=某大 V value=[zhangsan,lisi,wangwu]

比如 lrange 可以從某個元素開始讀取多少個元素,可以基于 list 實作分頁查詢功能,基于 redis實作高性能分頁,類似微網誌下來不斷分頁東西。

可以搞個簡單的消息隊列,從 list頭怼進去(lpush),list尾巴出來 (brpop)

  • set

無序集合,自動去重

需要對一些資料快速全局去重,(當然也可以基于 HashSet,但是單機)

基于 set 玩差集、并集、交集的操作。比如:2 個人的粉絲清單整一個交集,看看 2 個人的共同好友是誰?

把 2 個大 V 的粉絲都放在 2 個 set中,對 2 個 set做交集(sinter)

  • sorted set

排序的 set,去重但是可以排序,寫進去的時候給一個分數,自動根據分數排序

排行榜:

  1. 将每個使用者以及其對應的分數寫入進去zadd board score username
  2. zrevrange board 0 99 可以擷取排名前 100 的使用者
  3. zrank board username 可以看到使用者在排行榜裡的排名

例如:

zadd board 85 zhangsan

zadd board 72 wangwu

zadd board 96 lis

zadd board 62 zhaoliu

自動排序為:

96 lisi

85 zhangsan

72 wangwu

62 zhaoliu

擷取排名前 3 的使用者 : zrevrange board 0 3

96 lisi

85 zhangsan

72 wangwu

檢視zhaoliu的排行 :zrank board zhaoliu 傳回 4

Redis 過期政策

記憶體是寶貴的,磁盤是廉價的

給key設定過期時間後,redis對這批key是定期删除+惰性删除

定期删除:

redis 預設每隔 100ms随機抽取一些設定了過期時間的 key,檢查其是否過期了,如果過期就删除。

注意:redis是每隔100ms随機抽取一些 key來檢查和删除,而不是周遊所有的設定過期時間的key(否則CPU 負載會很高,消耗在檢查過期 key 上)

惰性删除:

擷取某個key的時候, redis 會檢查一下,這個key如果設定了過期時間那麼是否過期,如果過期了則删除。

如果定期删除漏掉了許多過期key,然後你也沒及時去查,也沒走惰性删除,如果大量過期的key堆積在記憶體裡,導緻 redis 記憶體塊耗盡,則走記憶體淘汰機制。

記憶體淘汰政策:

  1. noeviction:當記憶體不足以容納新寫入資料時,新寫入操作直接報錯(沒人用)
  2. allkeys-lru: 當記憶體不足以容納新寫入資料時,在鍵空間中,移除最近最少使用的key(最常用)
  3. allkeys-random: 當記憶體不足以容納新寫入資料時,在鍵空間中,随機移除某個 key,(沒人用)
  4. volatile-lru:當記憶體不足以容納新寫入資料時,在設定了過期時間的鍵空間中,移除最近最少使用的key(不合适)
  5. volatile-ttl:當記憶體不足以容納新寫入資料時,在設定了過期時間的鍵空間中,有更早過期時間的 key 優先移除(不合适)

LRU 算法:

package com.mousycoder.mycode;

 import java.util.LinkedHashMap;
 import java.util.Map;

 /**
 * @version 1.0
 * @author: mousycoder
 * @date: 2019/10/31 17:55
 */
 public class LRUCache<K,V> extends LinkedHashMap<K,V> {

 private final int CACHE_SIZE;

 public LRUCache( int cacheSize) {
 super((int)Math.ceil(cacheSize / 0.75) + 1 ,0.75f,true);
 this.CACHE_SIZE = cacheSize;
 }

 @Override
 protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
 return size() > CACHE_SIZE;
 }

 public static void main(String[] args) {
 LRUCache<Integer,Integer> lruCache = new LRUCache<>(10);
 for (int i = 0; i < 15; i++) {
 lruCache.put(i,i);
 }

 Integer integer1 = lruCache.get(0);
 for (Integer integer : lruCache.keySet()) {
 System.out.println(integer);
 }

 }
 }​​      

Redis 高并發和高可用

緩存架構(多級緩存架構、熱點緩存)

redis 高并發瓶頸在單機,讀寫分離,一般是支撐讀高并發,寫請求少,也就 一秒一兩千,大量請求讀,一秒鐘二十萬次。

面試官Redis這些必問

主從架構

面試官Redis這些必問

一主多從,主負責寫,将資料同步複制到其他 slave節點,從節點負責讀,所有讀的請求全部走從節點。主要是解決讀高并發。

主從架構->讀寫分離->支撐10W+讀QPS架構

Redis Replication

面試官Redis這些必問

master->slave 複制,是異步的

核心機制:

  1. redis 采用異步方式複制資料到 slave 節點
  2. 一個 master node是可以配置多個 slave node的
  3. slave node也可以連接配接其他的 slave node
  4. slave node 做複制的時候,是不會 block master node的正常工作
  5. slave node 在做複制的時候,也不會 block對自己的查詢操作,它會用舊的資料集來提供服務。但是複制完成時,需要删除舊資料集,加載新的資料集,這個時候就會暫停對外服務了。
  6. slave node 主要用來進行橫向擴容,做讀寫分離,擴容 slave node 可以提高讀的吞吐量

master持久化對主從架構的意義:

如果開啟了主從架構,一定要開啟 master node的持久化,不然 master當機重新開機資料是空的,一經複制,slave的資料也丢了

主從複制原理:

面試官Redis這些必問

第一次啟動或者斷開重連情況:

  1. 當啟動一個 slave node的時候,它會發送一個 PSYNC 指令給 master node
  2. master 會觸發一次 full resynchronization (如果不是第一次連接配接,master 隻會複制給 slave 部分缺少的資料,從backlog裡找)
  3. master會啟動一個背景線程,開始生成一份 RDB 快照( bgsave,也可以直接在記憶體中建立),同時将從用戶端收到的所有寫指令緩存在記憶體中。RDB 檔案生成完畢之後,master會将這個RDB發送給slave,slave會先寫入本地磁盤,然後再從本地磁盤加載到記憶體中。然後 master會将記憶體中緩存的寫指令發送給 slave,slave也會同步這些資料(slave如果跟 master網絡故障,斷開連接配接,會自動重連,master如果發現有多個 slave 來重新連接配接,僅僅隻會啟動一個 RDB save 操作,用一份資料服務所有 slave node)

正常情況下:

master 來一條資料,就異步給 slave

Redis高可用性

全年 99.99%的時間,都是出于可用的狀态,那麼就可以稱為高可用性

redis 高可用架構叫故障轉移,failover,也可以叫做主備切換,切換的時間不可用,但是整體高可用。

sentinal node(哨兵)

Sentinal

作用:

  1. 叢集監控,負責監控 redis master 和 slave程序是否正常
  2. 消息通知,如果某個 redis 執行個體有故障,那麼哨兵負責發送消息作為報警通知給管理者
  3. 故障轉移,如果 master 挂掉,會自動轉移到 slave
  4. 配置中心,如果故障轉移了,通知 client 用戶端新的 master位址

兩節點哨兵叢集

面試官Redis這些必問

quorum = 1 (代表哨兵最低個數可以嘗試故障轉移,選舉執行的哨兵)

master 當機,隻有 S2 存活,因為 quorum =1 可以嘗試故障轉移,但是沒達到 majority =2 (最低允許執行故障轉移的哨兵存活數)的标準,無法執行故障轉移

三節點哨兵叢集(經典)

面試官Redis這些必問

如果 M1 當機了,S2,S3 認為 master當機,選舉一個執行故障轉移,因為 3 個哨兵的 majority = 2,是以可以執行故障轉移

Redis 主從 + 哨兵

丢資料:

面試官Redis這些必問
  1. master記憶體中資料異步同步到 slave master 就挂掉了,丢掉了 master 記憶體中的資料
面試官Redis這些必問
  1. 腦裂,某個 master 所在機器突然脫離了正常的網絡,其他 slave機器不能連接配接,但是實際上 master還在運作,哨兵認為 master 當機,選舉 slave為master,此時叢集裡有 2 個 master, client還沒來得及切換到新的master,還繼續寫在舊的 master上,資料丢了,此時舊的 master再次恢複,被被作為一個 slave 挂到新的 master 上,自己的資料被清空 (腦裂,大腦一分為 2,同時指揮同一個人)

解決方案:

  1. min-slaves-max-lag 10 (至少一個 slave同步的延遲不能超過 10s) 減少異步複制的資料丢失,發現slave複制資料和 ack延時過長,拒絕寫入,減少同步資料損失。讓client做降級寫到本地磁盤裡和限流,或者先暫存到消息隊列,然後重新發回 master
  2. min-slaves-to-write 1 減少腦裂帶來的資料丢失,最多損失 10 s資料,假設master 不能繼續給 slave發送資料,并且 slave 10s沒給自己的 ack消息,直接拒絕用戶端寫請求,同時 client做降寫到本地磁盤、限流,或者先暫存到消息隊列,然後重新發回 master

哨兵

sdown 主觀當機,哨兵覺得一個 master 當機(ping 超過了 is-master-down-after-milliseconds毫秒數)

odown 客觀當機,quorum數量的哨兵都覺得 master當機

哨兵互相感覺通過 redis的 pub/sub系統,每隔 2 秒往同一個 channel裡發消息(自己的 host,ip,runid),其他哨兵可以消費這個消息

以及同步交換master的監控資訊。

哨兵確定其他slave修改master資訊為新選舉的master

當一個 master被認為 odown && marjority哨兵都同意,那麼某個哨兵會執行主備切換,選舉一個slave成為master(考慮 1. 跟master斷開連接配接的時長 2. slave 優先級 3.複制 offset 4. runid)

選舉算法:

  1. 如果slave跟master斷開連接配接已經超過 down-after-milliseconds * 10 + master當機時間,則放棄
  2. 按 slave 優先級排序 ,slave-priority 越小越靠前
  3. replica offset ,哪個slave複制越多的資料,越靠前
  4. runid 越小,越靠前

quorum 數量哨兵認為odown->選舉一個哨兵切換->獲得 majority哨兵的授權(quorum < majority 需要 majority個哨兵授權,quorum >= majority 需要 quorum 哨兵授權)

第一個選舉出來的哨兵切換失敗了,其他哨兵等待 failover-time之後,重新拿confiuration epoch做為新的version 切換,保證拿到最新配置,用于 configuration傳播(通過 pu/sub消息機制,其他哨兵對比 version 新舊更新 master配置)

Redis 優化方案

高并發:主從架構

高容量:Redis叢集,支援每秒幾十萬的讀寫并發

高可用:主從+哨兵

Redis 持久化

持久化的意義在于故障恢複資料備份(到其他伺服器)+故障恢複(遇到災難,機房斷電,電纜被切)

  • RDB 對 Redis 中的資料執行周期性的持久化。
  • AOF 機制,每條寫指令作為日志,以 append-only模式寫入一個日志檔案總,在 redis重新開機的時候,可以通過回放AOF日志中的寫入指令來重新建構整個資料集

AOF 隻有一個,Redis 中的資料是有一定限量的,記憶體大小是一定的,AOF 是存放寫指令的,當大到一定的時候,AOF 做 rewrite 操作,就會基于當時 redis 記憶體中的資料,來重新構造一個更小的 AOF 檔案,然後将舊的膨脹很大的檔案給删掉,AOF 檔案一直會被限制在和Redis記憶體中一樣的資料。AOF同步間隔比 RDB 小,資料更完整

RDB

優點:

  • RDB 會生成多個資料檔案,每個資料檔案都代表了某一個時刻中 redis 的資料,這種多個資料檔案的方式,非常适合做冷備,可以将這種完整的資料檔案發送到一些遠端的安全存儲上去,RDB 做冷備,生成多個檔案,每個檔案都代表某一個時刻的完整的資料快照,AOF 也可以做冷備,隻有一個檔案,每隔一定時間去 copy一份這個檔案出來。RDB 做冷備,由Redis控制固定時長去生成快照檔案,比較友善。AOF,需要自己寫腳本定時控制。
  • RDB 對 redis對外提供的讀寫服務,影響非常小,可以讓 redis 保持高性能,因為 redis 主程序隻需要 fork一個子程序,讓子程序執行磁盤 IO 操作來進行 RDB 持久化
  • 相對于 AOF 持久化機制來說,直接基于 RDB 資料檔案來重新開機和恢複 redis 程序,更加快速

缺點:

  • 如果想要在 redis故障時,盡可能少丢資料,那麼 RDB 沒有 AOF 好,一般 RDB 資料快照,都是間隔 5 分鐘,或者更長的時候生成一次,這個時候就得接受一旦 redis 程序當機,那麼會丢失最近 5 分鐘資料
  • RDB 每次在 fork子程序來執行 RDB 快早資料檔案生成的時候,如果資料檔案特别大,可能會導緻對用戶端提供的服務暫停數毫秒,甚至數秒(RDB 生成間隔不要太長)

AOF 存放的指令日志,資料恢複的時候,需要回放執行所有指令日志,RDB 就是一份資料檔案,直接加載到記憶體中。

AOF

優點:

  1. 更好保護資料不丢失,背景線程 fsync 操作,最多丢失一秒鐘資料,保證 os cache中的資料寫入磁盤中
  2. AOF 用 append-only 模式,沒有磁盤尋址開銷,寫入性能非常高,檔案不容易損壞。
  3. AOF 日志過大的時候,背景 rewrite log時候,老的日志檔案照常寫入,新的merge後的日志檔案 ready的時候,再交換新老日志檔案
  4. 适合災難性恢複,某人不小心 flushall清空所有資料,隻要背景 rewrite還沒發生,那麼可以立刻拷貝 AOF 檔案,将最後一條 flushall指令給删了,然後再将該 AOF 檔案放回去,可以通過恢複機制,自動恢複所有資料

缺點:

  1. AOF 日志檔案比 RDB 資料快照檔案大
  2. 降低 Redis的寫 QPS
  3. AOF 複雜,Bug多
  4. 資料恢複比較慢

最佳方案

AOF 來保證資料不丢失,RDB 做不同時間的冷備

Redis Cluster

面試官Redis這些必問

支援 N 個 Redis master node,每個 master node挂載多個 slave node

多master + 讀寫分離 + 高可用

資料量很少,高并發 -> replication + sentinal 叢集

海量資料 + 高并發 + 高可用 -> redis cluster

分布式算法

hash算法->一緻性 hash 算法-> redis cluster->hash slot算法

redis cluster :自動對資料進行分片,每個 master 上放一部分資料,提供内置的高可用支援,部分master不可用時,還是可以繼續工作

cluster bus 通過 16379進行通信,故障檢測,配置更新,故障轉移授權,另外一種二進制協定,主要用于節點間進行高效資料交換,占用更少的網絡帶寬和處理時間

hash算法

key進行hash,然後對節點數量取模,最大問題隻有任意一個 master 當機,大量資料就要根據新的節點數取模,會導緻大量緩存失效。

一緻性 hash 算法

面試官Redis這些必問

key進行hash,對應圓環上一個點,順時針尋找距離最近的一個點。保證任何一個 master 當機,隻受 master 當機那台影響,其他節點不受影響,此時會瞬間去查資料庫。

緩存熱點問題:

可能集中在某個 hash區間内的值特别多,那麼會導緻大量的資料都湧入同一個 master 内,造成 master的熱點問題,性能出現瓶頸。

解決方法:

給每個 master 都做了均勻分布的虛拟節點,這樣每個區間内大量資料都會均勻的分布到不同節點内,而不是順時針全部湧入到同一個節點中。

Hash Slot算法

redis cluster 有固定 16384 個 hash slot,對每個key計算 CRC16 值,然後對16384取模,可以擷取 key對應的 hash slot

redis cluster 中每個 master 都會持有部分 slot ,當一台 master 當機時候,會最快速度遷移 hash slot到可用的機器上(隻會短暫的通路不到)

走同一個 hash slot 通過 hash tag實作

Redis Cluster 核心

  1. 基礎通信

通過 gossip 協定通信(小道留言,所有節點都持有一份中繼資料,不同的節點如果出現了中繼資料的變更,就不斷将中繼資料發送給其他節點,讓其他節點也進行中繼資料的變更)

面試官Redis這些必問

叢集中繼資料:包括 hashslot->node之間的映射表關系,master->slave之間的關系,故障的資訊

叢集中繼資料集中式存儲(storm),底層基于zookeeper(分布式協調中間件)叢集所有中繼資料的維護。好處:中繼資料的更新和讀取,時效性好,一旦變更,其他節點立刻可以感覺。缺點:所有中繼資料的更新壓力全部集中在一個地方,可能會導緻中繼資料的存儲有壓力。

goosip: 好處:中繼資料的更新比較分散,有一定的延時,降低了壓力。缺點:更新有延時,叢集的一些操作會滞後。(reshared操作時configuration error)

  1. 10000 端口

自己提供服務的端口号+ 10000 ,每隔一段時間就會往另外幾個節點發送ping消息,同時其他幾點接收到ping之後傳回pong

  1. 交換的資訊

故障資訊,節點的增加和移除, hash slot 資訊

  1. gossip協定

meet:某個節點發送 meet給新加入的節點,讓新節點加入叢集中,然後新節點就會開始于其他節點進行通信

ping:每個節點都會頻繁給其他節點發送ping,其中包含自己的狀态還有自己維護的叢集中繼資料,互相通過ping交換中繼資料

ping:傳回ping和meet,包含自己的狀态和其他資訊

fail:某個節點判斷另一個節點fail之後,就發送 fail 給其他節點,通知其他節點,指定的節點當機了

  1. ping消息

ping 很頻繁,且攜帶中繼資料,會加重網絡負擔

每個節點每秒會執行 10 次 ping,每次選擇 5 個最久沒有通信的其他節點

當如果發現某個節點通信延遲達到了 cluster_node_timeout /2 ,那麼立即發送 ping, 避免資料交換延遲過長,落後時間太長(2 個節點之間 10 分鐘沒有交換資料,整個叢集處于嚴重的中繼資料不一緻的情況)。

每次ping,一個是帶上自己的節點資訊,還有就是帶上1/10其他節點的資訊,發送出去,進行資料交換

至少包含 3 個其他節點資訊,最多包含總節點-2 個其他節點的資訊

  1. JRedis原理
  • 請求重定向

用戶端發送到任意一個redis執行個體發送指令,每個redis執行個體接受到指令後,都會計算key對應的hash slot,如果在本地就本地處理,否則傳回moved給用戶端,讓用戶端進行重定向 (redis-cli -c)

  • hash slot

通過tag指定key對應的slot,同一個 tag 下的 key,都會在一個 hash slot中,比如 set key1:{100} 和 set key2:{100}

  • smart jedis

本地維護一份hashslot->node的映射表。

JedisCluster 初始化的時候,随機選擇一個 node,初始化 hashslot->node 映射表,同時為每個節點建立一個JedisPool連接配接池,每次基于JedisCluster執行操作,首先JedisCluster都會在本地計算key的hashslot,然後再本地映射表中找到對應的節點,如果發現對應的節點傳回moved,那麼利用該節點的中繼資料,更新 hashslot->node映射表(重試超過 5 次報錯)

  • hashslot遷移和ask重定向

hash slot正在遷移,那麼會傳回ask 重定向給jedis,jedis 接受到ask重定向之後,,會重定向到目标節點去執行

  • 高可用性和主備切換原理

判斷節點當機:

如果一個節點認為另外一個節點當機了, 就是pfail,主觀當機

如果多個節點都認為另外一個節點當機了,那麼就是fail,客觀當機(跟哨兵原理一樣)

在cluster-node-timeout内,某個節點一直沒有傳回 pong,那麼就被認為是 pfail

如果一個節點認為某個節點pfail了,那麼會在gossip消息中,ping給其他節點,如果超過半數的節點認為pfail了,那麼就會變成fail。

從節點過濾:

對當機的 mster node ,從其所有的 slave node中,選擇一個切換成 master node

檢查每個 slave node與master node斷開連接配接的時間,如果超過了cluster-node-timeout * cluster-slave-validity-factor,那麼就沒資格切換成 master(和哨兵一緻)

從節點選舉:

每個從節點,根據自己對 master 複制資料的 offset,設定一個選舉時間,offset越大(複制資料越多)的從節點,選舉時間越靠前,所有的 master node 開始投票,給要進行選舉的 slave進行投票,如果大部分 master node(N/2 +1) 都投票給某個從節點,那麼選舉通過,從節點執行主備切換,從節點切換成主節點

總結:和哨兵很像,直接內建了 replication 和 sentinal

緩存雪崩

面試官Redis這些必問

方案:

事前:保證 redis 叢集高可用性 (主從+哨兵或 redis cluster),避免全盤崩潰

事中:本地 ehcache 緩存 + hystrix 限流(保護資料庫) & 降級,避免 MySQL被打死

事後:redis持久化,快速恢複緩存資料,繼續分流高并發請求

限制元件每秒就 2000 個請求通過限流元件進入資料庫,剩餘的 3000 個請求走降級,傳回一些預設 的值,或者友情提示

好處 :

  1. 資料庫絕對不會死,確定了每秒隻會過去 2000 個請求
  2. 隻要資料庫不死,對于使用者來說 2/5的請求可以被處理
  3. 系統沒死,使用者多點幾次可能就刷出來了

緩存穿透

面試官Redis這些必問

4000 個請求攻請求資料庫裡沒有的資料

解決方案:把查資料庫中不存在的資料的值,寫到緩存中,比如:set -999 UNKNOWN

緩存與資料庫雙寫一緻性

  1. cache aside pattern
面試官Redis這些必問

讀的時候,先讀緩存,緩存沒有,就讀資料庫,然後取出資料後放入緩存,同時傳回響應

更新的時候,删除緩存,更新資料庫

為什麼不更新緩存:

更新緩存代價太高(更新 20 次,隻讀 1 次),lazy思想,需要的時候再計算,不需要的時候不計算

  1. 修改資料庫成功,删除緩存失敗,導緻資料庫是新的資料,緩存中是舊的資料

方案:先删除緩存,再修改資料庫

面試官Redis這些必問
面試官Redis這些必問
  1. 修改資料庫還沒修改完,同時又有查詢請求,把舊的資料放到緩存中(高并發,每秒并發讀幾萬,每秒隻要有資料更新請求,就可能出現資料庫+緩存不一緻情況)

方案:寫,讀路由到相同的一個記憶體隊列(唯一辨別,hash,取模)裡,更新和讀操作進行串行化(背景線程異步執行隊列串行化操作),(隊列裡隻放一個更新查詢操作即可,多餘的過濾掉,記憶體隊列裡沒有該資料更新操作,直接傳回 )有該資料更新操作則輪詢取緩存值,逾時取不到緩存值,直接取一次資料庫的舊值

面試官Redis這些必問

TP 99 意思是99%的請求可以在200ms内傳回

注意點:多個商品的更新操作都積壓在一個隊列裡面(太多操作積壓隻能增加機器),導緻讀請求發生大量的逾時,導緻大量的讀請求走資料庫

一秒 500 寫操作,每200ms,100 個寫操作,20 個記憶體隊列,每個隊列積壓 5 個寫操作,一般在20ms完成

Redis 并發競争問題

面試官Redis這些必問

Redis 叢集部署架構

繼續閱讀