redis運維相關

1、redis都有哪些數據類型?分別在哪些場景下使用比較合適?
2、redis雙寫不一致
3、雪崩和穿透
4、redis的過時策略,LRU
5、redis是如何實現高性能高併發
6、如何保證Redis的高併發和高可用?redis的主從複製原理能介紹一下麼?redis的哨兵原理能介紹一下麼?
7、併發競爭
8、redis的線程模型
9、redis集羣模式原理
10、redis的集羣部署架構
11、redis的持久化前端

 

1、redis都有哪些數據類型?分別在哪些場景下使用比較合適?
(1)string
這是最基本的類型了,沒啥可說的,就是普通的set和get,作簡單的kv緩存java

(2)hash
這個是相似map的一種結構,這個通常就是能夠將結構化的數據,好比一個對象(前提是這個對象沒嵌套其餘的對象)給緩存在redis裏,而後每次讀寫緩存的時候,能夠就操做hash裏的某個字段。
key=150
value={
「id」: 150,
「name」: 「zhangsan」,
「age」: 20
}node

hash類的數據結構,主要是用來存放一些對象,把一些簡單的對象給緩存起來,後續操做的時候,你能夠直接僅僅修改這個對象中的某個字段的值
value={
「id」: 150,
「name」: 「zhangsan」,
「age」: 21
}mysql

(3)list
有序列表,這個是能夠玩兒出不少花樣的
微博,某個大v的粉絲,就能夠以list的格式放在redis裏去緩存
key=某大v
value=[zhangsan, lisi, wangwu]
好比能夠經過list存儲一些列表型的數據結構,相似粉絲列表了、文章的評論列表了之類的東西
好比能夠經過lrange命令,就是從某個元素開始讀取多少個元素,能夠基於list實現分頁查詢,這個很棒的一個功能,基於redis實現簡單的高性能分頁,能夠作相似微博那種下拉不斷分頁的東西,性能高,就一頁一頁走
好比能夠搞個簡單的消息隊列,從list頭懟進去,從list尾巴那裏弄出來react

(4)set
無序集合,自動去重
直接基於set將系統裏須要去重的數據扔進去,自動就給去重了,若是你須要對一些數據進行快速的全局去重,你固然也能夠基於jvm內存裏的HashSet進行去重,可是若是你的某個系統部署在多臺機器上呢?
得基於redis進行全局的set去重
能夠基於set玩兒交集、並集、差集的操做,好比交集吧,能夠把兩我的的粉絲列表整一個交集,看看倆人的共同好友是誰?對吧
把兩個大v的粉絲都放在兩個set中,對兩個set作交集nginx

(5)sorted set
排序的set,去重可是能夠排序,寫進去的時候給一個分數,自動根據分數排序,這個能夠玩兒不少的花樣,最大的特色是有個分數能夠自定義排序規則
好比說你要是想根據時間對數據排序,那麼能夠寫入進去的時候用某個時間做爲分數,人家自動給你按照時間排序了
排行榜:將每一個用戶以及其對應的什麼分數寫入進去,zadd board score username,接着zrevrange board 0 99,就能夠獲取排名前100的用戶;zrank board username,能夠看到用戶在排行榜裏的排名面試

zadd board 85 zhangsan
zadd board 72 wangwu
zadd board 96 lisi
zadd board 62 zhaoliuredis

96 lisi
85 zhangsan
72 wangwu
62 zhaoliu
zrevrange board 0 3算法

獲取排名前3的用戶
96 lisi
85 zhangsan
72 wangwusql

zrank board zhaoliu
4

 

2、redis雙寫不一致
最經典的緩存+數據庫讀寫的模式,cache aside pattern

一、Cache Aside Pattern

(1)讀的時候,先讀緩存,緩存沒有的話,那麼就讀數據庫,而後取出數據後放入緩存,同時返回響應
(2)更新的時候,先刪除緩存,而後再更新數據庫

二、爲何是刪除緩存,而不是更新緩存呢?

緣由很簡單,不少時候,複雜點的緩存的場景,由於緩存有的時候,不簡單是數據庫中直接取出來的值
商品詳情頁的系統,修改庫存,只是修改了某個表的某些字段,可是要真正把這個影響的最終的庫存計算出來,可能還須要從其餘表查詢一些數據,而後進行一些複雜的運算,才能最終計算出

如今最新的庫存是多少,而後才能將庫存更新到緩存中去
好比可能更新了某個表的一個字段,而後其對應的緩存,是須要查詢另外兩個表的數據,並進行運算,才能計算出緩存最新的值的
更新緩存的代價是很高的
是否是說,每次修改數據庫的時候,都必定要將其對應的緩存去跟新一份?也許有的場景是這樣的,可是對於比較複雜的緩存數據計算的場景,就不是這樣了
若是你頻繁修改一個緩存涉及的多個表,那麼這個緩存會被頻繁的更新,頻繁的更新緩存
可是問題在於,這個緩存到底會不會被頻繁訪問到???

舉個例子,一個緩存涉及的表的字段,在1分鐘內就修改了20次,或者是100次,那麼緩存跟新20次,100次; 可是這個緩存在1分鐘內就被讀取了1次,有大量的冷數據
28法則,黃金法則,20%的數據,佔用了80%的訪問量
實際上,若是你只是刪除緩存的話,那麼1分鐘內,這個緩存不過就從新計算一次而已,開銷大幅度下降
每次數據過來,就只是刪除緩存,而後修改數據庫,若是這個緩存,在1分鐘內只是被訪問了1次,那麼只有那1次,緩存是要被從新計算的,用緩存纔去算緩存
其實刪除緩存,而不是更新緩存,就是一個lazy計算的思想,不要每次都從新作複雜的計算,無論它會不會用到,而是讓它到須要被使用的時候再從新計算
mybatis,hibernate,懶加載,思想
查詢一個部門,部門帶了一個員工的list,沒有必要說每次查詢部門,都裏面的1000個員工的數據也同時查出來啊
80%的狀況,查這個部門,就只是要訪問這個部門的信息就能夠了
先查部門,同時要訪問裏面的員工,那麼這個時候只有在你要訪問裏面的員工的時候,纔會去數據庫裏面查詢1000個員工


03_高併發場景下的緩存+數據庫雙寫不一致問題分析與解決方案設計

立刻開始去開發業務系統
從哪一步開始作,從比較簡單的那一塊開始作,實時性要求比較高的那塊數據的緩存去作
實時性比較高的數據緩存,選擇的就是庫存的服務
庫存可能會修改,每次修改都要去更新這個緩存數據; 每次庫存的數據,在緩存中一旦過時,或者是被清理掉了,前端的nginx服務都會發送請求給庫存服務,去獲取相應的數據
庫存這一塊,寫數據庫的時候,直接更新redis緩存
實際上沒有這麼的簡單,這裏,其實就涉及到了一個問題,數據庫與緩存雙寫,數據不一致的問題
圍繞和結合實時性較高的庫存服務,把數據庫與緩存雙寫不一致問題以及其解決方案,給你們講解一下
數據庫與緩存雙寫不一致,很常見的問題,大型的緩存架構中,第一個解決方案


一、最初級的緩存不一致問題以及解決方案

問題:先修改數據庫,再刪除緩存,若是刪除緩存失敗了,那麼會致使數據庫中是新數據,緩存中是舊數據,數據出現不一致
解決思路
先刪除緩存,再修改數據庫,若是刪除緩存成功了,若是修改數據庫失敗了,那麼數據庫中是舊數據,緩存中是空的,那麼數據不會不一致
由於讀的時候緩存沒有,則讀數據庫中舊數據,而後更新到緩存中

二、比較複雜的數據不一致問題分析

數據發生了變動,先刪除了緩存,而後要去修改數據庫,此時還沒修改
一個請求過來,去讀緩存,發現緩存空了,去查詢數據庫,查到了修改前的舊數據,放到了緩存中
數據變動的程序完成了數據庫的修改
完了,數據庫和緩存中的數據不同了。。。。

三、爲何上億流量高併發場景下,緩存會出現這個問題?

只有在對一個數據在併發的進行讀寫的時候,纔可能會出現這種問題
其實若是說你的併發量很低的話,特別是讀併發很低,天天訪問量就1萬次,那麼不多的狀況下,會出現剛纔描述的那種不一致的場景
可是問題是,若是天天的是上億的流量,每秒併發讀是幾萬,每秒只要有數據更新的請求,就可能會出現上述的數據庫+緩存不一致的狀況
高併發了之後,問題是不少的

四、數據庫與緩存更新與讀取操做進行異步串行化

