面試官:Redis 這些我必問

分布式緩存

緩存好處:高性能 + 高并發(fā)

高性能(常用)


數(shù)據(jù)庫查詢耗費了800ms,其他用戶對同一個數(shù)據(jù)再次查詢 嚷堡,假設該數(shù)據(jù)在10分鐘以內(nèi)沒有變化過厘灼,并且 10 分鐘之內(nèi)有 1000 個用戶 都查詢了同一數(shù)據(jù),10 分鐘之內(nèi)栖忠,那 1000 個用戶崔挖,每個人查詢這個數(shù)據(jù)都感覺很慢 800ms
比如 :某個商品信息,在 一天之內(nèi)都不會改變庵寞,但是這個商品每次查詢一次都要耗費2s狸相,一天之內(nèi)被瀏覽 100W次
mysql 單機也就 2000qps,緩存單機輕松幾萬幾十萬qps,單機 承載并發(fā)量是 mysql 單機的幾十倍。

高并發(fā)


在中午高峰期捐川,有 100W 個用戶訪問系統(tǒng) A脓鹃,每秒有 4000 個請求去查詢數(shù)據(jù)庫,數(shù)據(jù)庫承載每秒 4000 個請求會宕機古沥,加上緩存后瘸右,可以 3000 個請求走緩存 ,1000 個請求走數(shù)據(jù)庫岩齿。
緩存是走內(nèi)存的太颤,內(nèi)存天然可以支撐4w/s的請求,數(shù)據(jù)庫(基于磁盤)一般建議并發(fā)請求不要超過 2000/s

緩存不良后果

  1. 緩存與數(shù)據(jù)庫雙寫不一致
  2. 緩存雪崩
  3. 緩存穿透
  4. 緩存并發(fā)競爭

Redis 線程模型

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

Redis 和 Memcached 區(qū)別

  1. Redis 支持服務器端的數(shù)據(jù)操作:Redis比Memcached來說龄章,擁有更多的數(shù)據(jù)結(jié)構(gòu)和并支持更豐富的數(shù)據(jù)操作,通常在Memcached里乞封,你需要將數(shù)據(jù)拿到客戶端來進行類似的修改再set回去做裙。這大大增加了網(wǎng)絡 IO 的次數(shù)和數(shù)據(jù)體積。在Redis中肃晚,這些復雜的操作通常和一般的GET/SET一樣高效菇用。所以,如果需要緩存能支持更復雜的結(jié)構(gòu)和操作陷揪,那么Redis會是不錯的選擇
  2. 集群模式:memcached 沒有原生的集群模式惋鸥,需要依靠客戶端來實現(xiàn)往集群中分片寫入數(shù)據(jù)杂穷,但是 Redis 目前是原生支持 cluster模式的

Redis 單線程模型

一個線程+一個隊列


redis 基于 reactor 模式開發(fā)了網(wǎng)絡事件處理器操软,這個處理器叫做文件事件處理器奢啥,file event handler,這個文件事件處理器是單線程的缩擂,所以redis 是單線程的模型滤港,采用 io多路復用機制同時監(jiān)聽多個 socket,根據(jù)socket上的事件來選擇對應的事件處理器來處理這個事件廊蜒。
文件事件處理器包含:多個 socket,io多路復用程序,文件事件分派器溅漾,事件處理器(命令請求處理器山叮、命令回復處理器、連接應答處理器)
文件事件處理器是單線程的添履,通過 io 多路復用機制監(jiān)聽多個 socket屁倔,實現(xiàn)高性能和線程模型簡單性
被監(jiān)聽的 socket 準備好執(zhí)行 accept,read,write,close等操作的時候,會產(chǎn)生對應的文件事件暮胧,調(diào)用之前關(guān)聯(lián)好的時間處理器處理
多個 socket并發(fā)操作锐借,產(chǎn)生不同的文件事件,i/o多路復用會監(jiān)聽多個socket往衷,將這些 socket放入一個隊列中排隊钞翔。事件分派器從隊列中取出socket給對應事件處理器。
一個socket時間處理完后席舍,事件分派器才能從隊列中拿到下一個socket布轿,給對應事件處理器來處理。

文件事件:
AE_READABLE 對應 socket變得可讀(客戶端對redis執(zhí)行 write操作)
AE_WRITABLE 對應 socket 變得可寫(客戶端對 redis執(zhí)行 read操作)
I/O 多路復用可以同時監(jiān)聽AE_REABLE和 AE_WRITABLE 来颤,如果同時達到則優(yōu)先處理 AE_REABLE 時間
文件事件處理器:
連接應答處理器 對應 客戶端要連接 redis
命令請求處理器 對應 客戶端寫數(shù)據(jù)到 redis
命令回復處理器 對應 客戶端從 redis 讀數(shù)據(jù)

