一文把Redis主從複製、哨兵、Cluster三種模式摸透

概述

Redis做爲緩存的高效中間件,在咱們平常的開發中被頻繁的使用,今天就來講一說Redis的四種模式,分別是「單機版、主從複製、哨兵、以及集羣模式」css

可能,在通常公司的程序員使用單機版基本都能解決問題,在Redis的官網給出的數據是10W QPS,這對於應付通常的公司綽綽有餘了,再不行就來個主從模式,實現讀寫分離,性能又大大提升。html

可是,咱們做爲有抱負的程序員,僅限於單機版和主從模式的crud是不行的,至少也要了解「哨兵」「集羣模式」的原理,這樣面試的時候才能和麪試官扯皮啊。node

以前對於Redis方面也是寫了比較多的文章,如:「Redis的基本數據類型和底層的實現原理、事務、持久化、分佈式鎖、訂閱預發佈」等,能夠說是比較全面的教程了,這篇講完基本就全了,我會把文章系統的整理成pdf,分享給你們。nginx


一文把Redis主從複製、哨兵、Cluster三種模式摸透


單機

單機版的Redis就比較簡單了,基本90%的程序員都使用過,官網推薦操做Redis的第三方依賴庫是Jedis,在SpringBoot項目中,引入下面依賴就能夠直接使用了:程序員

<dependency>
   <groupId>redis.clients</groupId>
   <artifactId>jedis</artifactId>
   <version>${jedis.version}</version>
</dependency>

優勢

單機版的Redis也有不少優勢,好比實現實現簡單、維護簡單、部署簡單、維護成本很是低,不須要其它額外的開支。面試

缺點

可是,由於是單機版的Redis因此也存在不少的問題,好比最明顯的單點故障問題,一個Redis掛了,全部的請求就會直接打在了DB上。redis

而且一個Redis抗併發數量也是有限的,同時要兼顧讀寫兩種請求,只要訪問量一上來,Redis就受不了了,另外一方面單機版的Redis數據量存儲也是有限的,數據量一大,再重啓Redis的時候,就會很是的慢,因此侷限性也是比較大的。算法

實操搭建

單機版的搭建教程,在網上有很是多的全面的教程,基本就是傻瓜式操做,特別是在本地搭建的話,基本使用yum快捷方便,幾句命令就搞定了,這裏推薦一個搭建教程:https://www.cnblogs.com/ zuidongfeng/p/8032505.html。數據庫

上面這個教程講的很是的詳細,環境的搭建原本是運維的工做,可是做爲程序員嘗試本身去搭建環境仍是有必要的,並且搭建環境這種東西,基本就是一勞永逸,搭建一次,可能下次換電腦或者重裝虛擬機纔會再次搭建。c#

這裏也放出redis經常使用的redis.conf的配置項,而且附帶註釋,看我是否是很暖男:

daemonize yes  // 設置後臺啓動,通常設置yes
pidfile /var/run/redis.pid // edis以守護進程方式運行時,redis默認會把pid寫入/var/run/redis.pid文件port 6379 // 默認端口爲6379
bind 127.0.0.1 //主機地址,設置0.0.0.0表示均可以訪問。127.0.0.1表示只容許本機訪問
timeout 900  // 客戶端閒置多長時間後關閉鏈接,若是指定爲0,表示關閉該功能
logfile stdout // 日誌記錄方式,默認爲標準輸出
logfile "./redis7001.log"  # 指明日誌文件名
databases 16 // 設置數據庫的數量,默認數據庫爲0
save  //有多少次更新操做,就將數據同步到數據文件 Redis默認配置文件中提供了三個條件: save 900 1 //900秒(15分鐘)內有1個更改
 save 300 10 //300秒(5分鐘)內有10個更改
 save 60 10000  // 60秒內有10000個更改