更新數據的時候,根據數據的惟一標識,將操做路由以後,發送到一個jvm內部的隊列中
讀取數據的時候,若是發現數據不在緩存中,那麼將從新讀取數據+更新緩存的操做,根據惟一標識路由以後,也發送同一個jvm內部的隊列中
一個隊列對應一個工做線程
每一個工做線程串行拿到對應的操做,而後一條一條的執行
這樣的話,一個數據變動的操做,先執行,刪除緩存,而後再去更新數據庫,可是還沒完成更新
此時若是一個讀請求過來,讀到了空的緩存,那麼能夠先將緩存更新的請求發送到隊列中,此時會在隊列中積壓,而後同步等待緩存更新完成
這裏有一個優化點,一個隊列中,其實多個更新緩存請求串在一塊兒是沒意義的,所以能夠作過濾,若是發現隊列中已經有一個更新緩存的請求了,那麼就不用再放個更新請求操做進去了,直接等待前面的更新操做請求完成便可
待那個隊列對應的工做線程完成了上一個操做的數據庫的修改以後,纔會去執行下一個操做,也就是緩存更新的操做,此時會從數據庫中讀取最新的值,而後寫入緩存中
若是請求還在等待時間範圍內,不斷輪詢發現能夠取到值了,那麼就直接返回; 若是請求等待的時間超過必定時長,那麼這一次直接從數據庫中讀取當前的舊值

五、高併發的場景下,該解決方案要注意的問題

(1)讀請求長時阻塞

因爲讀請求進行了很是輕度的異步化,因此必定要注意讀超時的問題,每一個讀請求必須在超時時間範圍內返回
該解決方案,最大的風險點在於說,可能數據更新很頻繁,致使隊列中積壓了大量更新操做在裏面,而後讀請求會發生大量的超時,最後致使大量的請求直接走數據庫
務必經過一些模擬真實的測試,看看更新數據的頻繁是怎樣的
另一點,由於一個隊列中,可能會積壓針對多個數據項的更新操做,所以須要根據本身的業務狀況進行測試,可能須要部署多個服務,每一個服務分攤一些數據的更新操做
若是一個內存隊列裏竟然會擠壓100個商品的庫存修改操做,每隔庫存修改操做要耗費10ms區完成,那麼最後一個商品的讀請求,可能等待10 * 100 = 1000ms = 1s後,才能獲得數據
這個時候就致使讀請求的長時阻塞
必定要作根據實際業務系統的運行狀況,去進行一些壓力測試,和模擬線上環境,去看看最繁忙的時候,內存隊列可能會擠壓多少更新操做,可能會致使最後一個更新操做對應的讀請求,會hang多少時間,若是讀請求在200ms返回,若是你計算事後,哪怕是最繁忙的時候,積壓10個更新操做,最多等待200ms,那還能夠的
若是一個內存隊列可能積壓的更新操做特別多,那麼你就要加機器,讓每一個機器上部署的服務實例處理更少的數據,那麼每一個內存隊列中積壓的更新操做就會越少
其實根據以前的項目經驗,通常來講數據的寫頻率是很低的,所以實際上正常來講,在隊列中積壓的更新操做應該是不多的
針對讀高併發,讀緩存架構的項目,通常寫請求相對讀來講,是很是很是少的,每秒的QPS能到幾百就不錯了
一秒,500的寫操做,5份,每200ms,就100個寫操做
單機器,20個內存隊列,每一個內存隊列,可能就積壓5個寫操做,每一個寫操做性能測試後,通常在20ms左右就完成
那麼針對每一個內存隊列中的數據的讀請求,也就最多hang一下子,200ms之內確定能返回了
寫QPS擴大10倍,可是通過剛纔的測算,就知道,單機支撐寫QPS幾百沒問題,那麼就擴容機器,擴容10倍的機器,10臺機器,每一個機器20個隊列,200個隊列
大部分的狀況下,應該是這樣的,大量的讀請求過來,都是直接走緩存取到數據的
少許狀況下,可能遇到讀跟數據更新衝突的狀況,如上所述,那麼此時更新操做若是先入隊列,以後可能會瞬間來了對這個數據大量的讀請求,可是由於作了去重的優化,因此也就一個更新緩存的操做跟在它後面
等數據更新完了,讀請求觸發的緩存更新操做也完成,而後臨時等待的讀請求所有能夠讀到緩存中的數據

(2)讀請求併發量太高
這裏還必須作好壓力測試,確保恰巧碰上上述狀況的時候,還有一個風險,就是忽然間大量讀請求會在幾十毫秒的延時hang在服務上,看服務能不能抗的住,須要多少機器才能抗住最大的極限狀況的峯值
可是由於並非全部的數據都在同一時間更新,緩存也不會同一時間失效,因此每次可能也就是少數數據的緩存失效了,而後那些數據對應的讀請求過來,併發量應該也不會特別大
按1:99的比例計算讀和寫的請求,每秒5萬的讀QPS,可能只有500次更新操做
若是一秒有500的寫QPS,那麼要測算好,可能寫操做影響的數據有500條,這500條數據在緩存中失效後,可能致使多少讀請求,發送讀請求到庫存服務來,要求更新緩存
通常來講,1:1,1:2,1:3,每秒鐘有1000個讀請求,會hang在庫存服務上,每一個讀請求最多hang多少時間,200ms就會返回
在同一時間最多hang住的可能也就是單機200個讀請求,同時hang住
單機hang200個讀請求,仍是ok的
1:20,每秒更新500條數據,這500秒數據對應的讀請求,會有20 * 500 = 1萬
1萬個讀請求所有hang在庫存服務上,就死定了

(3)多服務實例部署的請求路由
可能這個服務部署了多個實例,那麼必須保證說,執行數據更新操做,以及執行緩存更新操做的請求,都經過nginx服務器路由到相同的服務實例上

(4)熱點商品的路由問題,致使請求的傾斜
萬一某個商品的讀寫請求特別高,所有打到相同的機器的相同的隊列裏面去了,可能形成某臺機器的壓力過大
就是說,由於只有在商品數據更新的時候纔會清空緩存,而後纔會致使讀寫併發,因此更新頻率不是過高的話,這個問題的影響並非特別大
可是的確可能某些機器的負載會高一些


3、雪崩和穿透
雪崩 每秒5000個請求,數據庫能處理每秒2000個,緩存每秒4000個,數據庫直接崩潰
穿透 惡意請求,不通過緩存,直接去數據庫查,數據庫直接崩潰

緩存雪崩發生的現象
緩存雪崩的事前事中過後的解決方案

事前:redis高可用,主從+哨兵,redis cluster,避免全盤崩潰
事中:本地ehcache緩存 + hystrix限流&降級,避免MySQL被打死
過後:redis持久化,快速恢復緩存數據

緩存穿透的現象
緩存穿透的解決方法

 

4、redis的過時策略,LRU
那幹掉哪些數據?保留哪些數據?固然是幹掉不經常使用的數據,保留經常使用的數據了。
因此說,這是緩存的一個最基本的概念,數據是會過時的,要麼是你本身設置個過時時間,要麼是redis本身給幹掉。
set key value 過時時間(1小時)
set進去的key,1小時以後就沒了,就失效了

若是你設置好了一個過時時間,你知道redis是怎麼給你弄成過時的嗎?何時刪除掉?若是你不知道,以前有個學員就問了,爲啥好多數據明明應該過時了,結果發現redis內存佔用仍是很高?那是由於你不知道redis是怎麼刪除那些過時key的。
redis 內存一共是10g,你如今往裏面寫了5g的數據,結果這些數據明明你都設置了過時時間,要求這些數據1小時以後都會過時,結果1小時以後,你回來一看,redis機器,怎麼內存佔用仍是50%呢?5g數據過時了,我從redis裏查,是查不到了,結果過時的數據還佔用着redis的內存。

(1)設置過時時間

咱們set key的時候,均可以給一個expire time,就是過時時間,指定這個key好比說只能存活1個小時?10分鐘?這個頗有用,咱們本身能夠指定緩存到期就失效。

若是假設你設置一個一批key只能存活1個小時,那麼接下來1小時後,redis是怎麼對這批key進行刪除的?

答案是:按期刪除+惰性刪除

所謂按期刪除,指的是redis默認是每隔100ms就隨機抽取一些設置了過時時間的key,檢查其是否過時,若是過時就刪除。假設redis裏放了10萬個key,都設置了過時時間,你每隔幾百毫秒,就檢查10萬個key,那redis基本上就死了,cpu負載會很高的,消耗在你的檢查過時key上了。注意,這裏可不是每隔100ms就遍歷全部的設置過時時間的key,那樣就是一場性能上的災難。實際上redis是每隔100ms隨機抽取一些key來檢查和刪除的。