流程:

  1. redis 初始化時驮捍,會將連接應答處理器跟 AE_READABLE事件關(guān)聯(lián)
  2. 客戶端對 redis 發(fā)起連接,產(chǎn)生一個 AE_READABLE 事件
  3. 連接應答處理器處理客戶端 AE_READABLE 事件脚曾,創(chuàng)建客戶端對應的 socket东且,同時將這個 socket的 AE_READABLE 事件和命令請求處理器關(guān)聯(lián)
  4. 客戶端對 redis 發(fā)起讀請求,會在 socket上產(chǎn)生一個 AE_READABLE 事件
  5. 綁定 AE_READABLE 事件的命令請求處理器會從 socket 中讀取請求相關(guān)數(shù)據(jù)本讥,執(zhí)行對應操作珊泳,當執(zhí)行完畢后,將 socket的 AE_WRITABLE 事件跟命令回復處理器關(guān)聯(lián)
  6. 當客戶端這邊準備好讀取響應時拷沸,會在 socket上產(chǎn)生一個AE_WRITABLE事件
  7. 綁定 AE_WRITABLE 事件的命令回復處理器將準備好的響應數(shù)據(jù)寫入 socket色查,供客戶端來讀取
  8. 命令回復處理器寫完后,刪掉 socket的 AE_WRITABLE 事件和命令回復處理器的綁定關(guān)系

Redis 單線程模型效率高

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

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

Redis 數(shù)據(jù)類型

  • string
    普通的 set,get kv緩存
  • hash
    類型 map結(jié)構(gòu)秧了,比如一個對象(沒有嵌套對象)緩存到 redis里面,然后讀寫緩存的時候序无,可以直接操作hash的字段(比如把 age 改成 21验毡,其他的不變)
    key=150
    value = {
    "id":150,
    "name":"zhangsan",
    "age":20
    }
  • list
    有序列表 衡创,元素可以重復
    可以通過 list 存儲一些列表型數(shù)據(jù)結(jié)構(gòu),類似粉絲列表晶通,文章評論列表璃氢。
    例如:微信大 V的粉絲,可以以 list 的格式放在 redis 里去緩存
    key=某大 V value=[zhangsan,lisi,wangwu]
    比如 lrange 可以從某個元素開始讀取多少個元素狮辽,可以基于 list 實現(xiàn)分頁查詢功能一也,基于 redis實現(xiàn)高性能分頁,類似微博下來不斷分頁東西喉脖。
    可以搞個簡單的消息隊列椰苟,從 list頭懟進去(lpush),list尾巴出來 (brpop)
  • set
    無序集合树叽,自動去重
    需要對一些數(shù)據(jù)快速全局去重舆蝴,(當然也可以基于 HashSet,但是單機)
    基于 set 玩差集菱皆、并集、交集的操作挨稿。比如:2 個人的粉絲列表整一個交集仇轻,看看 2 個人的共同好友是誰?
    把 2 個大 V 的粉絲都放在 2 個 set中奶甘,對 2 個 set做交集(sinter)
  • sorted set
    排序的 set篷店,去重但是可以排序,寫進去的時候給一個分數(shù)臭家,自動根據(jù)分數(shù)排序

排行榜:

  1. 將每個用戶以及其對應的分數(shù)寫入進去
    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 過期策略

內(nèi)存是寶貴的疲陕,磁盤是廉價的
給key設置過期時間后,redis對這批key是定期刪除+惰性刪除
定期刪除:
redis 默認每隔 100ms隨機抽取一些設置了過期時間的 key钉赁,檢查其是否過期了蹄殃,如果過期就刪除。
注意:redis是每隔100ms隨機抽取一些 key來檢查和刪除你踩,而不是遍歷所有的設置過期時間的key(否則CPU 負載會很高诅岩,消耗在檢查過期 key 上)
惰性刪除:
獲取某個key的時候, redis 會檢查一下带膜,這個key如果設置了過期時間那么是否過期吩谦,如果過期了則刪除。
如果定期刪除漏掉了許多過期key膝藕,然后你也沒及時去查式廷,也沒走惰性刪除,如果大量過期的key堆積在內(nèi)存里芭挽,導致 redis 內(nèi)存塊耗盡滑废,則走內(nèi)存淘汰機制蝗肪。