rdbcompression yes // 指定存儲至本地數據庫時是否壓縮數據dbfilename dump.rdb //指定本地數據庫文件名
dir ./    //指定本地數據庫存放目錄slaveof  // 主從同步設置,設置主數據庫的ip和端口# 若是非零,則設置SO_KEEPALIVE選項來向空閒鏈接的客戶端發送ACKtcp-keepalive 60
# 默認若是開啓RDB快照(至少一條save指令)而且最新的後臺保存失敗,Redis將會中止接受寫操做# 這將使用戶知道數據沒有正確的持久化到硬盤,不然可能沒人注意到而且形成一些災難stop-writes-on-bgsave-error yes
# 默認若是開啓RDB快照(至少一條save指令)而且最新的後臺保存失敗,Redis將會中止接受寫操做。stop-writes-on-bgsave-error yes
# 當導出到 .rdb 數據庫時是否用LZF壓縮字符串對象rdbcompression yes# 版本5的RDB有一個CRC64算法的校驗和放在了文件的最後。這將使文件格式更加可靠。
rdbchecksum yes# 持久化數據庫的文件名dbfilename dump-master.rdb
# 工做目錄dir /usr/local/redis-4.0.8/redis_master/
# slav服務鏈接master的密碼masterauth testmaster123# 當一個slave失去和master的鏈接,或者同步正在進行中,slave的行爲能夠有兩種:#1) 若是 slave-serve-stale-data 設置爲 "yes" (默認值),slave會繼續響應客戶端請求,多是正常數據,或者是過期了的數據,也多是還沒得到值的空數據。
2) 若是 slave-serve-stale-data 設置爲 "no",slave會回覆"正在從master同步
# (SYNC with master in progress)"來處理各類請求,除了 INFO 和 SLAVEOF 命令。
slave-serve-stale-data yes# 配置是否僅讀slave-read-only yes
# 若是你選擇「yes」Redis將使用更少的TCP包和帶寬來向slaves發送數據。可是這將使數據傳輸到slave上有延遲,Linux內核的默認配置會達到40毫秒
# 若是你選擇了 "no" 數據傳輸到salve的延遲將會減小但要使用更多的帶寬
repl-disable-tcp-nodelay no# slave的優先級,優先級數字小的salve會優先考慮提高爲masterslave-priority 100
# 密碼驗證requirepass testmaster123# redis實例最大佔用內存,一旦內存使用達到上限,Redis會根據選定的回收策略(參見:# maxmemmory-policy)刪除keymaxmemory 3gb
# 最大內存策略:若是達到內存限制了,Redis如何選擇刪除key。# volatile-lru -> 根據LRU算法刪除帶有過時時間的key。# allkeys-lru -> 根據LRU算法刪除任何key。# volatile-random -> 根據過時設置來隨機刪除key, 具有過時時間的key。 
# allkeys->random -> 無差異隨機刪, 任何一個key。 
# volatile-ttl -> 根據最近過時時間來刪除(輔以TTL), 這是對於有過時時間的key # noeviction -> 誰也不刪,直接在寫操做時返回錯誤。maxmemory-policy volatile-lru# AOF開啓appendonly no# aof文件名appendfilename "appendonly.aof"
# fsync() 系統調用告訴操做系統把數據寫到磁盤上,而不是等更多的數據進入輸出緩衝區。# 有些操做系統會真的把數據立刻刷到磁盤上;有些則會盡快去嘗試這麼作。# Redis支持三種不一樣的模式:# no:不要馬上刷,只有在操做系統須要刷的時候再刷。比較快。# always:每次寫操做都馬上寫入到aof文件。慢,可是最安全。# everysec:每秒寫一次。折中方案。 appendfsync everysec# 若是AOF的同步策略設置成 "always" 或者 "everysec",而且後臺的存儲進程(後臺存儲或寫入AOF
# 日誌)會產生不少磁盤I/O開銷。某些Linux的配置下會使Redis由於 fsync()系統調用而阻塞好久。# 注意,目前對這個狀況尚未完美修正,甚至不一樣線程的 fsync() 會阻塞咱們同步的write(2)調用。
# 爲了緩解這個問題,能夠用下面這個選項。它能夠在 BGSAVE 或 BGREWRITEAOF 處理時阻止主進程進行fsync()。# 這就意味着若是有子進程在進行保存操做,那麼Redis就處於"不可同步"的狀態。
# 這其實是說,在最差的狀況下可能會丟掉30秒鐘的日誌數據。(默認Linux設定)
# 若是你有延時問題把這個設置成"yes",不然就保持"no",這是保存持久數據的最安全的方式。
no-appendfsync-on-rewrite yes# 自動重寫AOF文件auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# AOF文件可能在尾部是不完整的(這跟system關閉有問題,尤爲是mount ext4文件系統時# 沒有加上data=ordered選項。只會發生在os死時,redis本身死不會不完整)。
# 那redis重啓時load進內存的時候就有問題了。
# 發生的時候,能夠選擇redis啓動報錯,而且通知用戶和寫日誌,或者load儘可能多正常的數據。
# 若是aof-load-truncated是yes,會自動發佈一個log給客戶端而後load(默認)。
# 若是是no,用戶必須手動redis-check-aof修復AOF文件才能夠。# 注意,若是在讀取的過程當中,發現這個aof是損壞的,服務器也是會退出的,# 這個選項僅僅用於當服務器嘗試讀取更多的數據但又找不到相應的數據時。aof-load-truncated yes
# Lua 腳本的最大執行時間,毫秒爲單位lua-time-limit 5000
# Redis慢查詢日誌能夠記錄超過指定時間的查詢slowlog-log-slower-than 10000
# 這個長度沒有限制。只是要主要會消耗內存。你能夠經過 SLOWLOG RESET 來回收內存。slowlog-max-len 128
# 客戶端的輸出緩衝區的限制,可用於強制斷開那些由於某種緣由從服務器讀取數據的速度不夠快的客戶端client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
# 當一個子進程重寫AOF文件時,文件每生成32M數據會被同步
aof-rewrite-incremental-fsync yes