可是問題是,按期刪除可能會致使不少過時key到了時間並無被刪除掉,那咋整呢?因此就是惰性刪除了。這就是說,在你獲取某個key的時候,redis會檢查一下 ,這個key若是設置了過時時間那麼是否過時了?若是過時了此時就會刪除,不會給你返回任何東西。

並非key到時間就被刪除掉,而是你查詢這個key的時候,redis再懶惰的檢查一下

經過上述兩種手段結合起來,保證過時的key必定會被幹掉。

很簡單,就是說,你的過時key,靠按期刪除沒有被刪除掉,還停留在內存裏,佔用着你的內存呢,除非你的系統去查一下那個key,纔會被redis給刪除掉。

可是實際上這仍是有問題的,若是按期刪除漏掉了不少過時key,而後你也沒及時去查,也就沒走惰性刪除,此時會怎麼樣?若是大量過時key堆積在內存裏,致使redis內存塊耗盡了,咋整?

答案是:走內存淘汰機制。

(2)內存淘汰

若是redis的內存佔用過多的時候,此時會進行內存淘汰,有以下一些策略:

redis 10個key,如今已經滿了,redis須要刪除掉5個key

1個key,最近1分鐘被查詢了100次
1個key,最近10分鐘被查詢了50次
1個key,最近1個小時倍查詢了1次

1)noeviction:當內存不足以容納新寫入數據時,新寫入操做會報錯,這個通常沒人用吧,實在是太噁心了
2)allkeys-lru:當內存不足以容納新寫入數據時,在鍵空間中,移除最近最少使用的key(這個是最經常使用的)
3)allkeys-random:當內存不足以容納新寫入數據時,在鍵空間中,隨機移除某個key,這個通常沒人用吧,爲啥要隨機,確定是把最近最少使用的key給幹掉啊
4)volatile-lru:當內存不足以容納新寫入數據時,在設置了過時時間的鍵空間中,移除最近最少使用的key(這個通常不太合適)
5)volatile-random:當內存不足以容納新寫入數據時,在設置了過時時間的鍵空間中,隨機移除某個key
6)volatile-ttl:當內存不足以容納新寫入數據時,在設置了過時時間的鍵空間中,有更早過時時間的key優先移除

你寫的數據太多,內存滿了,或者觸發了什麼條件,redis lru,自動給你清理掉了一些最近不多使用的數據

現場手寫最原始的LRU算法,那個代碼量太大了,我以爲不太現實

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); // 這塊就是設置一個hashmap的初始大小,同時最後一個true指的是讓linkedhashmap按照訪問順序來進行排序,最近訪問的放在頭,最老訪問的就在尾
CACHE_SIZE = cacheSize;
}

@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > CACHE_SIZE; // 這個意思就是說當map中的數據量大於指定的緩存個數的時候,就自動刪除最老的數據
}

}

 

5、redis是如何實現高性能高併發
1)在項目中緩存是如何使用的?
這個,你結合你本身項目的業務來,你若是用了那恭喜你,你若是沒用那很差意思,你硬加也得加一個場景吧
(2)爲啥在項目裏要用緩存呢?
用緩存,主要是倆用途,高性能和高併發

1)高性能
假設這麼個場景,你有個操做,一個請求過來,吭哧吭哧你各類亂七八糟操做mysql,半天查出來一個結果,耗時600ms。可是這個結果可能接下來幾個小時都不會變了,或者變了也能夠不用當即反饋給用戶。那麼此時咋辦?
緩存啊,折騰600ms查出來的結果,扔緩存裏,一個key對應一個value,下次再有人查,別走mysql折騰600ms了。直接從緩存裏,經過一個key查出來一個value,2ms搞定。性能提高300倍。
這就是所謂的高性能。
就是把你一些複雜操做耗時查出來的結果,若是肯定後面不咋變了,而後可是立刻還有不少讀請求,那麼直接結果放緩存,後面直接讀緩存就行了。

2)高併發
mysql這麼重的數據庫,壓根兒設計不是讓你玩兒高併發的,雖然也能夠玩兒,可是自然支持很差。mysql單機支撐到2000qps也開始容易報警了。
因此要是你有個系統,高峯期一秒鐘過來的請求有1萬,那一個mysql單機絕對會死掉。你這個時候就只能上緩存,把不少數據放緩存,別放mysql。緩存功能簡單,說白了就是key-value式操做,單機支撐的併發量輕鬆一秒幾萬十幾萬,支撐高併發so easy。單機承載併發量是mysql單機的幾十倍。

3)因此你要結合這倆場景考慮一下,你爲啥要用緩存?
通常不少同窗項目裏沒啥高併發場景,那就別折騰了,直接用高性能那個場景吧,就思考有沒有能夠緩存結果的複雜查詢場景,後續能夠大幅度提高性能,優化用戶體驗,有,就說這個理由,沒有??那你也得編一個出來吧,否則你不是在搞笑麼

(3)用了緩存以後會有啥不良的後果?
1)緩存與數據庫雙寫不一致
2)緩存雪崩
3)緩存穿透
4)緩存併發競爭


6、如何保證Redis的高併發和高可用?redis的主從複製原理能介紹一下麼?redis的哨兵原理能介紹一下麼?

redis如何經過讀寫分離來承載讀請求QPS超過10萬+?

一、redis高併發跟整個系統的高併發之間的關係
redis,你要搞高併發的話,不可避免,要把底層的緩存搞得很好
mysql,高併發,作到了,那麼也是經過一系列複雜的分庫分表,訂單系統,事務要求的,QPS到幾萬,比較高了

要作一些電商的商品詳情頁,真正的超高併發,QPS上十萬,甚至是百萬,一秒鐘百萬的請求量
光是redis是不夠的,可是redis是整個大型的緩存架構中,支撐高併發的架構裏面,很是重要的一個環節
首先,你的底層的緩存中間件,緩存系統,必須可以支撐的起咱們說的那種高併發,其次,再通過良好的總體的緩存架構的設計(多級緩存架構、熱點緩存),支撐真正的上十萬,甚至上百萬的高併發

二、redis不能支撐高併發的瓶頸在哪裏?
單機

三、若是redis要支撐超過10萬+的併發,那應該怎麼作?
單機的redis幾乎不太可能說QPS超過10萬+,除非一些特殊狀況,好比你的機器性能特別好,配置特別高,物理機,維護作的特別好,並且你的總體的操做不是太複雜
單機在幾萬
讀寫分離,通常來講,對緩存,通常都是用來支撐讀高併發的,寫的請求是比較少的,可能寫請求也就一秒鐘幾千,一兩千
大量的請求都是讀,一秒鐘二十萬次讀
讀寫分離
主從架構 -> 讀寫分離 -> 支撐10萬+讀QPS的架構
四、接下來要講解的一個topic
redis replication
redis主從架構 -> 讀寫分離架構 -> 可支持水平擴展的讀高併發架構


03_redis replication以及master持久化對主從架構的安全意義
課程大綱
一、圖解redis replication基本原理
二、redis replication的核心機制
三、master持久化對於主從架構的安全保障的意義
redis replication -> 主從架構 -> 讀寫分離 -> 水平擴容支撐讀高併發
redis replication的最最基本的原理,鋪墊