內(nèi)存淘汰策略:

  1. noeviction:當內(nèi)存不足以容納新寫入數(shù)據(jù)時,新寫入操作直接報錯(沒人用)
  2. allkeys-lru: 當內(nèi)存不足以容納新寫入數(shù)據(jù)時策严,在鍵空間中穗慕,移除最近最少使用的key(最常用)
  3. allkeys-random: 當內(nèi)存不足以容納新寫入數(shù)據(jù)時,在鍵空間中妻导,隨機移除某個 key逛绵,(沒人用)
  4. volatile-lru:當內(nèi)存不足以容納新寫入數(shù)據(jù)時,在設置了過期時間的鍵空間中倔韭,移除最近最少使用的key(不合適)
  5. volatile-ttl:當內(nèi)存不足以容納新寫入數(shù)據(jù)時术浪,在設置了過期時間的鍵空間中,有更早過期時間的 key 優(yōu)先移除(不合適)

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 高并發(fā)和高可用

緩存架構(gòu)(多級緩存架構(gòu)寿酌、熱點緩存)
redis 高并發(fā)瓶頸在單機胰苏,讀寫分離,一般是支撐讀高并發(fā)醇疼,寫請求少硕并,也就 一秒一兩千,大量請求讀秧荆,一秒鐘二十萬次倔毙。


主從架構(gòu)


一主多從,主負責寫乙濒,將數(shù)據(jù)同步復制到其他 slave節(jié)點陕赃,從節(jié)點負責讀,所有讀的請求全部走從節(jié)點颁股。主要是解決讀高并發(fā)么库。、
主從架構(gòu)->讀寫分離->支撐10W+讀QPS架構(gòu)

Redis Replication


master->slave 復制甘有,是異步的
核心機制:

  1. redis 采用異步方式復制數(shù)據(jù)到 slave 節(jié)點
  2. 一個 master node是可以配置多個 slave node的
  3. slave node也可以連接其他的 slave node
  4. slave node 做復制的時候诉儒,是不會 block master node的正常工作
  5. slave node 在做復制的時候,也不會 block對自己的查詢操作亏掀,它會用舊的數(shù)據(jù)集來提供服務允睹。但是復制完成時,需要刪除舊數(shù)據(jù)集幌氮,加載新的數(shù)據(jù)集缭受,這個時候就會暫停對外服務了。
  6. slave node 主要用來進行橫向擴容该互,做讀寫分離米者,擴容 slave node 可以提高讀的吞吐量

master持久化對主從架構(gòu)的意義:
如果開啟了主從架構(gòu),一定要開啟 master node的持久化,不然 master宕機重啟數(shù)據(jù)是空的蔓搞,一經(jīng)復制胰丁,slave的數(shù)據(jù)也丟了

主從復制原理:



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

  1. 當啟動一個 slave node的時候,它會發(fā)送一個 PSYNC 命令給 master node
  2. master 會觸發(fā)一次 full resynchronization (如果不是第一次連接喂分,master 只會復制給 slave 部分缺少的數(shù)據(jù)锦庸,從backlog里找)
  3. master會啟動一個后臺線程,開始生成一份 RDB 快照( bgsave,也可以直接在內(nèi)存中創(chuàng)建)蒲祈,同時將從客戶端收到的所有寫命令緩存在內(nèi)存中甘萧。RDB 文件生成完畢之后,master會將這個RDB發(fā)送給slave梆掸,slave會先寫入本地磁盤扬卷,然后再從本地磁盤加載到內(nèi)存中。然后 master會將內(nèi)存中緩存的寫命令發(fā)送給 slave,slave也會同步這些數(shù)據(jù)(slave如果跟 master網(wǎng)絡故障酸钦,斷開連接怪得,會自動重連,master如果發(fā)現(xiàn)有多個 slave 來重新連接卑硫,僅僅只會啟動一個 RDB save 操作徒恋,用一份數(shù)據(jù)服務所有 slave node)
    正常情況下:
    master 來一條數(shù)據(jù),就異步給 slave

Redis高可用性

全年 99.99%的時間欢伏,都是出于可用的狀態(tài)入挣,那么就可以稱為高可用性
redis 高可用架構(gòu)叫故障轉(zhuǎn)移,failover颜懊,也可以叫做主備切換财岔,切換的時間不可用风皿,但是整體高可用河爹。
sentinal node(哨兵)

Sentinal

作用:

  1. 集群監(jiān)控,負責監(jiān)控 redis master 和 slave進程是否正常
  2. 消息通知桐款,如果某個 redis 實例有故障咸这,那么哨兵負責發(fā)送消息作為報警通知給管理員
  3. 故障轉(zhuǎn)移,如果 master 掛掉魔眨,會自動轉(zhuǎn)移到 slave
  4. 配置中心媳维,如果故障轉(zhuǎn)移了,通知 client 客戶端新的 master地址

兩節(jié)點哨兵集群