因爲,單機版的Redis在併發量比較大的時候,而且須要較高性能和可靠性的時候,單機版基本就不適合了,因而就出現了「主從模式」

主從模式

原理

主從的原理還算是比較簡單的,一主多從,「主數據庫(master)能夠讀也能夠寫(read/write),從數據庫進度(only read)」

可是,主從模式通常實現「讀寫分離」「主數據庫僅寫(only write)」,減輕主數據庫的壓力,下面一張圖搞懂主從模式的原理:

一文把Redis主從複製、哨兵、Cluster三種模式摸透


主從模式原理就是那麼簡單,那它執行的過程(工做機制)又是怎麼樣的呢?再來一張圖:

一文把Redis主從複製、哨兵、Cluster三種模式摸透


當開啓主從模式的時候,他的具體工做機制以下:

  1. 當slave啓動後會向master發送SYNC命令,master節點收到從數據庫的命令後經過bgsave保存快照(「RDB持久化」),而且期間的執行的些命令會被緩存起來。

  2. 而後master會將保存的快照發送給slave,而且繼續緩存期間的寫命令。

  3. slave收到主數據庫發送過來的快照就會加載到本身的數據庫中。

  4. 最後master將緩存的命令同步給slave,slave收到命令後執行一遍,這樣master與slave數據就保持一致了。

優勢

之因此運用主從,是由於主從必定程度上解決了單機版併發量大,致使請求延遲或者redis宕機服務中止的問題。

從數據庫分擔主數據庫的讀壓力,如果主數據庫是隻寫模式,那麼實現讀寫分離,主數據庫就沒有了讀壓力了。

另外一方面解決了單機版單點故障的問題,如果主數據庫掛了,那麼從數據庫能夠隨時頂上來,綜上來講,主從模式必定程度上提升了系統的可用性和性能,是實現哨兵和集羣的基礎。