二、redis replication的核心機制
(1)redis採用異步方式複製數據到slave節點,不過redis 2.8開始,slave node會週期性地確認本身每次複製的數據量
(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能夠提升讀的吞吐量
slave,高可用性,有很大的關係


redis replication以及master持久化對主從架構的安全意義
課程大綱
一、圖解redis replication基本原理
二、redis replication的核心機制
三、master持久化對於主從架構的安全保障的意義
redis replication -> 主從架構 -> 讀寫分離 -> 水平擴容支撐讀高併發
redis replication的最最基本的原理,鋪墊

------------------------------------------------------------------------

一、圖解redis replication基本原理

------------------------------------------------------------------------

二、redis replication的核心機制
(1)redis採用異步方式複製數據到slave節點,不過redis 2.8開始,slave node會週期性地確認本身每次複製的數據量
(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能夠提升讀的吞吐量
slave,高可用性,有很大的關係
------------------------------------------------------------------------

三、master持久化對於主從架構的安全保障的意義
若是採用了主從架構,那麼建議必須開啓master node的持久化!
不建議用slave node做爲master node的數據熱備,由於那樣的話,若是你關掉master的持久化,可能在master宕機重啓的時候數據是空的,而後可能一通過複製,salve node數據也丟了
master -> RDB和AOF都關閉了 -> 所有在內存中
master宕機,重啓,是沒有本地數據能夠恢復的,而後就會直接認爲本身IDE數據是空的
master就會將空的數據集同步到slave上去,全部slave的數據所有清空
100%的數據丟失
master節點,必需要使用持久化機制

第二個,master的各類備份方案,要不要作,萬一說本地的全部文件丟失了; 從備份中挑選一份rdb去恢復master; 這樣才能確保master啓動的時候,是有數據的

即便採用了後續講解的高可用機制,slave node能夠自動接管master node,可是也可能sentinal尚未檢測到master failure,master node就自動重啓了,仍是可能致使上面的全部slave node數據清空故障


04_redis主從複製原理、斷點續傳、無磁盤化複製、過時key處理

一、主從架構的核心原理

當啓動一個slave node的時候,它會發送一個PSYNC命令給master node
若是這是slave node從新鏈接master node,那麼master node僅僅會複製給slave部分缺乏的數據; 不然若是是slave node第一次鏈接master node,那麼會觸發一次full resynchronization
開始full resynchronization的時候,master會啓動一個後臺線程,開始生成一份RDB快照文件,同時還會將從客戶端收到的全部寫命令緩存在內存中。RDB文件生成完畢以後,master會將這個RDB發送給slave,slave會先寫入本地磁盤,而後再從本地磁盤加載到內存中。而後master會將內存中緩存的寫命令發送給slave,slave也會同步這些數據。
slave node若是跟master node有網絡故障,斷開了鏈接,會自動重連。master若是發現有多個slave node都來從新鏈接,僅僅會啓動一個rdb save操做,用一份數據服務全部slave node。

二、主從複製的斷點續傳
從redis 2.8開始,就支持主從複製的斷點續傳,若是主從複製過程當中,網絡鏈接斷掉了,那麼能夠接着上次複製的地方,繼續複製下去,而不是從頭開始複製一份

master node會在內存中常見一個backlog,master和slave都會保存一個replica offset還有一個master id,offset就是保存在backlog中的。若是master和slave網絡鏈接斷掉了,slave會讓master從上次的replica offset開始繼續複製
可是若是沒有找到對應的offset,那麼就會執行一次resynchronization

三、無磁盤化複製
master在內存中直接建立rdb,而後發送給slave,不會在本身本地落地磁盤了
repl-diskless-sync
repl-diskless-sync-delay,等待必定時長再開始複製,由於要等更多slave從新鏈接過來

四、過時key處理
slave不會過時key,只會等待master過時key。若是master過時了一個key,或者經過LRU淘汰了一個key,那麼會模擬一條del命令發送給slave。


05_redis replication的完整流運行程和原理的再次深刻剖析

一、複製的完整流程

(1)slave node啓動,僅僅保存master node的信息,包括master node的host和ip,可是複製流程沒開始
master host和ip是從哪兒來的,redis.conf裏面的slaveof配置的

(2)slave node內部有個定時任務,每秒檢查是否有新的master node要鏈接和複製,若是發現,就跟master node創建socket網絡鏈接
(3)slave node發送ping命令給master node
(4)口令認證,若是master設置了requirepass,那麼salve node必須發送masterauth的口令過去進行認證
(5)master node第一次執行全量複製,將全部數據發給slave node
(6)master node後續持續將寫命令,異步複製給slave node

二、數據同步相關的核心機制

指的就是第一次slave鏈接msater的時候,執行的全量複製,那個過程裏面你的一些細節的機制

(1)master和slave都會維護一個offset

master會在自身不斷累加offset,slave也會在自身不斷累加offset
slave每秒都會上報本身的offset給master,同時master也會保存每一個slave的offset

這個倒不是說特定就用在全量複製的,主要是master和slave都要知道各自的數據的offset,才能知道互相之間的數據不一致的狀況

(2)backlog

master node有一個backlog,默認是1MB大小
master node給slave node複製數據時,也會將數據在backlog中同步寫一份
backlog主要是用來作全量複製中斷候的增量複製的

(3)master run id

info server,能夠看到master run id
若是根據host+ip定位master node,是不靠譜的,若是master node重啓或者數據出現了變化,那麼slave node應該根據不一樣的run id區分,run id不一樣就作全量複製
若是須要不更改run id重啓redis,能夠使用redis-cli debug reload命令

(4)psync

從節點使用psync從master node進行復制,psync runid offset
master node會根據自身的狀況返回響應信息,多是FULLRESYNC runid offset觸發全量複製,多是CONTINUE觸發增量複製

三、全量複製
(1)master執行bgsave,在本地生成一份rdb快照文件
(2)master node將rdb快照文件發送給salve node,若是rdb複製時間超過60秒(repl-timeout),那麼slave node就會認爲複製失敗,能夠適當調節大這個參數
(3)對於千兆網卡的機器,通常每秒傳輸100MB,6G文件,極可能超過60s
(4)master node在生成rdb時,會將全部新的寫命令緩存在內存中,在salve node保存了rdb以後,再將新的寫命令複製給salve node
(5)client-output-buffer-limit slave 256MB 64MB 60,若是在複製期間,內存緩衝區持續消耗超過64MB,或者一次性超過256MB,那麼中止複製,複製失敗
(6)slave node接收到rdb以後,清空本身的舊數據,而後從新加載rdb到本身的內存中,同時基於舊的數據版本對外提供服務
(7)若是slave node開啓了AOF,那麼會當即執行BGREWRITEAOF,重寫AOF

rdb生成、rdb經過網絡拷貝、slave舊數據的清理、slave aof rewrite,很耗費時間
若是複製的數據量在4G~6G之間,那麼極可能全量複製時間消耗到1分半到2分鐘

四、增量複製
(1)若是全量複製過程當中,master-slave網絡鏈接斷掉,那麼salve從新鏈接master時,會觸發增量複製
(2)master直接從本身的backlog中獲取部分丟失的數據,發送給slave node,默認backlog就是1MB
(3)msater就是根據slave發送的psync中的offset來從backlog中獲取數據的

五、heartbeat
主從節點互相都會發送heartbeat信息
master默認每隔10秒發送一次heartbeat,salve node每隔1秒發送一個heartbeat

六、異步複製
master每次接收到寫命令以後,如今內部寫入數據,而後異步發送給slave node

06_redis主從架構下如何才能作到99.99%的高可用性?
一、什麼是99.99%高可用?
架構上,高可用性,99.99%的高可用性
講的學術,99.99%,公式,系統可用的時間 / 系統故障的時間,365天,在365天 * 99.99%的時間內,你的系統都是能夠嘩嘩對外提供服務的,那就是高可用性,99.99%
系統可用的時間 / 總的時間 = 高可用性,而後會對各類時間的概念,說一大堆解釋
二、redis不可用是什麼?單實例不可用?主從架構不可用?不可用的後果是什麼?
三、redis怎麼才能作到高可用?


07_redis哨兵架構的相關基礎知識的講解

一、哨兵的介紹

sentinal,中文名是哨兵
哨兵是redis集羣架構中很是重要的一個組件,主要功能以下

(1)集羣監控,負責監控redis master和slave進程是否正常工做
(2)消息通知,若是某個redis實例有故障,那麼哨兵負責發送消息做爲報警通知給管理員
(3)故障轉移,若是master node掛掉了,會自動轉移到slave node上
(4)配置中心,若是故障轉移發生了,通知client客戶端新的master地址

哨兵自己也是分佈式的,做爲一個哨兵集羣去運行,互相協同工做
(1)故障轉移時,判斷一個master node是宕機了,須要大部分的哨兵都贊成才行,涉及到了分佈式選舉的問題
(2)即便部分哨兵節點掛掉了,哨兵集羣仍是能正常工做的,由於若是一個做爲高可用機制重要組成部分的故障轉移系統自己是單點的,那就很坑爹了
目前採用的是sentinal 2版本,sentinal 2相對於sentinal 1來講,重寫了不少代碼,主要是讓故障轉移的機制和算法變得更加健壯和簡單

二、哨兵的核心知識

(1)哨兵至少須要3個實例,來保證本身的健壯性
(2)哨兵 + redis主從的部署架構,是不會保證數據零丟失的,只能保證redis集羣的高可用性
(3)對於哨兵 + redis主從這種複雜的部署架構,儘可能在測試環境和生產環境,都進行充足的測試和演練

三、爲何redis哨兵集羣只有2個節點沒法正常工做?

哨兵集羣必須部署2個以上節點
若是哨兵集羣僅僅部署了個2個哨兵實例,quorum=1

+----+ +----+
| M1 |---------| R1 |
| S1 | | S2 |
+----+ +----+

Configuration: quorum = 1

master宕機,s1和s2中只要有1個哨兵認爲master宕機就能夠還行切換,同時s1和s2中會選舉出一個哨兵來執行故障轉移
同時這個時候,須要majority,也就是大多數哨兵都是運行的,2個哨兵的majority就是2(2的majority=2,3的majority=2,5的majority=3,4的majority=2),2個哨兵都運行着,就能夠容許執行故障轉移
可是若是整個M1和S1運行的機器宕機了,那麼哨兵只有1個了,此時就沒有majority來容許執行故障轉移,雖然另一臺機器還有一個R1,可是故障轉移不會執行

四、經典的3節點哨兵集羣

+----+
| M1 |
| S1 |
+----+
|
+----+ | +----+
| R2 |----+----| R3 |
| S2 | | S3 |
+----+ +----+

Configuration: quorum = 2,majority
若是M1所在機器宕機了,那麼三個哨兵還剩下2個,S2和S3能夠一致認爲master宕機,而後選舉出一個來執行故障轉移
同時3個哨兵的majority是2,因此還剩下的2個哨兵運行着,就能夠容許執行故障轉移

 

08_redis哨兵主備切換的數據丟失問題:異步複製、集羣腦裂
課程大綱

一、兩種數據丟失的狀況
二、解決異步複製和腦裂致使的數據丟失

------------------------------------------------------------------

一、兩種數據丟失的狀況
主備切換的過程,可能會致使數據丟失

(1)異步複製致使的數據丟失
由於master -> slave的複製是異步的,因此可能有部分數據還沒複製到slave,master就宕機了,此時這些部分數據就丟失了

(2)腦裂致使的數據丟失

腦裂,也就是說,某個master所在機器忽然脫離了正常的網絡,跟其餘slave機器不能鏈接,可是實際上master還運行着
此時哨兵可能就會認爲master宕機了,而後開啓選舉,將其餘slave切換成了master
這個時候,集羣裏就會有兩個master,也就是所謂的腦裂
此時雖然某個slave被切換成了master,可是可能client還沒來得及切換到新的master,還繼續寫向舊master的數據可能也丟失了
所以舊master再次恢復的時候,會被做爲一個slave掛到新的master上去,本身的數據會清空,從新重新的master複製數據

------------------------------------------------------------------

二、解決異步複製和腦裂致使的數據丟失

min-slaves-to-write 1
min-slaves-max-lag 10

要求至少有1個slave,數據複製和同步的延遲不能超過10秒
若是說一旦全部的slave,數據複製和同步的延遲都超過了10秒鐘,那麼這個時候,master就不會再接收任何請求了
上面兩個配置能夠減小異步複製和腦裂致使的數據丟失

(1)減小異步複製的數據丟失
有了min-slaves-max-lag這個配置,就能夠確保說,一旦slave複製數據和ack延時太長,就認爲可能master宕機後損失的數據太多了,那麼就拒絕寫請求,這樣能夠把master宕機時因爲部分數據未同步到slave致使的數據丟失下降的可控範圍內

(2)減小腦裂的數據丟失
若是一個master出現了腦裂,跟其餘slave丟了鏈接,那麼上面兩個配置能夠確保說,若是不能繼續給指定數量的slave發送數據,並且slave超過10秒沒有給本身ack消息,那麼就直接拒絕客戶端的寫請求
這樣腦裂後的舊master就不會接受client的新數據,也就避免了數據丟失
上面的配置就確保了,若是跟任何一個slave丟了鏈接,在10秒後發現沒有slave給本身ack,那麼就拒絕新的寫請求
所以在腦裂場景下,最多就丟失10秒的數據


一、sdown和odown轉換機制

sdown和odown兩種失敗狀態
sdown是主觀宕機,就一個哨兵若是本身以爲一個master宕機了,那麼就是主觀宕機
odown是客觀宕機,若是quorum數量的哨兵都以爲一個master宕機了,那麼就是客觀宕機
sdown達成的條件很簡單,若是一個哨兵ping一個master,超過了is-master-down-after-milliseconds指定的毫秒數以後,就主觀認爲master宕機
sdown到odown轉換的條件很簡單,若是一個哨兵在指定時間內,收到了quorum指定數量的其餘哨兵也認爲那個master是sdown了,那麼就認爲是odown了,客觀認爲master宕機

二、哨兵集羣的自動發現機制

哨兵互相之間的發現,是經過redis的pub/sub系統實現的,每一個哨兵都會往__sentinel__:hello這個channel裏發送一個消息,這時候全部其餘哨兵均可以消費到這個消息,並感知到其餘的哨兵的存在
每隔兩秒鐘,每一個哨兵都會往本身監控的某個master+slaves對應的__sentinel__:hello channel裏發送一個消息,內容是本身的host、ip和runid還有對這個master的監控配置
每一個哨兵也會去監聽本身監控的每一個master+slaves對應的__sentinel__:hello channel,而後去感知到一樣在監聽這個master+slaves的其餘哨兵的存在
每一個哨兵還會跟其餘哨兵交換對master的監控配置,互相進行監控配置的同步

三、slave配置的自動糾正

哨兵會負責自動糾正slave的一些配置,好比slave若是要成爲潛在的master候選人,哨兵會確保slave在複製現有master的數據; 若是slave鏈接到了一個錯誤的master上,好比故障轉移以後,那麼哨兵會確保它們鏈接到正確的master上

四、slave->master選舉算法

若是一個master被認爲odown了,並且majority哨兵都容許了主備切換,那麼某個哨兵就會執行主備切換操做,此時首先要選舉一個slave來
會考慮slave的一些信息

(1)跟master斷開鏈接的時長
(2)slave優先級
(3)複製offset
(4)run id

若是一個slave跟master斷開鏈接已經超過了down-after-milliseconds的10倍,外加master宕機的時長,那麼slave就被認爲不適合選舉爲master
(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state

接下來會對slave進行排序
(1)按照slave優先級進行排序,slave priority越低,優先級就越高
(2)若是slave priority相同,那麼看replica offset,哪一個slave複製了越多的數據,offset越靠後,優先級就越高
(3)若是上面兩個條件都相同,那麼選擇一個run id比較小的那個slave

五、quorum和majority

每次一個哨兵要作主備切換,首先須要quorum數量的哨兵認爲odown,而後選舉出一個哨兵來作切換,這個哨兵還得獲得majority哨兵的受權,才能正式執行切換
若是quorum < majority,好比5個哨兵,majority就是3,quorum設置爲2,那麼就3個哨兵受權就能夠執行切換
可是若是quorum >= majority,那麼必須quorum數量的哨兵都受權,好比5個哨兵,quorum是5,那麼必須5個哨兵都贊成受權,才能執行切換

六、configuration epoch

哨兵會對一套redis master+slave進行監控,有相應的監控的配置
執行切換的那個哨兵,會從要切換到的新master(salve->master)那裏獲得一個configuration epoch,這就是一個version號,每次切換的version號都必須是惟一的
若是第一個選舉出的哨兵切換失敗了,那麼其餘哨兵,會等待failover-timeout時間,而後接替繼續執行切換,此時會從新獲取一個新的configuration epoch,做爲新的version號

七、configuraiton傳播

哨兵完成切換以後,會在本身本地更新生成最新的master配置,而後同步給其餘的哨兵,就是經過以前說的pub/sub消息機制
這裏以前的version號就很重要了,由於各類消息都是經過一個channel去發佈和監聽的,因此一個哨兵完成一次新的切換以後,新的master配置是跟着新的version號的
其餘的哨兵都是根據版本號的大小來更新本身的master配置的


三、面試題剖析

就是若是你用redis緩存技術的話,確定要考慮如何用redis來加多臺機器,保證redis是高併發的,還有就是如何讓Redis保證本身不是掛掉之後就直接死掉了,redis高可用

我這裏會選用我以前講解過這一塊內容,redis高併發、高可用、緩存一致性
redis高併發:主從架構,一主多從,通常來講,不少項目其實就足夠了,單主用來寫入數據,單機幾萬QPS,多從用來查詢數據,多個從實例能夠提供每秒10萬的QPS。

redis高併發的同時,還須要容納大量的數據:一主多從,每一個實例都容納了完整的數據,好比redis主就10G的內存量,其實你就最對只能容納10g的數據量。若是你的緩存要容納的數據量很大,達到了幾十g,甚至幾百g,或者是幾t,那你就須要redis集羣,並且用redis集羣以後,能夠提供可能每秒幾十萬的讀寫併發。
redis高可用:若是你作主從架構部署,其實就是加上哨兵就能夠了,就能夠實現,任何一個實例宕機,自動會進行主備切換。

 

7、併發競爭
redis的併發競爭問題是什麼?如何解決這個問題?瞭解Redis事務的CAS方案嗎?
多客戶端同時併發寫一個key,可能原本應該先到的數據後到了,致使數據版本錯了。或者是多客戶端同時獲取一個key,修改值以後再寫回去,只要順序錯了,數據就錯了。並且redis本身就有自然解決這個問題的CAS類的樂觀鎖方案

zk 分佈式鎖 確保同一時間,只能有一個系統實例在操做某個key,每次要寫以前,先判斷一下當前這個value的時間戳是否比緩存裏的value的時間戳更新,若是更新,那麼能夠寫

寫入緩存的數據,都是從mysql裏查出來的,都得寫入mysql中,寫入mysql中的必須保存一個時間戳


8、redis的線程模型
redis和memcached有什麼區別?redis的線程模型是什麼?爲何單線程的redis比多線程的memcached效率要高得多(爲何redis是單線程的可是還能夠支撐高併發)?


1)文件事件處理器
redis基於reactor模式開發了網絡事件處理器,這個處理器叫作文件事件處理器,file event handler。這個文件事件處理器,是單線程的,redis才叫作單線程的模型,採用IO多路複用機制同時監聽多個socket,根據socket上的事件來選擇對應的事件處理器來處理這個事件。
若是被監聽的socket準備好執行accept、read、write、close等操做的時候,跟操做對應的文件事件就會產生,這個時候文件事件處理器就會調用以前關聯好的事件處理器來處理這個事件。
文件事件處理器是單線程模式運行的,可是經過IO多路複用機制監聽多個socket,能夠實現高性能的網絡通訊模型,又能夠跟內部其餘單線程的模塊進行對接,保證了redis內部的線程模型的簡單性。
文件事件處理器的結構包含4個部分:多個socket,IO多路複用程序,文件事件分派器,事件處理器(命令請求處理器、命令回覆處理器、鏈接應答處理器,等等)。
多個socket可能併發的產生不一樣的操做,每一個操做對應不一樣的文件事件,可是IO多路複用程序會監聽多個socket,可是會將socket放入一個隊列中排隊,每次從隊列中取出一個socket給事件分派器,事件分派器把socket給對應的事件處理器。
而後一個socket的事件處理完以後,IO多路複用程序纔會將隊列中的下一個socket給事件分派器。文件事件分派器會根據每一個socket當前產生的事件,來選擇對應的事件處理器來處理。

2)文件事件

當socket變得可讀時(好比客戶端對redis執行write操做,或者close操做),或者有新的能夠應答的sccket出現時(客戶端對redis執行connect操做),socket就會產生一個AE_READABLE事件。
當socket變得可寫的時候(客戶端對redis執行read操做),socket會產生一個AE_WRITABLE事件。
IO多路複用程序能夠同時監聽AE_REABLE和AE_WRITABLE兩種事件,要是一個socket同時產生了AE_READABLE和AE_WRITABLE兩種事件,那麼文件事件分派器優先處理AE_REABLE事件,而後纔是AE_WRITABLE事件。

3)文件事件處理器