quorum = 1 (代表哨兵最低個數(shù)可以嘗試故障轉(zhuǎn)移遏暴,選舉執(zhí)行的哨兵)
master 宕機侄刽,只有 S2 存活,因為 quorum =1 可以嘗試故障轉(zhuǎn)移朋凉,但是沒達到 majority =2 (最低允許執(zhí)行故障轉(zhuǎn)移的哨兵存活數(shù))的標準州丹,無法執(zhí)行故障轉(zhuǎn)移

三節(jié)點哨兵集群(經(jīng)典)


如果 M1 宕機了,S2,S3 認為 master宕機,選舉一個執(zhí)行故障轉(zhuǎn)移墓毒,因為 3 個哨兵的 majority = 2吓揪,所以可以執(zhí)行故障轉(zhuǎn)移

Redis 主從 + 哨兵

丟數(shù)據(jù):


  1. master內(nèi)存中數(shù)據(jù)異步同步到 slave master 就掛掉了,丟掉了 master 內(nèi)存中的數(shù)據(jù)


  2. 腦裂,某個 master 所在機器突然脫離了正常的網(wǎng)絡所计,其他 slave機器不能連接柠辞,但是實際上 master還在運行,哨兵認為 master 宕機主胧,選舉 slave為master叭首,此時集群里有 2 個 master, client還沒來得及切換到新的master,還繼續(xù)寫在舊的 master上讥裤,數(shù)據(jù)丟了放棒,此時舊的 master再次恢復,被被作為一個 slave 掛到新的 master 上己英,自己的數(shù)據(jù)被清空 (腦裂间螟,大腦一分為 2,同時指揮同一個人)

解決方案:

  1. min-slaves-max-lag 10 (至少一個 slave同步的延遲不能超過 10s) 減少異步復制的數(shù)據(jù)丟失损肛,發(fā)現(xiàn)slave復制數(shù)據(jù)和 ack延時過長厢破,拒絕寫入,減少同步數(shù)據(jù)損失治拿。讓client做降級寫到本地磁盤里和限流摩泪,或者先暫存到消息隊列,然后重新發(fā)回 master
  2. min-slaves-to-write 1 減少腦裂帶來的數(shù)據(jù)丟失劫谅,最多損失 10 s數(shù)據(jù)见坑,假設master 不能繼續(xù)給 slave發(fā)送數(shù)據(jù),并且 slave 10s沒給自己的 ack消息捏检,直接拒絕客戶端寫請求荞驴,同時 client做降寫到本地磁盤、限流贯城,或者先暫存到消息隊列熊楼,然后重新發(fā)回 master

哨兵

sdown 主觀宕機,哨兵覺得一個 master 宕機(ping 超過了 is-master-down-after-milliseconds毫秒數(shù))
odown 客觀宕機能犯,quorum數(shù)量的哨兵都覺得 master宕機
哨兵互相感知通過 redis的 pub/sub系統(tǒng)鲫骗,每隔 2 秒往同一個 channel里發(fā)消息(自己的 host,ip,runid),其他哨兵可以消費這個消息
以及同步交換master的監(jiān)控信息踩晶。
哨兵確保其他slave修改master信息為新選舉的master
當一個 master被認為 odown && marjority哨兵都同意执泰,那么某個哨兵會執(zhí)行主備切換,選舉一個slave成為master(考慮 1. 跟master斷開連接的時長 2. slave 優(yōu)先級 3.復制 offset 4. runid)
選舉算法:

  1. 如果slave跟master斷開連接已經(jīng)超過 down-after-milliseconds * 10 + master宕機時間渡蜻,則放棄
  2. 按 slave 優(yōu)先級排序 术吝,slave-priority 越小越靠前
  3. replica offset ,哪個slave復制越多的數(shù)據(jù),越靠前
  4. runid 越小顿苇,越靠前

quorum 數(shù)量哨兵認為odown->選舉一個哨兵切換->獲得 majority哨兵的授權(quán)(quorum < majority 需要 majority個哨兵授權(quán)峭咒,quorum >= majority 需要 quorum 哨兵授權(quán))
第一個選舉出來的哨兵切換失敗了,其他哨兵等待 failover-time之后纪岁,重新拿confiuration epoch做為新的version 切換凑队,保證拿到最新配置,用于 configuration傳播(通過 pu/sub消息機制幔翰,其他哨兵對比 version 新舊更新 master配置)

Redis 優(yōu)化方案

高并發(fā):主從架構(gòu)
高容量:Redis集群漩氨,支持每秒幾十萬的讀寫并發(fā)
高可用:主從+哨兵

Redis 持久化

