面試官:Redis 這些我必問

分佈式緩存

緩存好處:高性能 + 高併發java

高性能(經常使用)


數據庫查詢耗費了800ms,其餘用戶對同一個數據再次查詢 ,假設該數據在10分鐘之內沒有變化過,而且 10 分鐘以內有 1000 個用戶 都查詢了同一數據,10 分鐘以內,那 1000 個用戶,每一個人查詢這個數據都感受很慢 800ms
好比 :某個商品信息,在 一天以內都不會改變,可是這個商品每次查詢一次都要耗費2s,一天以內被瀏覽 100W次
mysql 單機也就 2000qps,緩存單機輕鬆幾萬幾十萬qps,單機 承載併發量是 mysql 單機的幾十倍。node

高併發


在中午高峯期,有 100W 個用戶訪問系統 A,每秒有 4000 個請求去查詢數據庫,數據庫承載每秒 4000 個請求會宕機,加上緩存後,能夠 3000 個請求走緩存 ,1000 個請求走數據庫。
緩存是走內存的,內存自然能夠支撐4w/s的請求,數據庫(基於磁盤)通常建議併發請求不要超過 2000/smysql

緩存不良後果

  1. 緩存與數據庫雙寫不一致
  2. 緩存雪崩
  3. 緩存穿透
  4. 緩存併發競爭

Redis 線程模型

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

Redis 和 Memcached 區別

  1. Redis 支持服務器端的數據操做:Redis比Memcached來講,擁有更多的數據結構和並支持更豐富的數據操做,一般在Memcached裏,你須要將數據拿到客戶端來進行相似的修改再set回去。這大大增長了網絡 IO 的次數和數據體積。在Redis中,這些複雜的操做一般和通常的GET/SET同樣高效。因此,若是須要緩存能支持更復雜的結構和操做,那麼Redis會是不錯的選擇
  2. 集羣模式: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 讀數據sql

流程:數據庫

  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 單線程模型效率高

一秒鐘能夠處理幾萬個請求segmentfault

  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

  1. zrevrange board 0 99 能夠獲取排名前 100 的用戶
  2. 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 高併發瓶頸在單機,讀寫分離,通常是支撐讀高併發,寫請求少,也就 一秒一兩千,大量請求讀,一秒鐘二十萬次。

主從架構


一主多從,主負責寫,將數據同步複製到其餘 slave節點,從節點負責讀,全部讀的請求所有走從節點。主要是解決讀高併發。、
主從架構->讀寫分離->支撐10W+讀QPS架構

Redis Replication


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的數據也丟了

主從複製原理:

第一次啓動或者斷開重連狀況:

  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地址

兩節點哨兵集羣


quorum = 1 (表明哨兵最低個數能夠嘗試故障轉移,選舉執行的哨兵)
master 宕機,只有 S2 存活,由於 quorum =1 能夠嘗試故障轉移,可是沒達到 majority =2 (最低容許執行故障轉移的哨兵存活數)的標準,沒法執行故障轉移

三節點哨兵集羣(經典)


若是 M1 宕機了,S2,S3 認爲 master宕機,選舉一個執行故障轉移,由於 3 個哨兵的 majority = 2,因此能夠執行故障轉移

Redis 主從 + 哨兵

丟數據:

  1. master內存中數據異步同步到 slave master 就掛掉了,丟掉了 master 內存中的數據

  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


支持 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 算法


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 協議通訊(小道留言,全部節點都持有一份元數據,不一樣的節點若是出現了元數據的變動,就不斷將元數據發送給其餘節點,讓其餘節點也進行元數據的變動)

集羣元數據:包括 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 cluster),避免全盤崩潰
事中:本地 ehcache 緩存 + hystrix 限流(保護數據庫) & 降級,避免 MySQL被打死
過後: redis持久化,快速恢復緩存數據,繼續分流高併發請求

限制組件每秒就 2000 個請求經過限流組件進入數據庫,剩餘的 3000 個請求走降級,返回一些默認 的值,或者友情提示
好處 :

  1. 數據庫絕對不會死,確保了每秒只會過去 2000 個請求
  2. 只要數據庫不死,對於用戶來講 2/5的請求能夠被處理
  3. 系統沒死,用戶多點幾回可能就刷出來了

緩存穿透


4000 個請求黑客攻擊請求數據庫裏沒有的數據
解決方案:把黑客查數據庫中不存在的數據的值,寫到緩存中,好比: set -999 UNKNOWN

緩存與數據庫雙寫一致性

  1. cache aside pattern


讀的時候,先讀緩存,緩存沒有,就讀數據庫,而後取出數據後放入緩存,同時返回響應
更新的時候,刪除緩存,更新數據庫
爲何不更新緩存:
更新緩存代價過高(更新 20 次,只讀 1 次),lazy思想,須要的時候再計算,不須要的時候不計算

  1. 修改數據庫成功,刪除緩存失敗,致使數據庫是新的數據,緩存中是舊的數據

方案:先刪除緩存,再修改數據庫

  1. 修改數據庫還沒修改完,同時又有查詢請求,把舊的數據放到緩存中(高併發,每秒併發讀幾萬,每秒只要有數據更新請求,就可能出現數據庫+緩存不一致狀況)

方案:寫,讀路由到相同的一個內存隊列(惟一標識,hash,取模)裏,更新和讀操做進行串行化(後臺線程異步執行隊列串行化操做),(隊列裏只放一個更新查詢操做便可,多餘的過濾掉,內存隊列裏沒有該數據更新操做,直接返回 )有該數據更新操做則輪詢取緩存值,超時取不到緩存值,直接取一次數據庫的舊值

TP 99 意思是99%的請求能夠在200ms內返回
注意點:多個商品的更新操做都積壓在一個隊列裏面(太多操做積壓只能增長機器),致使讀請求發生大量的超時,致使大量的讀請求走數據庫
一秒 500 寫操做,每200ms,100 個寫操做,20 個內存隊列,每一個隊列積壓 5 個寫操做,通常在20ms完成

Redis 併發競爭問題


方案:分佈式鎖 + 時間戳比較

Redis 集羣部署架構

10臺機器,5 主 5 從,每一個節點QPS 5W ,一共 25W QPS(Redis cluster 32G + 8 核 ,Redis 進程不超過 10G)總內存 50g,每條數據10kb,10W 條數據1g,200W 條數據 20G,佔用總內存不到50%,目前高峯期 3500 QPS

本文由博客一文多發平臺 OpenWrite 發佈!
相關文章
相關標籤/搜索