若是是客戶端要鏈接redis,那麼會爲socket關聯鏈接應答處理器
若是是客戶端要寫數據到redis,那麼會爲socket關聯命令請求處理器
若是是客戶端要從redis讀數據,那麼會爲socket關聯命令回覆處理器

4)客戶端與redis通訊的一次流程

在redis啓動初始化的時候,redis會將鏈接應答處理器跟AE_READABLE事件關聯起來,接着若是一個客戶端跟redis發起鏈接,此時會產生一個AE_READABLE事件,而後由鏈接應答處理器來處理跟客戶端創建鏈接,建立客戶端對應的socket,同時將這個socket的AE_READABLE事件跟命令請求處理器關聯起來。
當客戶端向redis發起請求的時候(無論是讀請求仍是寫請求,都同樣),首先就會在socket產生一個AE_READABLE事件,而後由對應的命令請求處理器來處理。這個命令請求處理器就會從socket中讀取請求相關數據,而後進行執行和處理。
接着redis這邊準備好了給客戶端的響應數據以後,就會將socket的AE_WRITABLE事件跟命令回覆處理器關聯起來,當客戶端這邊準備好讀取響應數據時,就會在socket上產生一個AE_WRITABLE事件,會由對應的命令回覆處理器來處理,就是將準備好的響應數據寫入socket,供客戶端來讀取。
命令回覆處理器寫完以後,就會刪除這個socket的AE_WRITABLE事件和命令回覆處理器的關聯關係。