持久化的意義在于故障恢復數(shù)據(jù)備份(到其他服務器)+故障恢復(遇到災難,機房斷電遗增,電纜被切)

  • RDB 對 Redis 中的數(shù)據(jù)執(zhí)行周期性的持久化叫惊。
  • AOF 機制,每條寫命令作為日志做修,以 append-only模式寫入一個日志文件總霍狰,在 redis重啟的時候,可以通過回放AOF日志中的寫入指令來重新構(gòu)建整個數(shù)據(jù)集
    AOF 只有一個饰及,Redis 中的數(shù)據(jù)是有一定限量的蔗坯,內(nèi)存大小是一定的,AOF 是存放寫命令的,當大到一定的時候燎含,AOF 做 rewrite 操作宾濒,就會基于當時 redis 內(nèi)存中的數(shù)據(jù),來重新構(gòu)造一個更小的 AOF 文件屏箍,然后將舊的膨脹很大的文件給刪掉绘梦,AOF 文件一直會被限制在和Redis內(nèi)存中一樣的數(shù)據(jù)。AOF同步間隔比 RDB 小赴魁,數(shù)據(jù)更完整

RDB

優(yōu)點:

  • RDB 會生成多個數(shù)據(jù)文件卸奉,每個數(shù)據(jù)文件都代表了某一個時刻中 redis 的數(shù)據(jù),這種多個數(shù)據(jù)文件的方式尚粘,非常適合做冷備择卦,可以將這種完整的數(shù)據(jù)文件發(fā)送到一些遠程的安全存儲上去敲长,RDB 做冷備郎嫁,生成多個文件,每個文件都代表某一個時刻的完整的數(shù)據(jù)快照祈噪,AOF 也可以做冷備泽铛,只有一個文件,每隔一定時間去 copy一份這個文件出來辑鲤。 RDB 做冷備盔腔,由Redis控制固定時長去生成快照文件,比較方便。AOF弛随,需要自己寫腳本定時控制瓢喉。
  • RDB 對 redis對外提供的讀寫服務,影響非常小舀透,可以讓 redis 保持高性能栓票,因為 redis 主進程只需要 fork一個子進程,讓子進程執(zhí)行磁盤 IO 操作來進行 RDB 持久化
  • 相對于 AOF 持久化機制來說愕够,直接基于 RDB 數(shù)據(jù)文件來重啟和恢復 redis 進程走贪,更加快速
    缺點:
  • 如果想要在 redis故障時,盡可能少丟數(shù)據(jù)惑芭,那么 RDB 沒有 AOF 好坠狡,一般 RDB 數(shù)據(jù)快照,都是間隔 5 分鐘遂跟,或者更長的時候生成一次逃沿,這個時候就得接受一旦 redis 進程宕機,那么會丟失最近 5 分鐘數(shù)據(jù)
  • RDB 每次在 fork子進程來執(zhí)行 RDB 快早數(shù)據(jù)文件生成的時候幻锁,如果數(shù)據(jù)文件特別大感挥,可能會導致對客戶端提供的服務暫停數(shù)毫秒,甚至數(shù)秒(RDB 生成間隔不要太長)
    AOF 存放的指令日志越败,數(shù)據(jù)恢復的時候触幼,需要回放執(zhí)行所有指令日志,RDB 就是一份數(shù)據(jù)文件究飞,直接加載到內(nèi)存中置谦。

AOF

優(yōu)點:

  1. 更好保護數(shù)據(jù)不丟失,后臺線程 fsync 操作亿傅,最多丟失一秒鐘數(shù)據(jù)媒峡,保證 os cache中的數(shù)據(jù)寫入磁盤中
  2. AOF 用 append-only 模式,沒有磁盤尋址開銷葵擎,寫入性能非常高谅阿,文件不容易損壞。
  3. AOF 日志過大的時候酬滤,后臺 rewrite log時候签餐,老的日志文件照常寫入,新的merge后的日志文件 ready的時候盯串,再交換新老日志文件
  4. 適合災難性恢復氯檐,某人不小心 flushall清空所有數(shù)據(jù),只要后臺 rewrite還沒發(fā)生体捏,那么可以立刻拷貝 AOF 文件冠摄,將最后一條 flushall命令給刪了糯崎,然后再將該 AOF 文件放回去,可以通過恢復機制河泳,自動恢復所有數(shù)據(jù)

缺點:

  1. AOF 日志文件比 RDB 數(shù)據(jù)快照文件大
  2. 降低 Redis的寫 QPS
  3. AOF 復雜沃呢,Bug多
  4. 數(shù)據(jù)恢復比較慢

最佳方案

AOF 來保證數(shù)據(jù)不丟失,RDB 做不同時間的冷備

Redis Cluster


支持 N 個 Redis master node,每個 master node掛載多個 slave node
多master + 讀寫分離 + 高可用