主從同步以異步方式進行同步,期間Redis仍然能夠響應客戶端提交的查詢和更新的請求。

缺點

主從模式好是好,他也有本身的缺點,好比數據的一致性問題,假如主數據庫寫操做完成,那麼他的數據會被複制到從數據庫,如果尚未即便複製到從數據庫,讀請求又來了,此時讀取的數據就不是最新的數據。

如果從主同步的過程網絡出故障了,致使主從同步失敗,也會出現問題數據一致性的問題。

主從模式不具有自動容錯和恢復的功能,一旦主數據庫,從節點晉升爲主數據庫的過程須要人爲操做,維護的成本就會升高,而且主節點的寫能力、存儲能力都會受到限制。

實操搭建

下面的咱們來實操搭建一下主從模式,主從模式的搭建仍是比較簡單的,我這裏一臺centos 7虛擬機,使用開啓redis多實例的方法搭建主從。

redis中開啓多實例的方法,首先建立一個文件夾,用於存放redis集羣的配置文件:

mkdir redis

而後粘貼複製redis.conf配置文件:

cp /root/redis-4.0.6/redis.conf /root/redis/redis-6379.conf
cp /root/redis-4.0.6/redis.conf /root/redis/redis-6380.conf
cp /root/redis-4.0.6/redis.conf /root/redis/redis-6381.conf

複製三份配置文件,一主兩從,6379端口做爲主數據庫(master),6380、6381做爲從數據庫(slave)。

首先是配置主數據庫的配置文件:vi redis-6379.conf

bind 0.0.0.0 # 註釋掉或配置成0.0.0.0表示任意IP都可訪問。
protected-mode no # 關閉保護模式,使用密碼訪問。port 6379  # 設置端口,63806381依次爲63806381
timeout 30 # 客戶端鏈接空閒多久後斷開鏈接,單位秒,0表示禁用
daemonize yes # 在後臺運行pidfile /var/run/redis_6379.pid  # pid進程文件名,63806381依次爲redis_6380.pid、redis_6381.pid
logfile /root/reids/log/6379.log # 日誌文件,63806381依次爲6380.log6381.log
save 900 1 # 900s內至少一次寫操做則執行bgsave進行RDB持久化
save 300 10
save 60 10000 
rdbcompression yes #是否對RDB文件進行壓縮,建議設置爲no,以(磁盤)空間換(CPU)時間dbfilename dump.rdb # RDB文件名稱
dir /root/redis/datas # RDB文件保存路徑,AOF文件也保存在這裏appendonly yes # 表示使用AOF增量持久化的方式appendfsync everysec # 可選值 always, everysec,no,建議設置爲everysecrequirepass 123456 # 設置密碼

而後,就是修改從數據庫的配置文件,再從數據庫的配置文件中加入如下的配置信息:

slaveof 127.0.0.1 6379 # 配置master的ip,port
masterauth 123456 # 配置訪問master的密碼
slaveof-serve-stale-data no 

接下來就是啓動三個redis實例,啓動的命令,先cd到redis的src目錄下,而後執行:

./redis-server /root/redis/6379.conf
./redis-server /root/redis/6380.conf./redis-server /root/redis/6381.conf

經過命令ps -aux | grep redis,查看啓動的redis進程:

一文把Redis主從複製、哨兵、Cluster三種模式摸透


如上圖所示,表示啓動成功,下面就開始進入測試階段。

測試

我這裏使用SecureCRT做爲redis鏈接的客戶端,同時啓動三個SecureCRT,分別鏈接redis1的三個實例,啓動時指定端口以及密碼:

./redis-cli -p 6379 -a 123456

一文把Redis主從複製、哨兵、Cluster三種模式摸透


啓動後,在master(6379),輸入:set name 'ldc',在slave中經過get name,能夠查看:

一文把Redis主從複製、哨兵、Cluster三種模式摸透