(3)爲啥redis單線程模型也能效率這麼高?

1)純內存操做
2)核心是基於非阻塞的IO多路複用機制
3)單線程反而避免了多線程的頻繁上下文切換問題(百度)

實際就是協程的操做模式

 

9、redis集羣模式原理
redis集羣模式的工做原理能說一下麼?在集羣模式下,redis的key是如何尋址的?分佈式尋址都有哪些算法?瞭解一致性hash算法嗎

redis cluster對你來個幾連炮。要是你沒用過redis cluster,正常,之前不少人用codis之類的客戶端來支持集羣,可是起碼你得研究一下redis cluster吧。

02_redis如何在保持讀寫分離+高可用的架構下,還能橫向擴容支撐1T+海量數據

一、單機redis在海量數據面前的瓶頸
二、怎麼纔可以突破單機瓶頸,讓redis支撐海量數據?
三、redis的集羣架構

redis cluster

支撐N個redis master node,每一個master node均可以掛載多個slave node
讀寫分離的架構,對於每一個master來講,寫就寫到master,而後讀就從mater對應的slave去讀
高可用,由於每一個master都有salve節點,那麼若是mater掛掉,redis cluster這套機制,就會自動將某個slave切換成master
redis cluster(多master + 讀寫分離 + 高可用)
咱們只要基於redis cluster去搭建redis集羣便可,不須要手工去搭建replication複製+主從架構+讀寫分離+哨兵集羣+高可用

四、redis cluster vs. replication + sentinal

若是你的數據量不多,主要是承載高併發高性能的場景,好比你的緩存通常就幾個G,單機足夠了
replication,一個mater,多個slave,要幾個slave跟你的要求的讀吞吐量有關係,而後本身搭建一個sentinal集羣,去保證redis主從架構的高可用性,就能夠了
redis cluster,主要是針對海量數據+高併發+高可用的場景,海量數據,若是你的數據量很大,那麼建議就用redis cluster

03_數據分佈算法:hash+一致性hash+redis cluster的hash slot

講解分佈式數據存儲的核心算法,數據分佈的算法
hash算法 -> 一致性hash算法(memcached) -> redis cluster,hash slot算法
用不一樣的算法,就決定了在多個master節點的時候,數據如何分佈到這些節點上去,解決這個問題

一、redis cluster介紹

redis cluster
(1)自動將數據進行分片,每一個master上放一部分數據
(2)提供內置的高可用支持,部分master不可用時,仍是能夠繼續工做的

在redis cluster架構下,每一個redis要放開兩個端口號,好比一個是6379,另一個就是加10000的端口號,好比16379
16379端口號是用來進行節點間通訊的,也就是cluster bus的東西,集羣總線。cluster bus的通訊,用來進行故障檢測,配置更新,故障轉移受權
cluster bus用了另一種二進制的協議,主要用於節點間進行高效的數據交換,佔用更少的網絡帶寬和處理時間

二、最老土的hash算法和弊端(大量緩存重建)
三、一致性hash算法(自動緩存遷移)+虛擬節點(自動負載均衡)
四、redis cluster的hash slot算法

redis cluster有固定的16384個hash slot,對每一個key計算CRC16值,而後對16384取模,能夠獲取key對應的hash slot
redis cluster中每一個master都會持有部分slot,好比有3個master,那麼可能每一個master持有5000多個hash slot
hash slot讓node的增長和移除很簡單,增長一個master,就將其餘master的hash slot移動部分過去,減小一個master,就將它的hash slot移動到其餘master上去
移動hash slot的成本是很是低的
客戶端的api,能夠對指定的數據,讓他們走同一個hash slot,經過hash tag來實現

04_redis cluster的核心原理分析:gossip通訊、jedis smart定位、主備切換