數(shù)據(jù)量很少拆挥,高并發(fā) -> replication + sentinal 集群
海量數(shù)據(jù) + 高并發(fā) + 高可用 -> redis cluster

分布式算法

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

redis cluster :自動對數(shù)據(jù)進行分片樟插,每個 master 上放一部分數(shù)據(jù),提供內(nèi)置的高可用支持竿刁,部分master不可用時黄锤,還是可以繼續(xù)工作
cluster bus 通過 16379進行通信,故障檢測食拜,配置更新鸵熟,故障轉(zhuǎn)移授權(quán),另外一種二進制協(xié)議负甸,主要用于節(jié)點間進行高效數(shù)據(jù)交換流强,占用更少的網(wǎng)絡帶寬和處理時間

hash算法

key進行hash,然后對節(jié)點數(shù)量取模呻待,最大問題只有任意一個 master 宕機打月,大量數(shù)據(jù)就要根據(jù)新的節(jié)點數(shù)取模,會導致大量緩存失效蚕捉。

一致性 hash 算法


key進行hash奏篙,對應圓環(huán)上一個點,順時針尋找距離最近的一個點迫淹。保證任何一個 master 宕機秘通,只受 master 宕機那臺影響,其他節(jié)點不受影響敛熬,此時會瞬間去查數(shù)據(jù)庫肺稀。
緩存熱點問題:
可能集中在某個 hash區(qū)間內(nèi)的值特別多,那么會導致大量的數(shù)據(jù)都涌入同一個 master 內(nèi)应民,造成 master的熱點問題话原,性能出現(xiàn)瓶頸。
解決方法:
給每個 master 都做了均勻分布的虛擬節(jié)點诲锹,這樣每個區(qū)間內(nèi)大量數(shù)據(jù)都會均勻的分布到不同節(jié)點內(nèi)繁仁,而不是順時針全部涌入到同一個節(jié)點中。

Hash Slot算法

redis cluster 有固定 16384 個 hash slot,對每個key計算 CRC16 值辕狰,然后對16384取模改备,可以獲取 key對應的 hash slot
redis cluster 中每個 master 都會持有部分 slot ,當一臺 master 宕機時候控漠,會最快速度遷移 hash slot到可用的機器上(只會短暫的訪問不到)
走同一個 hash slot 通過 hash tag實現(xiàn)