數據同步成功,這有幾個坑一個是redis.conf中沒有設置對bind,會致使非本機的ip被過濾掉,通常配置0.0.0.0就能夠了。

另外一個是沒有配置密碼requirepass 123456,會致使IO一直鏈接異常,這個是我遇到的坑,後面配置密碼後就成功了。

還有,就是查看redis的啓動日誌能夠發現有兩個warning,雖然不影響搭建主從同步,看着挺煩人的,可是有些人會遇到,有些人不會遇到。

可是,我這我的比較有強迫症,百度也是有解決方案的,這裏就不講了,交給大家本身解決,這裏只是告訴你有這個問題,有些人看都不看日誌的,看到啓動成功就認爲萬事大吉了,也不看日誌,這習慣並很差。

一文把Redis主從複製、哨兵、Cluster三種模式摸透


哨兵模式

原理

哨兵模式是主從的升級版,由於主從的出現故障後,不會自動恢復,須要人爲干預,這就很蛋疼啊。

在主從的基礎上,實現哨兵模式就是爲了監控主從的運行情況,對主從的健壯進行監控,就好像哨兵同樣,只要有異常就發出警告,對異常情況進行處理。

一文把Redis主從複製、哨兵、Cluster三種模式摸透


因此,總的歸納來講,哨兵模式有如下的優勢(功能點):

  1. 「監控」:監控master和slave是否正常運行,以及哨兵之間也會相互監控

  2. 「自動故障恢復」:當master出現故障的時候,會自動選舉一個slave做爲master頂上去。

哨兵模式的監控配置信息,是經過配置從數據庫的sentinel monitor <master-name> <ip> <redis-port> <quorum> 來指定的,好比:

// mymaster 表示給master數據庫定義了一個名字,後面的是master的ip和端口,1表示至少須要一個Sentinel進程贊成才能將master判斷爲失效,若是不知足這個條件,則自動故障轉移(failover)不會執行
sentinel monitor mymaster 127.0.0.1 6379 1

節點通訊

固然還有其它的配置信息,其它配置信息,在環境搭建的時候再說。當哨兵啓動後,會與master創建一條鏈接,用於訂閱master的_sentinel_:hello頻道。

該頻道用於獲取監控該master的其它哨兵的信息。而且還會創建一條定時向master發送INFO命令獲取master信息的鏈接。

「當哨兵與master創建鏈接後,按期會向(10秒一次)master和slave發送INFO命令,如果master被標記爲主觀下線,頻率就會變爲1秒一次。」

而且,按期向_sentinel_:hello頻道發送本身的信息,以便其它的哨兵可以訂閱獲取本身的信息,發送的內容包含「哨兵的ip和端口、運行id、配置版本、master名字、master的ip端口還有master的配置版本」等信息。

以及,「按期的向master、slave和其它哨兵發送PING命令(每秒一次),以便檢測對象是否存活」,如果對方接收到了PING命令,無端障狀況下,會回覆PONG命令。

因此,哨兵經過創建這兩條鏈接、經過按期發送INFO、PING命令來實現哨兵與哨兵、哨兵與master之間的通訊。

這裏涉及到一些概念須要理解,INFO、PING、PONG等命令,後面還會有MEET、FAIL命令,以及主觀下線,固然還會有客觀下線,這裏主要說一下這幾個概念的理解:

  1. INFO:該命令能夠獲取主從數據庫的最新信息,能夠實現新結點的發現

  2. PING:該命令被使用最頻繁,該命令封裝了自身節點和其它節點的狀態數據。

  3. PONG:當節點收到MEET和PING,會回覆PONG命令,也把本身的狀態發送給對方。

  4. MEET:該命令在新結點加入集羣的時候,會向老節點發送該命令,表示本身是個新人

  5. FAIL:當節點下線,會向集羣中廣播該消息。

上線和下線