1、節點間的內部通訊機制

一、基礎通訊原理

(1)redis cluster節點間採起gossip協議進行通訊

跟集中式不一樣,不是將集羣元數據(節點信息,故障,等等)集中存儲在某個節點上,而是互相之間不斷通訊,保持整個集羣全部節點的數據是完整的
維護集羣的元數據用得,集中式,一種叫作gossip
集中式:好處在於,元數據的更新和讀取,時效性很是好,一旦元數據出現了變動,當即就更新到集中式的存儲中,其餘節點讀取的時候當即就能夠感知到; 很差在於,全部的元數據的跟新壓力所有集中在一個地方,可能會致使元數據的存儲有壓力
gossip:好處在於,元數據的更新比較分散,不是集中在一個地方,更新請求會陸陸續續,打到全部節點上去更新,有必定的延時,下降了壓力; 缺點,元數據更新有延時,可能致使集羣的一些操做會有一些滯後
咱們剛纔作reshard,去作另一個操做,會發現說,configuration error,達成一致

(2)10000端口

每一個節點都有一個專門用於節點間通訊的端口,就是本身提供服務的端口號+10000,好比7001,那麼用於節點間通訊的就是17001端口
每隔節點每隔一段時間都會往另外幾個節點發送ping消息,同時其餘幾點接收到ping以後返回pong

(3)交換的信息
故障信息,節點的增長和移除,hash slot信息,等等

二、gossip協議

gossip協議包含多種消息,包括ping,pong,meet,fail,等等
meet: 某個節點發送meet給新加入的節點,讓新節點加入集羣中,而後新節點就會開始與其餘節點進行通訊
redis-trib.rb add-node
其實內部就是發送了一個gossip meet消息,給新加入的節點,通知那個節點去加入咱們的集羣
ping: 每一個節點都會頻繁給其餘節點發送ping,其中包含本身的狀態還有本身維護的集羣元數據,互相經過ping交換元數據
每一個節點每秒都會頻繁發送ping給其餘的集羣,ping,頻繁的互相之間交換數據,互相進行元數據的更新
pong: 返回ping和meet,包含本身的狀態和其餘信息,也能夠用於信息廣播和更新
fail: 某個節點判斷另外一個節點fail以後,就發送fail給其餘節點,通知其餘節點,指定的節點宕機了

三、ping消息深刻

ping很頻繁,並且要攜帶一些元數據,因此可能會加劇網絡負擔
每一個節點每秒會執行10次ping,每次會選擇5個最久沒有通訊的其餘節點
固然若是發現某個節點通訊延時達到了cluster_node_timeout / 2,那麼當即發送ping,避免數據交換延時過長,落後的時間太長了
好比說,兩個節點之間都10分鐘沒有交換數據了,那麼整個集羣處於嚴重的元數據不一致的狀況,就會有問題
因此cluster_node_timeout能夠調節,若是調節比較大,那麼會下降發送的頻率
每次ping,一個是帶上本身節點的信息,還有就是帶上1/10其餘節點的信息,發送出去,進行數據交換
至少包含3個其餘節點的信息,最多包含總節點-2個其餘節點的信息

-------------------------------------------------------------------------------------------------------

2、面向集羣的jedis內部實現原理

開發,jedis,redis的java client客戶端,redis cluster,jedis cluster api
jedis cluster api與redis cluster集羣交互的一些基本原理

一、基於重定向的客戶端

redis-cli -c,自動重定向

(1)請求重定向

客戶端可能會挑選任意一個redis實例去發送命令,每一個redis實例接收到命令,都會計算key對應的hash slot
若是在本地就在本地處理,不然返回moved給客戶端,讓客戶端進行重定向
cluster keyslot mykey,能夠查看一個key對應的hash slot是什麼
用redis-cli的時候,能夠加入-c參數,支持自動的請求重定向,redis-cli接收到moved以後,會自動重定向到對應的節點執行命令

(2)計算hash slot

計算hash slot的算法,就是根據key計算CRC16值,而後對16384取模,拿到對應的hash slot
用hash tag能夠手動指定key對應的slot,同一個hash tag下的key,都會在一個hash slot中,好比set mykey1:{100}和set mykey2:{100}

(3)hash slot查找
節點間經過gossip協議進行數據交換,就知道每一個hash slot在哪一個節點上

二、smart jedis

(1)什麼是smart jedis

基於重定向的客戶端,很消耗網絡IO,由於大部分狀況下,可能都會出現一次請求重定向,才能找到正確的節點
因此大部分的客戶端,好比java redis客戶端,就是jedis,都是smart的
本地維護一份hashslot -> node的映射表,緩存,大部分狀況下,直接走本地緩存就能夠找到hashslot -> node,不須要經過節點進行moved重定向

(2)JedisCluster的工做原理

在JedisCluster初始化的時候,就會隨機選擇一個node,初始化hashslot -> node映射表,同時爲每一個節點建立一個JedisPool鏈接池
每次基於JedisCluster執行操做,首先JedisCluster都會在本地計算key的hashslot,而後在本地映射表找到對應的節點
若是那個node正好仍是持有那個hashslot,那麼就ok; 若是說進行了reshard這樣的操做,可能hashslot已經不在那個node上了,就會返回moved
若是JedisCluter API發現對應的節點返回moved,那麼利用該節點的元數據,更新本地的hashslot -> node映射表緩存
重複上面幾個步驟,直到找到對應的節點,若是重試超過5次,那麼就報錯,JedisClusterMaxRedirectionException
jedis老版本,可能會出如今集羣某個節點故障還沒完成自動切換恢復時,頻繁更新hash slot,頻繁ping節點檢查活躍,致使大量網絡IO開銷
jedis最新版本,對於這些過分的hash slot更新和ping,都進行了優化,避免了相似問題

(3)hashslot遷移和ask重定向

若是hash slot正在遷移,那麼會返回ask重定向給jedis
jedis接收到ask重定向以後,會從新定位到目標節點去執行,可是由於ask發生在hash slot遷移過程當中,因此JedisCluster API收到ask是不會更新hashslot本地緩存
已經能夠肯定說,hashslot已經遷移完了,moved是會更新本地hashslot->node映射表緩存的

-------------------------------------------------------------------------------------------------------

3、高可用性與主備切換原理
redis cluster的高可用的原理,幾乎跟哨兵是相似的

一、判斷節點宕機

若是一個節點認爲另一個節點宕機,那麼就是pfail,主觀宕機
若是多個節點都認爲另一個節點宕機了,那麼就是fail,客觀宕機,跟哨兵的原理幾乎同樣,sdown,odown
在cluster-node-timeout內,某個節點一直沒有返回pong,那麼就被認爲pfail
若是一個節點認爲某個節點pfail了,那麼會在gossip ping消息中,ping給其餘節點,若是超過半數的節點都認爲pfail了,那麼就會變成fail

二、從節點過濾

對宕機的master node,從其全部的slave node中,選擇一個切換成master node
檢查每一個slave node與master node斷開鏈接的時間,若是超過了cluster-node-timeout * cluster-slave-validity-factor,那麼就沒有資格切換成master
這個也是跟哨兵是同樣的,從節點超時過濾的步驟

三、從節點選舉

哨兵:對全部從節點進行排序,slave priority,offset,run id
每一個從節點,都根據本身對master複製數據的offset,來設置一個選舉時間,offset越大(複製數據越多)的從節點,選舉時間越靠前,優先進行選舉
全部的master node開始slave選舉投票,給要進行選舉的slave進行投票,若是大部分master node(N/2 + 1)都投票給了某個從節點,那麼選舉經過,那個從節點能夠切換成master
從節點執行主備切換,從節點切換爲主節點

四、與哨兵比較

整個流程跟哨兵相比,很是相似,因此說,redis cluster功能強大,直接集成了replication和sentinal的功能
沒有辦法去給你們深刻講解redis底層的設計的細節,核心原理和設計的細節,那個除非單獨開一門課,redis底層原理深度剖析,redis源碼
對於我們這個架構課來講,主要關注的是架構,不是底層的細節,對於架構來講,核心的原理的基本思路,是要梳理清晰的


10、redis的集羣部署架構

你的redis是主從架構?集羣架構?用了哪一種集羣方案?有沒有作高可用保證?有沒有開啓持久化機制確保能夠進行數據恢復?線上redis給幾個G的內存?設置了哪些參數?壓測後大家redis集羣承載多少QPS?