Redis Cluster 核心

  1. 基礎通信
    通過 gossip 協(xié)議通信(小道留言蔓倍,所有節(jié)點都持有一份元數(shù)據(jù)悬钳,不同的節(jié)點如果出現(xiàn)了元數(shù)據(jù)的變更,就不斷將元數(shù)據(jù)發(fā)送給其他節(jié)點偶翅,讓其他節(jié)點也進行元數(shù)據(jù)的變更)



    集群元數(shù)據(jù):包括 hashslot->node之間的映射表關(guān)系默勾,master->slave之間的關(guān)系,故障的信息
    集群元數(shù)據(jù)集中式存儲(storm)聚谁,底層基于zookeeper(分布式協(xié)調(diào)中間件)集群所有元數(shù)據(jù)的維護母剥。好處:元數(shù)據(jù)的更新和讀取,時效性好形导,一旦變更环疼,其他節(jié)點立刻可以感知。缺點:所有元數(shù)據(jù)的更新壓力全部集中在一個地方朵耕,可能會導致元數(shù)據(jù)的存儲有壓力炫隶。
    goosip: 好處:元數(shù)據(jù)的更新比較分散,有一定的延時阎曹,降低了壓力伪阶。缺點:更新有延時,集群的一些操作會滯后处嫌。(reshared操作時configuration error)

  2. 10000 端口
    自己提供服務的端口號+ 10000 栅贴,每隔一段時間就會往另外幾個節(jié)點發(fā)送ping消息,同時其他幾點接收到ping之后返回pong

  3. 交換的信息
    故障信息熏迹,節(jié)點的增加和移除檐薯, hash slot 信息

  4. gossip協(xié)議
    meet:某個節(jié)點發(fā)送 meet給新加入的節(jié)點,讓新節(jié)點加入集群中注暗,然后新節(jié)點就會開始于其他節(jié)點進行通信
    ping:每個節(jié)點都會頻繁給其他節(jié)點發(fā)送ping厨剪,其中包含自己的狀態(tài)還有自己維護的集群元數(shù)據(jù),互相通過ping交換元數(shù)據(jù)
    ping:返回ping和meet友存,包含自己的狀態(tài)和其他信息
    fail:某個節(jié)點判斷另一個節(jié)點fail之后祷膳,就發(fā)送 fail 給其他節(jié)點,通知其他節(jié)點屡立,指定的節(jié)點宕機了

  5. ping消息
    ping 很頻繁直晨,且攜帶元數(shù)據(jù),會加重網(wǎng)絡負擔
    每個節(jié)點每秒會執(zhí)行 10 次 ping膨俐,每次選擇 5 個最久沒有通信的其他節(jié)點
    當如果發(fā)現(xiàn)某個節(jié)點通信延遲達到了 cluster_node_timeout /2 勇皇,那么立即發(fā)送 ping, 避免數(shù)據(jù)交換延遲過長焚刺,落后時間太長(2 個節(jié)點之間 10 分鐘沒有交換數(shù)據(jù)敛摘,整個集群處于嚴重的元數(shù)據(jù)不一致的情況)。
    每次ping乳愉,一個是帶上自己的節(jié)點信息兄淫,還有就是帶上1/10其他節(jié)點的信息屯远,發(fā)送出去,進行數(shù)據(jù)交換
    至少包含 3 個其他節(jié)點信息捕虽,最多包含總節(jié)點-2 個其他節(jié)點的信息

  6. JRedis原理

  • 請求重定向
    客戶端發(fā)送到任意一個redis實例發(fā)送命令慨丐,每個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 映射表,同時為每個節(jié)點創(chuàng)建一個JedisPool連接池惧盹,每次基于JedisCluster執(zhí)行操作乳幸,首先JedisCluster都會在本地計算key的hashslot,然后再本地映射表中找到對應的節(jié)點钧椰,如果發(fā)現(xiàn)對應的節(jié)點返回moved粹断,那么利用該節(jié)點的元數(shù)據(jù),更新 hashslot->node映射表(重試超過 5 次報錯)

  • hashslot遷移和ask重定向
    hash slot正在遷移嫡霞,那么會返回ask 重定向給jedis,jedis 接受到ask重定向之后瓶埋,,會重定向到目標節(jié)點去執(zhí)行

  • 高可用性和主備切換原理
    判斷節(jié)點宕機:
    如果一個節(jié)點認為另外一個節(jié)點宕機了诊沪, 就是pfail,主觀宕機
    如果多個節(jié)點都認為另外一個節(jié)點宕機了养筒,那么就是fail,客觀宕機(跟哨兵原理一樣)
    在cluster-node-timeout內(nèi)端姚,某個節(jié)點一直沒有返回 pong,那么就被認為是 pfail
    如果一個節(jié)點認為某個節(jié)點pfail了晕粪,那么會在gossip消息中,ping給其他節(jié)點渐裸,如果超過半數(shù)的節(jié)點認為pfail了巫湘,那么就會變成fail。
    從節(jié)點過濾:
    對宕機的 mster node 昏鹃,從其所有的 slave node中尚氛,選擇一個切換成 master node
    檢查每個 slave node與master node斷開連接的時間,如果超過了cluster-node-timeout * cluster-slave-validity-factor洞渤,那么就沒資格切換成 master(和哨兵一致)
    從節(jié)點選舉:
    每個從節(jié)點阅嘶,根據(jù)自己對 master 復制數(shù)據(jù)的 offset,設置一個選舉時間载迄,offset越大(復制數(shù)據(jù)越多)的從節(jié)點讯柔,選舉時間越靠前抡蛙,所有的 master node 開始投票,給要進行選舉的 slave進行投票咖熟,如果大部分 master node(N/2 +1) 都投票給某個從節(jié)點逊拍,那么選舉通過,從節(jié)點執(zhí)行主備切換,從節(jié)點切換成主節(jié)點
    總結(jié):和哨兵很像蛋欣,直接集成了 replication 和 sentinal

緩存雪崩

方案:
事前:保證 redis 集群高可用性 (主從+哨兵或 redis cluster),避免全盤崩潰
事中:本地 ehcache 緩存 + hystrix 限流(保護數(shù)據(jù)庫) & 降級熄诡,避免 MySQL被打死
事后: redis持久化临燃,快速恢復緩存數(shù)據(jù),繼續(xù)分流高并發(fā)請求

限制組件每秒就 2000 個請求通過限流組件進入數(shù)據(jù)庫蒜田,剩余的 3000 個請求走降級稿械,返回一些默認 的值,或者友情提示
好處 :

  1. 數(shù)據(jù)庫絕對不會死冲粤,確保了每秒只會過去 2000 個請求
  2. 只要數(shù)據(jù)庫不死美莫,對于用戶來說 2/5的請求可以被處理
  3. 系統(tǒng)沒死,用戶多點幾次可能就刷出來了

緩存穿透