當哨兵與master相同以後就會按期一直保持聯繫,如果某一時刻哨兵發送的PING在指定時間內沒有收到回覆(sentinel down-after-milliseconds master-name milliseconds 配置),那麼發送PING命令的哨兵就會認爲該master「主觀下線」Subjectively Down)。

由於有多是哨兵與該master之間的網絡問題形成的,而不是master自己的緣由,因此哨兵同時會詢問其它的哨兵是否也認爲該master下線,如果認爲該節點下線的哨兵達到必定的數量(「前面的quorum字段配置」),就會認爲該節點「客觀下線」Objectively Down)。

如果沒有足夠數量的sentinel贊成該master下線,則該master客觀下線的標識會被移除;如果master從新向哨兵的PING命令回覆了客觀下線的標識也會被移除。

選舉算法

當master被認爲客觀下線後,又是怎麼進行故障恢復的呢?原來哨兵中首先選舉出一個老大哨兵來進行故障恢復,選舉老大哨兵的算法叫作「Raft算法」

  1. 發現master下線的哨兵(sentinelA)會向其它的哨兵發送命令進行拉票,要求選擇本身爲哨兵大佬。

  2. 如果目標哨兵沒有選擇其它的哨兵,就會選擇該哨兵(sentinelA)爲大佬。

  3. 如果選擇sentinelA的哨兵超過半數(半數原則),該大佬非sentinelA莫屬。

  4. 若是有多個哨兵同時競選,而且可能存在票數一致的狀況,就會等待下次的一個隨機時間再次發起競選請求,進行新的一輪投票,直到大佬被選出來。

選出大佬哨兵後,大佬哨兵就會對故障進行自動回覆,從slave中選出一名slave做爲主數據庫,選舉的規則以下所示:

  1. 全部的slave中slave-priority優先級最高的會被選中。

  2. 如果優先級相同,會選擇偏移量最大的,由於偏移量記錄着數據的複製的增量,越大表示數據越完整。

  3. 如果以上二者都相同,選擇ID最小的。

經過以上的層層篩選最終實現故障恢復,當選的slave晉升爲master,其它的slave會向新的master複製數據,如果down掉的master從新上線,會被看成slave角色運行。

優勢

哨兵模式是主從模式的升級版,因此在系統層面提升了系統的可用性和性能、穩定性。當master宕機的時候,可以自動進行故障恢復,需不要人爲的干預。

哨兵與哨兵之間、哨兵與master之間可以進行及時的監控,心跳檢測,及時發現系統的問題,這都是彌補了主從的缺點。

缺點

哨兵一主多從的模式一樣也會遇到寫的瓶頸,已經存儲瓶頸,如果master宕機了,故障恢復的時間比較長,寫的業務就會受到影響。

增長了哨兵也增長了系統的複雜度,須要同時維護哨兵模式。

實操搭建

最後,咱們進行一下哨兵模式的搭建,配置哨兵模式仍是比較簡單的,在上面配置的主從模式的基礎上,同時建立一個文件夾用於存放三個哨兵的配置文件:

mkdir /root/redis-4.0.6/sentinel.conf  /root/redis/sentinel/sentinel1.conf 
mkdir /root/redis-4.0.6/sentinel.conf  /root/redis/sentinel/sentinel2.conf 
mkdir /root/redis-4.0.6/sentinel.conf  /root/redis/sentinel/sentinel3.conf 

分別在這三個文件中添加以下配置:

daemonize yes # 在後臺運行
sentinel monitor mymaster 127.0.0.1 6379 1 # 給master起一個名字mymaster,而且配置masterip和端口
sentinel auth-pass mymaster 123456 # master的密碼
port 26379 #另外兩個配置36379,46379端口
sentinel down-after-milliseconds mymaster 3000 # 3s未回覆PING就認爲master主觀下線
sentinel parallel-syncs mymaster 2  # 執行故障轉移時,最多能夠有2個slave實例在同步新的master實例
sentinel failover-timeout mymaster 100000 # 若是在10s內未能完成故障轉移操做認爲故障轉移失敗