redis cluster,10臺機器,5臺機器部署了redis主實例,另外5臺機器部署了redis的從實例,每一個主實例掛了一個從實例,5個節點對外提供讀寫服務,每一個節點的讀寫高峯qps可能能夠達到每秒5萬,5臺機器最可能是25萬讀寫請求/s。
機器是什麼配置?32G內存+8核CPU+1T磁盤,可是分配給redis進程的是10g內存,通常線上生產環境,redis的內存儘可能不要超過10g,超過10g可能會有問題。
5臺機器對外提供讀寫,一共有50g內存。
由於每一個主實例都掛了一個從實例,因此是高可用的,任何一個主實例宕機,都會自動故障遷移,redis從實例會自動變成主實例繼續提供讀寫服務
你往內存裏寫的是什麼數據?每條數據的大小是多少?商品數據,每條數據是10kb。100條數據是1mb,10萬條數據是1g。常駐內存的是200萬條商品數據,佔用內存是20g,僅僅不到總內存的50%。
目前高峯期每秒就是3500左右的請求量
好比咱們吧,大型的公司,其實基礎架構的team,會負責緩存集羣的運維

 

10、redis的持久化
redis持久化機對於生產環境中的災難恢復的意義
一、故障發生的時候會怎麼樣
二、如何應對故障的發生

redis的持久化,RDB,AOF,區別,各自的特色是什麼,適合什麼場景
redis的企業級的持久化方案是什麼,是用來跟哪些企業級的場景結合起來使用的???
redis持久化的意義,在於故障恢復
好比你部署了一個redis,做爲cache緩存,固然也能夠保存一些較爲重要的數據
若是沒有持久化的話,redis遇到災難性故障的時候,就會丟失全部的數據
若是經過持久化將數據搞一份兒在磁盤上去,而後按期好比說同步和備份到一些雲存儲服務上去,那麼就能夠保證數據不丟失所有,仍是能夠恢復一部分數據回來的

一、RDB和AOF兩種持久化機制的介紹
二、RDB持久化機制的優勢
三、RDB持久化機制的缺點
四、AOF持久化機制的優勢
五、AOF持久化機制的缺點
六、RDB和AOF到底該如何選擇

企業級redis集羣架構:海量數據、高併發、高可用
持久化主要是作災難恢復,數據恢復,也能夠歸類到高可用的一個環節裏面去
好比你redis整個掛了,而後redis就不可用了,你要作的事情是讓redis變得可用,儘快變得可用
重啓redis,儘快讓它對外提供服務,可是就像上一講說,若是你沒作數據備份,這個時候redis啓動了,也不可用啊,數據都沒了
極可能說,大量的請求過來,緩存所有沒法命中,在redis里根本找不到數據,這個時候就死定了,緩存雪崩問題,全部請求,沒有在redis命中,就會去mysql數據庫這種數據源頭中去找,一會兒mysql承接高併發,而後就掛了
mysql掛掉,你都無法去找數據恢復到redis裏面去,redis的數據從哪兒來?從mysql來。。。
具體的完整的緩存雪崩的場景,還有企業級的解決方案,到後面講
若是你把redis的持久化作好,備份和恢復方案作到企業級的程度,那麼即便你的redis故障了,也能夠經過備份數據,快速恢復,一旦恢復當即對外提供服務
redis的持久化,跟高可用,是有關係的,企業級redis架構中去講解
redis持久化:RDB,AOF

-------------------------------------------------------------------------------------

一、RDB和AOF兩種持久化機制的介紹

RDB持久化機制,對redis中的數據執行週期性的持久化
AOF機制對每條寫入命令做爲日誌,以append-only的模式寫入一個日誌文件中,在redis重啓的時候,能夠經過回放AOF日誌中的寫入指令來從新構建整個數據集
若是咱們想要redis僅僅做爲純內存的緩存來用,那麼能夠禁止RDB和AOF全部的持久化機制
經過RDB或AOF,均可以將redis內存中的數據給持久化到磁盤上面來,而後能夠將這些數據備份到別的地方去,好比說阿里雲,雲服務
若是redis掛了,服務器上的內存和磁盤上的數據都丟了,能夠從雲服務上拷貝回來以前的數據,放到指定的目錄中,而後從新啓動redis,redis就會自動根據持久化數據文件中的數據,去恢復內存中的數據,繼續對外提供服務
若是同時使用RDB和AOF兩種持久化機制,那麼在redis重啓的時候,會使用AOF來從新構建數據,由於AOF中的數據更加完整

-------------------------------------------------------------------------------------

二、RDB持久化機制的優勢

(1)RDB會生成多個數據文件,每一個數據文件都表明了某一個時刻中redis的數據,這種多個數據文件的方式,很是適合作冷備,能夠將這種完整的數據文件發送到一些遠程的安全存儲上去,好比說Amazon的S3雲服務上去,在國內能夠是阿里雲的ODPS分佈式存儲上,以預約好的備份策略來按期備份redis中的數據
(2)RDB對redis對外提供的讀寫服務,影響很是小,可讓redis保持高性能,由於redis主進程只須要fork一個子進程,讓子進程執行磁盤IO操做來進行RDB持久化便可
(3)相對於AOF持久化機制來講,直接基於RDB數據文件來重啓和恢復redis進程,更加快速

-------------------------------------------------------------------------------------

三、RDB持久化機制的缺點

(1)若是想要在redis故障時,儘量少的丟失數據,那麼RDB沒有AOF好。通常來講,RDB數據快照文件,都是每隔5分鐘,或者更長時間生成一次,這個時候就得接受一旦redis進程宕機,那麼會丟失最近5分鐘的數據
(2)RDB每次在fork子進程來執行RDB快照數據文件生成的時候,若是數據文件特別大,可能會致使對客戶端提供的服務暫停數毫秒,或者甚至數秒

-------------------------------------------------------------------------------------

四、AOF持久化機制的優勢

(1)AOF能夠更好的保護數據不丟失,通常AOF會每隔1秒,經過一個後臺線程執行一次fsync操做,最多丟失1秒鐘的數據
(2)AOF日誌文件以append-only模式寫入,因此沒有任何磁盤尋址的開銷,寫入性能很是高,並且文件不容易破損,即便文件尾部破損,也很容易修復
(3)AOF日誌文件即便過大的時候,出現後臺重寫操做,也不會影響客戶端的讀寫。由於在rewrite log的時候,會對其中的指導進行壓縮,建立出一份須要恢復數據的最小日誌出來。再建立新日誌文件的時候,老的日誌文件仍是照常寫入。當新的merge後的日誌文件ready的時候,再交換新老日誌文件便可。
(4)AOF日誌文件的命令經過很是可讀的方式進行記錄,這個特性很是適合作災難性的誤刪除的緊急恢復。好比某人不當心用flushall命令清空了全部數據,只要這個時候後臺rewrite尚未發生,那麼就能夠當即拷貝AOF文件,將最後一條flushall命令給刪了,而後再將該AOF文件放回去,就能夠經過恢復機制,自動恢復全部數據


-------------------------------------------------------------------------------------

五、AOF持久化機制的缺點

(1)對於同一份數據來講,AOF日誌文件一般比RDB數據快照文件更大
(2)AOF開啓後,支持的寫QPS會比RDB支持的寫QPS低,由於AOF通常會配置成每秒fsync一第二天志文件,固然,每秒一次fsync,性能也仍是很高的
(3)之前AOF發生過bug,就是經過AOF記錄的日誌,進行數據恢復的時候,沒有恢復如出一轍的數據出來。因此說,相似AOF這種較爲複雜的基於命令日誌/merge/回放的方式,比基於RDB每次持久化一份完整的數據快照文件的方式,更加脆弱一些,容易有bug。不過AOF就是爲了不rewrite過程致使的bug,所以每次rewrite並非基於舊的指令日誌進行merge的,而是基於當時內存中的數據進行指令的從新構建,這樣健壯性會好不少。


-------------------------------------------------------------------------------------

六、RDB和AOF到底該如何選擇

(1)不要僅僅使用RDB,由於那樣會致使你丟失不少數據(2)也不要僅僅使用AOF,由於那樣有兩個問題,第一,你經過AOF作冷備,沒有RDB作冷備,來的恢復速度更快; 第二,RDB每次簡單粗暴生成數據快照,更加健壯,能夠避免AOF這種複雜的備份和恢復機制的bug(3)綜合使用AOF和RDB兩種持久化機制,用AOF來保證數據不丟失,做爲數據恢復的第一選擇; 用RDB來作不一樣程度的冷備,在AOF文件都丟失或損壞不可用的時候,還能夠使用RDB來進行快速的數據恢復

相關文章
相關標籤/搜索