4000 個請求黑客攻擊請求數(shù)據(jù)庫里沒有的數(shù)據(jù)
解決方案:把黑客查數(shù)據(jù)庫中不存在的數(shù)據(jù)的值梯捕,寫到緩存中厢呵,比如: set -999 UNKNOWN

緩存與數(shù)據(jù)庫雙寫一致性

  1. cache aside pattern



    讀的時候,先讀緩存傀顾,緩存沒有襟铭,就讀數(shù)據(jù)庫,然后取出數(shù)據(jù)后放入緩存短曾,同時返回響應
    更新的時候寒砖,刪除緩存,更新數(shù)據(jù)庫
    為什么不更新緩存:
    更新緩存代價太高(更新 20 次嫉拐,只讀 1 次)哩都,lazy思想,需要的時候再計算婉徘,不需要的時候不計算

  2. 修改數(shù)據(jù)庫成功茅逮,刪除緩存失敗,導致數(shù)據(jù)庫是新的數(shù)據(jù)判哥,緩存中是舊的數(shù)據(jù)
    方案:先刪除緩存献雅,再修改數(shù)據(jù)庫



  3. 修改數(shù)據(jù)庫還沒修改完,同時又有查詢請求塌计,把舊的數(shù)據(jù)放到緩存中(高并發(fā)挺身,每秒并發(fā)讀幾萬,每秒只要有數(shù)據(jù)更新請求锌仅,就可能出現(xiàn)數(shù)據(jù)庫+緩存不一致情況)
    方案:寫章钾,讀路由到相同的一個內(nèi)存隊列(唯一標識墙贱,hash,取模)里贱傀,更新和讀操作進行串行化(后臺線程異步執(zhí)行隊列串行化操作)惨撇,(隊列里只放一個更新查詢操作即可,多余的過濾掉府寒,內(nèi)存隊列里沒有該數(shù)據(jù)更新操作魁衙,直接返回 )有該數(shù)據(jù)更新操作則輪詢?nèi)【彺嬷担瑫r取不到緩存值株搔,直接取一次數(shù)據(jù)庫的舊值


    TP 99 意思是99%的請求可以在200ms內(nèi)返回
    注意點:多個商品的更新操作都積壓在一個隊列里面(太多操作積壓只能增加機器)剖淀,導致讀請求發(fā)生大量的超時,導致大量的讀請求走數(shù)據(jù)庫
    一秒 500 寫操作纤房,每200ms纵隔,100 個寫操作,20 個內(nèi)存隊列炮姨,每個隊列積壓 5 個寫操作捌刮,一般在20ms完成

Redis 并發(fā)競爭問題


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

Redis 集群部署架構(gòu)

10臺機器,5 主 5 從舒岸,每個節(jié)點QPS 5W 绅作,一共 25W QPS(Redis cluster 32G + 8 核 ,Redis 進程不超過 10G)總內(nèi)存 50g吁津,每條數(shù)據(jù)10kb棚蓄,10W 條數(shù)據(jù)1g,200W 條數(shù)據(jù) 20G碍脏,占用總內(nèi)存不到50%梭依,目前高峰期 3500 QPS

本文由博客一文多發(fā)平臺 OpenWrite 發(fā)布!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末典尾,一起剝皮案震驚了整個濱河市役拴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌钾埂,老刑警劉巖河闰,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異褥紫,居然都是意外死亡姜性,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門髓考,熙熙樓的掌柜王于貴愁眉苦臉地迎上來部念,“玉大人,你說我怎么就攤上這事±芰叮” “怎么了妓湘?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長乌询。 經(jīng)常有香客問我榜贴,道長,這世上最難降的妖魔是什么妹田? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任唬党,我火速辦了婚禮,結(jié)果婚禮上秆麸,老公的妹妹穿的比我還像新娘初嘹。我一直安慰自己及汉,他們只是感情好沮趣,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著坷随,像睡著了一般房铭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上温眉,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天缸匪,我揣著相機與錄音,去河邊找鬼类溢。 笑死凌蔬,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的闯冷。 我是一名探鬼主播砂心,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蛇耀!你這毒婦竟也來了辩诞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤纺涤,失蹤者是張志新(化名)和其女友劉穎译暂,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體撩炊,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡外永,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了拧咳。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伯顶。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出砾淌,到底是詐尸還是另有隱情啦撮,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布汪厨,位于F島的核電站赃春,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏劫乱。R本人自食惡果不足惜织中,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望衷戈。 院中可真熱鬧狭吼,春花似錦、人聲如沸殖妇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谦趣。三九已至疲吸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間前鹅,已是汗流浹背摘悴。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留舰绘,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓口四,卻偏偏與公主長得像者蠕,于是被迫代替她去往敵國和親窃祝。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容