配置完後分別啓動三臺哨兵:

./redis-server sentinel1.conf --sentinel
./redis-server sentinel2.conf --sentinel
./redis-server sentinel3.conf --sentinel

而後經過:ps -aux|grep redis進行查看:

一文把Redis主從複製、哨兵、Cluster三種模式摸透


能夠看到三臺redis實例以及三個哨兵都已經正常啓動,現登錄6379,經過INFO Repliaction查看master信息:

一文把Redis主從複製、哨兵、Cluster三種模式摸透


當前master爲6379,而後咱們來測試一下哨兵的自動故障恢復,直接kill掉6379進程,而後經過登錄6380再次查看master的信息:

一文把Redis主從複製、哨兵、Cluster三種模式摸透


能夠看到當前的6380角色是master,而且6380可讀可寫,而不是隻讀模式,這說明咱們的哨兵是起做用了,搭建成功,感興趣的能夠自行搭建,也有可能你會踩一堆的坑。

Cluster模式

最後,Cluster是真正的集羣模式了,哨兵解決和主從不能自動故障恢復的問題,可是同時也存在難以擴容以及單機存儲、讀寫能力受限的問題,而且集羣以前都是一臺redis都是全量的數據,這樣全部的redis都冗餘一份,就會大大消耗內存空間。

集羣模式實現了Redis數據的分佈式存儲,實現數據的分片,每一個redis節點存儲不一樣的內容,而且解決了在線的節點收縮(下線)和擴容(上線)問題。

集羣模式真正意義上實現了系統的高可用和高性能,可是集羣同時進一步使系統變得愈來愈複雜,接下來咱們來詳細的瞭解集羣的運做原理。

數據分區原理

集羣的原理圖仍是很好理解的,在Redis集羣中採用的是虛擬槽分區算法,會把redis集羣分紅16384 個槽(0 -16383)。

好比:下圖所示三個master,會把0 -16383範圍的槽可能分紅三部分(0-5000)、(5001-11000)、(11001-16383)分別數據三個緩存節點的槽範圍。

一文把Redis主從複製、哨兵、Cluster三種模式摸透


當客戶端請求過來,會首先經過對key進行CRC16 校驗並對 16384 取模(CRC16(key)%16383)計算出key所在的槽,而後再到對應的槽上進行取數據或者存數據,這樣就實現了數據的訪問更新。

一文把Redis主從複製、哨兵、Cluster三種模式摸透


之因此進行分槽存儲,是將一整堆的數據進行分片,防止單臺的redis數據量過大,影響性能的問題。

節點通訊

節點之間實現了將數據進行分片存儲,那麼節點之間又是怎麼通訊的呢?這個和前面哨兵模式講的命令基本同樣。

首先新上線的節點,會經過 Gossip 協議向老成員發送Meet消息,表示本身是新加入的成員。

老成員收到Meet消息後,在沒有故障的狀況下會恢復PONG消息,表示歡迎新結點的加入,除了第一次發送Meet消息後,以後都會發送按期PING消息,實現節點之間的通訊。

一文把Redis主從複製、哨兵、Cluster三種模式摸透


通訊的過程當中會爲每個通訊的節點開通一條tcp通道,以後就是定時任務,不斷的向其它節點發送PING消息,這樣作的目的就是爲了瞭解節點之間的元數據存儲狀況,以及健康情況,以便即便發現問題。

數據請求

上面說到了槽信息,在Redis的底層維護了unsigned char myslots[CLUSTER_SLOTS/8] 一個數組存放每一個節點的槽信息。

由於他是一個二進制數組,只有存儲0和1值,以下圖所示:

一文把Redis主從複製、哨兵、Cluster三種模式摸透


這樣數組只表示本身是否存儲對應的槽數據,如果1表示存在該數據,0表示不存在該數據,這樣查詢的效率就會很是的高,相似於布隆過濾器,二進制存儲。

好比:集羣節點1負責存儲0-5000的槽數據,可是此時只有0、一、2存儲有數據,其它的槽尚未存數據,因此0、一、2對應的值爲1。

而且,每一個redis底層還維護了一個clusterNode數組,大小也是16384,用於儲存負責對應槽的節點的ip、端口等信息,這樣每個節點就維護了其它節點的元數據信息,便於及時的找到對應的節點。

當新結點加入或者節點收縮,經過PING命令通訊,及時的更新本身clusterNode數組中的元數據信息,這樣有請求過來也就能及時的找到對應的節點。

一文把Redis主從複製、哨兵、Cluster三種模式摸透


有兩種其它的狀況就是,如果請求過來發現,數據發生了遷移,好比新節點加入,會使舊的緩存節點數據遷移到新結點。

請求過來發現舊節點已經發生了數據遷移而且數據被遷移到新結點,因爲每一個節點都有clusterNode信息,經過該信息的ip和端口。此時舊節點就會向客戶端發一個MOVED 的重定向請求,表示數據已經遷移到新結點上,你要訪問這個新結點的ip和端口就能拿到數據,這樣就能從新獲取到數據。

假若正在發生數據遷移呢?舊節點就會向客戶端發送一個ASK 重定向請求,並返回給客戶端遷移的目標節點的ip和端口,這樣也能獲取到數據。

擴容和收縮

擴容和收縮也就是節點的上線和下線,可能節點發生故障了,故障自動恢復的過程(節點收縮)。

節點的收縮和擴容時,會從新計算每個節點負責的槽範圍,併發根據虛擬槽算法,將對應的數據更新到對應的節點。

還有前面的講的新加入的節點會首先發送Meet消息,詳細能夠查看前面講的內容,基本同樣的模式。

以及發生故障後,哨兵老大節點的選舉,master節點的從新選舉,slave怎樣晉升爲master節點,能夠查看前面哨兵模式選舉過程。

優勢

集羣模式是一個無中心的架構模式,將數據進行分片,分佈到對應的槽中,每一個節點存儲不一樣的數據內容,經過路由可以找到對應的節點負責存儲的槽,可以實現高效率的查詢。

而且集羣模式增長了橫向和縱向的擴展能力,實現節點加入和收縮,集羣模式是哨兵的升級版,哨兵的優勢集羣都有。

缺點

緩存的最大問題就是帶來數據一致性問題,在平衡數據一致性的問題時,兼顧性能與業務要求,大多數都是以最終一致性的方案進行解決,而不是強一致性。

而且集羣模式帶來節點數量的劇增,一個集羣模式最少要6臺機,由於要知足半數原則的選舉方式,因此也帶來了架構的複雜性。

slave只充當冷備,並不能緩解master的讀的壓力。

實操搭建

集羣模式的部署比較簡單,只要在redis.conf加入下面的配置信息便可:

port 6379# 本示例6個節點端口分別爲637九、6380、638一、638二、638三、6384
daemonize yes # r後臺運行 
pidfile /var/run/redis_6379.pid # 分別對應637九、6380、638一、638二、638三、6384
cluster-enabled yes # 開啓集羣模式 
masterauth 123456# 若是設置了密碼,須要指定master密碼
cluster-config-file nodes_6379.conf # 集羣的配置文件,一樣對應637九、6380、638一、638二、638三、6384六個節點
cluster-node-timeout 10000 # 請求超時時間

同時開啓這六個實例,經過下面的命令將這六個實例以集羣的方式運行

./redis-cli --cluster create --cluster-replicas 1 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381  127.0.0.1:6382  127.0.0.1:6383  127.0.0.1:6384  -a 123456

這樣就實現了集羣的搭建

推薦學習:阿里P8架構師用450分鐘時間讓你精通Redis,面試不再怕被問Redis!

相關文章
相關標籤/搜索