高可用方案

高可用方案

“高可用性”(High Availability)通常來描述一個系統(tǒng)經(jīng)過專門的設計专肪,從而減少停工時間,而保持其服 務的高度可用性。CAP的 AP模型(C一致性疤祭,A可用性,P分區(qū)容錯性)饵婆。單機的Redis是無法保證高可用性的勺馆,當Redis服務器宕機后,即使在有持久化的機制下也無法保證不丟 失數(shù)據(jù)侨核。

所以我們采用Redis多機和集群的方式來保證Redis的高可用性草穆。 單進程+單線程 + 多機 (集群)

主從復制

Redis支持主從復制功能,可以通過執(zhí)行slaveof(Redis5以后改成replicaof)或者在配置文件中設置slaveof(Redis5以后改成replicaof)來開啟復制功能搓译。

一主一從:


一主一從.png

一主多從:


一主多從.png

傳遞復制


傳遞復制.png
  • 主對外從對內(nèi)悲柱,主可寫從不可寫
  • 主掛了,從不可以自動為主

主從配置

主Redis配置

無需特殊配置

從Redis配置

修改從服務器上的 redis.conf 文件:

# slaveof <masterip> <masterport>
# 表示當前【從服務器】對應的【主服務器】的IP是192.168.10.135些己,端口是6379豌鸡。 
replicaof 127.0.0.1 6379

作用

讀寫分離
  • 一主多從嘿般,主從同步
  • 主負責寫,從負責讀
  • 提升Redis的性能和吞吐量
  • 主從的數(shù)據(jù)一致性問題
數(shù)據(jù)容災
  • 從機是主機的備份
  • 主機宕機涯冠,從機可讀不可寫
  • 默認情況下主機宕機后炉奴,從機不可為主機
  • 利用哨兵可以實現(xiàn)主從切換,做到高可用

實現(xiàn)與原理

復制流程

保存主節(jié)點信息

當客戶端向從服務器發(fā)送slaveof(replicaof) 主機地址(127.0.0.1) 端口(6379)時:從服務器將主機 ip(127.0.0.1)和端口(6379)保存到redisServer的masterhost和masterport中功偿。

Struct redisServer{
  char *masterhost;//主服務器ip 
  int masterport;//主服務器端口
};

從服務器將向發(fā)送SLAVEOF命令的客戶端返回OK盆佣,表示復制指令已經(jīng)被接收,而實際上復制工作是在 OK返回之后進行械荷。

建立socket鏈接

slaver與master建立socket連接

slaver關聯(lián)文件事件處理器共耍,該處理器接收RDB文件(全量復制)、接收Master傳播來的寫命令(增量復制)


建立Socket鏈接.png

主服務器accept從服務器Socket連接后吨瞎,創(chuàng)建相應的客戶端狀態(tài)痹兜。相當于從服務器是主服務器的Client 端。

發(fā)送ping命令

Slaver向Master發(fā)送ping命令

  1. 檢測socket的讀寫狀態(tài)
  2. 檢測Master能否正常處理

Master的響應:

  1. 發(fā)送“pong” , 說明正常
  2. 返回錯誤颤诀,說明Master不正常
  3. timeout字旭,說明網(wǎng)絡超時


    發(fā)送ping命令.png

權限驗證

主從正常連接后,進行權限驗證崖叫。主未設置密碼(requirepass=“”) 遗淳,從也不用設置密碼(masterauth=“”)。主設置密碼(requirepass!=""),從需要設置密碼(masterauth=主的requirepass的值) 或者從通過auth命令向主發(fā)送密碼


權限驗證.png

發(fā)送端口信息

在身份驗證步驟之后心傀,從服務器將執(zhí)行命令REPLCONF listening-port 屈暗,向主服務器發(fā)送從服務器的監(jiān)聽端口號。


發(fā)送端口信息.png

同步數(shù)據(jù)

Redis 2.8之后分為全量同步和增量同步脂男,具體的后面詳細講解养叛。

命令傳播

當同步數(shù)據(jù)完成后,主從服務器就會進入命令傳播階段宰翅,主服務器只要將自己執(zhí)行的寫命令發(fā)送給從服 務器弃甥,而從服務器只要一直執(zhí)行并接收主服務器發(fā)來的寫命令。

同步數(shù)據(jù)集
  • Redis 2.8以前使用SYNC命令同步復制

  • Redis 2.8之后采用PSYNC命令替代SYNC

舊版本實現(xiàn)方式

Redis 2.8以前:Redis的同步功能分為同步(sync)和命令傳播(command propagate)汁讼。

  1. 同步操作

    • 通過從服務器發(fā)送到SYNC命令給主服務器
    • 主服務器生成RDB文件并發(fā)送給從服務器淆攻,同時發(fā)送保存所有寫命令給從服務器
    • 從服務器清空之前數(shù)據(jù)并執(zhí)行解釋RDB文件
    • 保持數(shù)據(jù)一致(還需要命令傳播過程才能保持一致)


      舊版本同步操作.png
  2. 命令傳播操作

    同步操作完成后,主服務器執(zhí)行寫命令嘿架,該命令發(fā)送給從服務器并執(zhí)行瓶珊,使主從保存一致。

缺陷:

  • 沒有全量同步和增量同步的概念眶明,從服務器在同步時艰毒,會清空所有數(shù)據(jù)。
  • 主從服務器斷線后重復制搜囱,主服務器會重新生成RDB文件和重新記錄緩沖區(qū)的所有命令丑瞧,并全量同步到 從服務器上柑土。
新版本實現(xiàn)方式

Redis 2.8以后,在Redis 2.8之后使用PSYNC命令绊汹,具備完整重同步和部分重同步模式稽屏。

  • Redis 的主從同步,分為全量同步增量同步西乖。

  • 只有從機第一次連接上主機是全量同步狐榔。

  • 斷線重連有可能觸發(fā)全量同步也有可能是增量同步( master 判斷 runid 是否一致)。

  • 除此之外的情況都是增量同步获雕。

    斷線重連.png

全量同步

Redis的全量同步過程主要分三個階段:

  • 同步快照階段: Master 創(chuàng)建并發(fā)送快照RDB給 Slave 薄腻, Slave 載入并解析快照。 Master 同時將此階段所產(chǎn)生的新的寫命令存儲到緩沖區(qū)届案。
  • 同步寫緩沖階段: Master 向 Slave 同步存儲在緩沖區(qū)的寫操作命令庵楷。
  • 同步增量階段: Master 向 Slave 同步寫操作命令。
    新版本全量同步.png

增量同步

  • Redis增量同步主要指Slave完成初始化后開始正常工作時楣颠, Master 發(fā)生的寫操作同步到 Slave 的過程尽纽。
  • 通常情況下, Master 每執(zhí)行一個寫命令就會向 Slave 發(fā)送相同的寫命令童漩,然后 Slave 接收并執(zhí)行弄贿。
心跳檢測

在命令傳播階段,從服務器默認會以每秒一次的頻率向主服務器發(fā)送命令:

replconf ack <replication_offset>
#ack :應答 
#replication_offset:從服務器當前的復制偏移量

主要作用有三個

  1. 檢測主從的連接狀態(tài)

    • 檢測主從服務器的網(wǎng)絡連接狀態(tài)

    • 通過向主服務器發(fā)送INFO replication命令矫膨,可以列出從服務器列表差凹,可以看出從最后一次向主發(fā) 送命令距離現(xiàn)在過了多少秒。lag的值應該在0或1之間跳動豆拨,如果超過1則說明主從之間的連接有故障直奋。

  2. 輔助實現(xiàn)min-slaves

    • Redis可以通過配置防止主服務器在不安全的情況下執(zhí)行寫命令
      • min-slaves-to-write 3 (min-replicas-to-write 3 )
      • min-slaves-max-lag 10 (min-replicas-max-lag 10)

    上面的配置表示:從服務器的數(shù)量少于3個能庆,或者三個從服務器的延遲(lag)值都大于或等于10 秒時施禾,主服務器將拒絕執(zhí)行寫命令。這里的延遲值就是上面INFOreplication命令的lag值搁胆。

  3. 檢測命令丟失

    • 如果因為網(wǎng)絡故障弥搞,主服務器傳播給從服務器的寫命令在半路丟失,那么當從服務器向主服務器發(fā) 送REPLCONF ACK命令時渠旁,主服務器將發(fā)覺從服務器當前的復制偏移量少于自己的復制偏移量攀例, 然后主服務器就會根據(jù)從服務器提交的復制偏移量,在復制積壓緩沖區(qū)里面找到從服務器缺少的數(shù)據(jù),并將這些數(shù)據(jù)重新發(fā)送給從服務器顾腊。(補發(fā)) 網(wǎng)絡不斷

    • 增量同步:網(wǎng)斷了粤铭,再次連接時

哨兵模式

哨兵(sentinel)是Redis的高可用性(High Availability)的解決方案: 由一個或多個sentinel實例組成sentinel集群可以監(jiān)視一個或多個主服務器和多個從服務器。當主服務器進入下線狀態(tài)時杂靶,sentinel可以將該主服務器下的某一從服務器升級為主服務器繼續(xù)提供服 務梆惯,從而保證redis的高可用性酱鸭。

部署方案
哨兵模式部署方案.png
搭建配置

在一臺機器上采用偽分布式的方式部署。(生產(chǎn)環(huán)境應該是多臺機器)垛吗,根據(jù)上面的部署方案搭建如下:Redis-Master :127.0.0.1 6379凹髓。 采用安裝的方式,正常安裝和配置

  1. 安裝Redis5.0
cd /mnt/module
mkdir redis-ms
cd redis-ms
mkdir master slaver-1 slaver-2 sentinel1 sentinel2 sentinel3
cd /mnt/module/redis-5.0.5/src
make install PREFIX=/mnt/module/redis-ms/master
cd /mnt/module/redis-5.0.5
cp redis.conf /mnt/module/redis-ms/master/bin
  1. 修改redis.com
/mnt/module/redis-ms/master/bin
vim redis.conf

#默認綁定的是回環(huán)地址怯屉,默認不能被其他機器訪問
#bind 127.0.0.1
protected-mode no
daemonize yes


退出保存
  1. 配置兩個從節(jié)點
cp -r /mnt/module/redis-ms/master/* /mnt/module/redis-ms/slaver-1
cp -r /mnt/module/redis-ms/master/* /mnt/module/redis-ms/slaver-2
## 修改slaver-1配置文件
vim /mnt/module/redis-ms/slaver-1/bin/redis.conf

port 6380
replicaof 127.0.0.1 6379

## 修改slaver-2配置文件
vim /mnt/module/redis-ms/slaver-1/bin/redis.conf

port 6381
replicaof 127.0.0.1 6379
  1. 配置sentinel1
cd /mnt/module/redis-5.0.5/src
make install PREFIX=/mnt/module/redis-ms/sentinel1
cd /mnt/module/redis-5.0.5
cp sentinel.conf /mnt/module/redis-ms/sentinel1/bin
cd /mnt/module/redis-ms/sentinel1/bin
vim sentinel.conf

# 哨兵sentinel實例運行的端口 默認26379
port 26379
# 將`daemonize`由`no`改為`yes` 
daemonize yes
# 哨兵sentinel監(jiān)控的redis主節(jié)點的 ip port
# master-name 可以自己命名的主節(jié)點名字 只能由字母A-z蔚舀、數(shù)字0-9 、這三個字符".-_"組成锨络。
# quorum 當這些quorum個數(shù)sentinel哨兵認為master主節(jié)點失聯(lián) 那么這時 客觀上認為主節(jié)點失聯(lián)了 
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 127.0.0.1 6379 2
# 當在Redis實例中開啟了requirepass foobared 授權密碼 這樣所有連接Redis實例的客戶端都要提供密碼
# 設置哨兵sentinel 連接主從的密碼 注意必須為主從設置一樣的驗證密碼
# sentinel auth-pass <master-name> <password>
sentinel auth-pass mymaster MySUPER--secret-0123passw0rd
# 指定多少毫秒之后 主節(jié)點沒有應答哨兵sentinel 此時 哨兵主觀上認為主節(jié)點下線 默認30秒赌躺,改成3秒 # sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 3000
# 這個配置項指定了在發(fā)生failover主備切換時最多可以有多少個slave同時對新的master進行 同步, 這個數(shù)字越小羡儿,完成failover所需的時間就越長寿谴,但是如果這個數(shù)字越大,就意味著越 多的slave因為replication而不可用失受⊙忍可以通過將這個值設為 1 來保證每次只有一個slave 處于不能處理命令請求的狀態(tài)。
# sentinel parallel-syncs <master-name> <numslaves>
sentinel parallel-syncs mymaster 1
# 故障轉(zhuǎn)移的超時時間 failover-timeout 可以用在以下這些方面:
#1. 同一個sentinel對同一個master兩次failover之間的間隔時間拂到。
#2. 當一個slave從一個錯誤的master那里同步數(shù)據(jù)開始計算時間痪署。直到slave被糾正為向正確的master 那里同步數(shù)據(jù)時典奉。
#3.當想要取消一個正在進行的failover所需要的時間叉寂。 #4.當進行failover時,配置所有slaves指向新的master所需的最大時間窃植。不過领铐,即使過了這個超時悯森, slaves依然會被正確配置為指向master,但是就不按parallel-syncs所配置的規(guī)則來了
# 默認三分鐘
# sentinel failover-timeout <master-name> <milliseconds>
sentinel failover-timeout mymaster 180000
  1. 配置sentinel2绪撵、配置sentinel3
cd /mnt/module/redis-ms
cp -r /sentinel1/* sentinel2
cp -r /sentinel1/* sentinel3

# 配置sentinel2
cd /mnt/module/redis-ms/sentinel2/bin
vim sentinel.conf

port 26380

# 配置sentinel3
cd /mnt/module/redis-ms/sentinel3/bin
vim sentinel.conf

port 26381
  1. 啟動
#啟動redis-master和redis-slaver
cd /mnt/module/redis-ms/master/bin/
./redis-server redis.conf 
cd /mnt/module/redis-ms/slaver-1/bin/
./redis-server redis.conf 
cd /mnt/module/redis-ms/slaver-2/bin/
./redis-server redis.conf 
#啟動redis-sentinel
cd /mnt/module/redis-ms/sentinel1/bin/
./redis-sentinel sentinel.conf
cd /mnt/module/redis-ms/sentinel2/bin/
./redis-sentinel sentinel.conf
cd /mnt/module/redis-ms/sentinel3/bin/
./redis-sentinel sentinel.conf
  1. 查看啟動狀態(tài)
[root@hhb redis-ms]# ps -ef | grep redis
root      1559     1  0 8月05 ?       00:01:05 ./redis-server *:6380
root      1565     1  0 8月05 ?       00:01:14 ./redis-server *:6381
root      1572     1  0 8月05 ?       00:01:36 ./redis-sentinel *:26379 [sentinel]
root      1577     1  0 8月05 ?       00:01:35 ./redis-sentinel *:26380 [sentinel]
root      1582     1  0 8月05 ?       00:01:35 ./redis-sentinel *:26381 [sentinel]
root      1651     1  0 8月05 ?       00:01:04 ./redis-server *:6379
root      2928  2834  0 11:16 pts/0    00:00:00 grep --color=auto redis
  • 如果kill掉master瓢姻,會在兩個從節(jié)點選擇一個節(jié)點自動切換為master
  • 被kill的master如果被重啟,啟動后會自動變?yōu)閟laver
  • master讀寫音诈,slaver只能讀
執(zhí)行流程
啟動并初始化Sentinel

Sentinel是一個特殊的Redis服務器幻碱。不會進行持久化,Sentinel實例啟動后细溅,每個Sentinel會創(chuàng)建2個連向主服務器的網(wǎng)絡連接 命令連接:用于向主服務器發(fā)送命令褥傍,并接收響應;

訂閱連接:用于訂閱主服務器的—sentinel—:hello頻道。


啟動并初始化Sentinel.png
獲取主服務信息

Sentinel默認每10s一次喇聊,向被監(jiān)控的主服務器發(fā)送info命令恍风,獲取主服務器和其下屬從服務器的信息。

 info
# Server
redis_version:5.0.5
redis_git_sha1:00000000
redis_git_dirty:0

……
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=9968302,lag=0
slave1:ip=127.0.0.1,port=6379,state=online,offset=9968036,lag=0
master_replid:42c43604e651941fec4e47ac2cf86602a7965650
master_replid2:0ed1c001d719e6590fee6da5fd9392c9f0dcd1f5
master_repl_offset:9968302
second_repl_offset:83024
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:8919727
repl_backlog_histlen:1048576

獲取從服務器信息

當Sentinel發(fā)現(xiàn)主服務器有新的從服務器出現(xiàn)時,Sentinel還會向從服務器建立命令連接和訂閱連接朋贬。 在命令連接建立之后鸥咖,Sentinel還是默認10s一次,向從服務器發(fā)送info命令兄世,并記錄從服務器的信息啼辣。


獲取從服務器信息.png
127.0.0.1:6379> info
# Server
redis_version:5.0.5
redis_git_sha1:00000000
……
# Replication
role:slave
master_host:127.0.0.1
master_port:6381
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_repl_offset:9921416
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:42c43604e651941fec4e47ac2cf86602a7965650
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:9921416
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:8872841
repl_backlog_histlen:1048576
……
向主服務器和從服務器發(fā)送消息(以訂閱的方式)

默認情況下,Sentinel每2s一次御滩,向所有被監(jiān)視的主服務器和從服務器所訂閱的_sentinel_:hello頻道上發(fā)送消息鸥拧,消息中會攜帶Sentinel自身的信息和主服務器的信息。

PUBLISH _sentinel_:hello "< s_ip > < s_port >< s_runid >< s_epoch > < m_name > <m_ip >< m_port ><m_epoch>"
接收來自主服務器和從服務器的頻道信息

當Sentinel與主服務器或者從服務器建立起訂閱連接之后削解,Sentinel就會通過訂閱連接富弦,向服務器發(fā)送 以下命令:

subscribe —sentinel—:hello

Sentinel彼此之間只創(chuàng)建命令連接,而不創(chuàng)建訂閱連接氛驮,因為Sentinel通過訂閱主服務器或從服務器腕柜,就可以感知到新的Sentinel的加入,而一旦新Sentinel加入后矫废,相互感知的Sentinel通過命令連接來通信就可以了盏缤。

檢測主觀下線狀態(tài)

當一個Sentinel將一個主服務器判斷為主觀下線后,Sentinel會向同時監(jiān)控這個主服務器的所有其他Sentinel發(fā)送查詢命令

主機的

SENTINEL is-master-down-by-addr <ip> <port> <current_epoch> <runid>

其他Sentinel回復

<down_state>< leader_runid >< leader_epoch >

判斷它們是否也認為主服務器下線。如果達到Sentinel配置中的quorum數(shù)量的Sentinel實例都判斷主服務器為主觀下線蓖扑,則該主服務器就會被判定為客觀下線(ODown)唉铜。

選舉Leader Sentinel

當一個主服務器被判定為客觀下線后,監(jiān)視這個主服務器的所有Sentinel會通過選舉算法(raft)律杠,選出一個Leader Sentinel去執(zhí)行failover(故障轉(zhuǎn)移)操作潭流。

哨兵leader選舉

Raft

Raft協(xié)議是用來解決分布式系統(tǒng)一致性問題的協(xié)議。 Raft協(xié)議描述的節(jié)點共有三種狀態(tài):Leader, Follower, Candidate柜去。

term:Raft協(xié)議將時間切分為一個個的Term(任期)灰嫉,可以認為是一種“邏輯時間”。

選舉流程:
Raft采用心跳機制觸發(fā)Leader選舉嗓奢,系統(tǒng)啟動后讼撒,全部節(jié)點初始化為Follower,term為0蔓罚。節(jié)點如果收到了RequestVote或者AppendEntries椿肩,就會保持自己的Follower身份瞻颂;節(jié)點如果一段時間內(nèi)沒收AppendEntries消息豺谈,在該節(jié)點的超時時間內(nèi)還沒發(fā)現(xiàn)Leader,F(xiàn)ollower就會轉(zhuǎn)換成Candidate贡这,自己開始競選Leader茬末。一旦轉(zhuǎn)化為Candidate,該節(jié)點立即開始下面幾件事情:

  • 增加自己的term。

  • 啟動一個新的定時器丽惭。

  • 給自己投一票击奶。

  • 向所有其他節(jié)點發(fā)送RequestVote,并等待其他節(jié)點的回復责掏。

如果在計時器超時前柜砾,節(jié)點收到多數(shù)節(jié)點的同意投票,就轉(zhuǎn)換成Leader换衬。同時向所有其他節(jié)點發(fā)送 AppendEntries痰驱,告知自己成為了Leader。每個節(jié)點在一個term內(nèi)只能投一票瞳浦,采取先到先得的策略担映,Candidate前面說到已經(jīng)投給了自己, Follower會投給第一個收到RequestVote的節(jié)點叫潦。Raft協(xié)議的定時器采取隨機超時時間蝇完,這是選舉Leader的關鍵。 在同一個term內(nèi)矗蕊,先轉(zhuǎn)為Candidate的節(jié)點會先發(fā)起投票短蜕,從而獲得多數(shù)票。

Sentinel的leader選舉流程

  1. 某Sentinel認定master客觀下線后傻咖,該Sentinel會先看看自己有沒有投過票忿危,如果自己已經(jīng)投過票給其他Sentinel了,在一定時間內(nèi)自己就不會成為Leader没龙。

  2. 如果該Sentinel還沒投過票铺厨,那么它就成為Candidate。

  3. Sentinel需要完成幾件事情:

    • 更新故障轉(zhuǎn)移狀態(tài)為start
    • 當前epoch加1硬纤,相當于進入一個新term解滓,在Sentinel中epoch就是Raft協(xié)議中的term。
    • 向其他節(jié)點發(fā)送 is-master-down-by-addr 命令請求投票筝家。命令會帶上自己的epoch洼裤。
    • 給自己投一票(leader、leader_epoch)
  4. 當其它哨兵收到此命令時溪王,可以同意或者拒絕它成為領導者;(通過判斷epoch)

  5. Candidate會不斷的統(tǒng)計自己的票數(shù)腮鞍,直到他發(fā)現(xiàn)認同他成為Leader的票數(shù)超過一半而且超過它配置的quorum,這時它就成為了Leader莹菱。

  6. 其他Sentinel等待Leader從slave選出master后移国,檢測到新的master正常工作后,就會去掉客觀下線的標識道伟。

故障轉(zhuǎn)移

當選舉出Leader Sentinel后迹缀,Leader Sentinel會對下線的主服務器執(zhí)行故障轉(zhuǎn)移操作使碾,主要有三個步 驟:

  1. 它會將失效 Master 的其中一個 Slave 升級為新的 Master , 并讓失效 Master 的其他 Slave 改為復制新的 Master ;
  2. 當客戶端試圖連接失效的 Master 時,集群也會向客戶端返回新 Master 的地址祝懂,使得集群可以使用現(xiàn)在的 Master 替換失效 Master 票摇。
  3. Master 和 Slave 服務器切換后, Master 的 redis.conf 砚蓬、 Slave 的 redis.conf 和 sentinel.conf 的配置文件的內(nèi)容都會發(fā)生相應的改變矢门,即, Master 主服務器的 redis.conf配置文件中會多一行 replicaof 的配置灰蛙, sentinel.conf 的監(jiān)控目標會隨之調(diào)換颅和。
主服務器的選擇

哨兵leader根據(jù)以下規(guī)則從客觀下線的主服務器的從服務器中選擇出新的主服務器。

  1. 過濾掉主觀下線的節(jié)點
  2. 選擇slave-priority最高的節(jié)點缕允,如果由則返回沒有就繼續(xù)選擇
  3. 選擇出復制偏移量最大的系節(jié)點峡扩,因為復制偏移量越大則數(shù)據(jù)復制的越完整,如果由就返回了障本,沒有就繼續(xù)
  4. 選擇run_id最小的節(jié)點教届,因為run_id越小說明重啟次數(shù)越少

集群與分區(qū)

分區(qū)是將數(shù)據(jù)分布在多個Redis實例(Redis主機)上,以至于每個實例只包含一部分數(shù)據(jù)驾霜。

分區(qū)的意義
  • 性能的提升

    單機Redis的網(wǎng)絡I/O能力和計算資源是有限的案训,將請求分散到多臺機器,充分利用多臺機器的計算能力可網(wǎng)絡帶寬粪糙,有助于提高Redis總體的服務能力强霎。

  • 存儲能力的橫向擴展

    即使Redis的服務能力能夠滿足應用需求,但是隨著存儲數(shù)據(jù)的增加蓉冈,單臺機器受限于機器本身的存儲 容量城舞,將數(shù)據(jù)分散到多臺機器上存儲使得Redis服務可以橫向擴展。

分區(qū)的方式

根據(jù)分區(qū)鍵(id)進行分區(qū):

范圍分區(qū)

根據(jù)id數(shù)字的范圍比如1--10000寞酿、100001--20000.....90001-100000家夺,每個范圍分到不同的Redis實例中

好處: 實現(xiàn)簡單,方便遷移和擴展

缺陷: 熱點數(shù)據(jù)分布不均伐弹,性能損失

? 非數(shù)字型key拉馋,比如uuid無法使用(可采用雪花算法替代) 分布式環(huán)境 主鍵 雪花算法
? 是數(shù)字
能排序

hash分區(qū)

利用簡單的hash算法即可:Redis實例=hash(key)%N

key:要進行分區(qū)的鍵,比如user_id N:Redis實例個數(shù)(Redis主機)

好處:支持任何類型的key惨好, 熱點分布較均勻煌茴,性能較好
缺陷: 遷移復雜,需要重新計算日川,擴展較差(利用一致性hash環(huán))

一致性hash

基本概念

普通hash是對主機數(shù)量取模蔓腐,而一致性hash是對2^32(4 294 967 296)取模。我們把2^32想象成一 個圓逗鸣,就像鐘表一樣合住,鐘表的圓可以理解成由60個點組成的圓绰精,而此處我們把這個圓想象成由2^32個 點組成的圓撒璧,(Hash環(huán))


Hash環(huán).png

圓環(huán)的正上方的點代表0透葛,0點右側的第一個點代表1,以此類推卿樱,2僚害、3、4繁调、5萨蚕、6......直到2^32-1,也就 是說0點左側的第一個點代表2^32-1 。我們把這個由2的32次方個點組成的圓環(huán)稱為hash環(huán)蹄胰。

假設我們有3臺緩存服務器岳遥,服務器A、服務器B裕寨、服務器C浩蓉,那么,在生產(chǎn)環(huán)境中宾袜,這三臺服務器肯定有自己的IP地址捻艳,我們使用它們各自的IP地址進行哈希計算,使用哈希后的結果對2^32取模庆猫,可以使用 如下公式:

hash(服務器的IP地址) % 2^32

通過上述公式算出的結果一定是一個0到232-1之間的一個整數(shù)认轨,我們就用算出的這個整數(shù),代表服務器A月培、服務器B嘁字、服務器C,既然這個整數(shù)肯定處于0到232-1之間杉畜,那么拳锚,上圖中的hash環(huán)上必定有一 個點與這個整數(shù)對應,也就是服務器A寻行、服務器B霍掺、服務C就可以映射到這個環(huán)上,如下圖:

hash環(huán)1.png

假設拌蜘,我們需要使用Redis緩存數(shù)據(jù)杆烁,那么我們使用公式:hash(key) % 2^32可以將數(shù)據(jù)映射到上圖中的hash環(huán)上。
映射后的示意圖如下简卧,下圖中的橘黃色圓形表示數(shù)據(jù)

hash環(huán)2.png

現(xiàn)在服務器與數(shù)據(jù)都被映射到了hash環(huán)上兔魂,上圖中的數(shù)據(jù)將會被緩存到服務器A上,因為從數(shù)據(jù)的位置開始举娩,沿順時針方向遇到的第一個服務器就是A服務器析校,所以构罗,上圖中的數(shù)據(jù)將會被緩存到服務器A上。 如圖:


hash環(huán)3.png

將緩存服務器與被緩存對象都映射到hash環(huán)上以后智玻,從被緩存對象的位置出發(fā)遂唧,沿順時針方向遇到的第一個服務器,就是當前對象將要緩存于的服務器吊奢,由于被緩存對象與服務器hash后的值是固定的盖彭,所以,在服務器不變的情況下页滚,數(shù)據(jù)必定會被緩存到固定的服務器上召边,那么,當下次想要訪問這個數(shù)據(jù) 時裹驰,只要再次使用相同的算法進行計算隧熙,即可算出這個數(shù)據(jù)被緩存在哪個服務器上,直接去對應的服務器查找對應的數(shù)據(jù)即可幻林。多條數(shù)據(jù)存儲如下:


hash環(huán)4.png

優(yōu)點
添加或移除節(jié)點時贞盯,數(shù)據(jù)只需要做部分的遷移,比如上圖中把C服務器移除滋将,則數(shù)據(jù)4遷移到服務器A 中邻悬,而其他的數(shù)據(jù)保持不變。添加效果是一樣的随闽。
hash環(huán)偏移
在介紹一致性哈希的概念時父丰,我們理想化的將3臺服務器均勻的映射到了hash環(huán)上。也就是說數(shù)據(jù)的范圍是2^32/N掘宪。但實際情況往往不是這樣的蛾扇。有可能某個服務器的數(shù)據(jù)會很多,某個服務器的數(shù)據(jù)會很 少魏滚,造成服務器性能不平均镀首。這種現(xiàn)象稱為hash環(huán)偏移。

hash環(huán)5.png

理論上我們可以通過增加服務器的方式來減少偏移鼠次,但這樣成本較高更哄,所以我們可以采用虛擬節(jié)點的方式,也就是虛擬服務器腥寇,如圖:


hash環(huán)6.png

"虛擬節(jié)點"是"實際節(jié)點"(實際的物理服務器)在hash環(huán)上的復制品,一個實際節(jié)點可以對應多個虛擬節(jié)點成翩。從上圖可以看出,A赦役、B麻敌、C三臺服務器分別虛擬出了一個虛擬節(jié)點,當然掂摔,如果你需要术羔,也可以虛擬出 更多的虛擬節(jié)點赢赊。引入虛擬節(jié)點的概念后,緩存的分布就均衡多了级历,上圖中释移,1號、3號數(shù)據(jù)被緩存在服 務器A中鱼喉,5號秀鞭、4號數(shù)據(jù)被緩存在服務器B中趋观,6號扛禽、2號數(shù)據(jù)被緩存在服務器C中,如果你還不放心皱坛,可 以虛擬出更多的虛擬節(jié)點编曼,以便減小hash環(huán)偏斜所帶來的影響,虛擬節(jié)點越多剩辟,hash環(huán)上的節(jié)點就越多掐场,緩存被均勻分布的概率就越大。

缺點

  • 復雜度高 客戶端需要自己處理數(shù)據(jù)路由贩猎、高可用熊户、故障轉(zhuǎn)移等問題

  • 使用分區(qū),數(shù)據(jù)的處理會變得復雜吭服,不得不對付多個redis數(shù)據(jù)庫和AOF文件嚷堡,不得在多個實例和主機之 間持久化你的數(shù)據(jù)。

  • 不易擴展

  • 一旦節(jié)點的增或者刪操作艇棕,都會導致key無法在redis中命中蝌戒,必須重新根據(jù)節(jié)點計算,并手動遷移全部 或部分數(shù)據(jù)沼琉。

官方cluster分區(qū)

Redis3.0之后北苟,Redis官方提供了完整的集群解決方案。

方案采用去中心化的方式打瘪,包括:sharding(分區(qū))友鼻、replication(復制)、failover(故障轉(zhuǎn)移)闺骚。 稱為RedisCluster彩扔。Redis5.0前采用redis-trib進行集群的創(chuàng)建和管理,需要ruby支持葛碧;Redis5.0可以直接使用Redis-cli進行集群的創(chuàng)建和管理

部署架構
官方cluster分區(qū)部署架構.png
去中心化

RedisCluster由多個Redis節(jié)點組構成借杰,是一個P2P(點對點)無中心節(jié)點的集群架構,依靠Gossip協(xié)議傳播的集群进泼。

Gossip協(xié)議

Gossip協(xié)議是一個通信協(xié)議蔗衡,一種傳播消息的方式纤虽。
起源于:病毒傳播
Gossip協(xié)議基本思想就是: 一個節(jié)點周期性(每秒)隨機選擇一些節(jié)點,并把信息傳遞給這些節(jié)點绞惦。這些收到信息的節(jié)點接下來會做同樣的事情逼纸,即把這些信息傳遞給其他一些隨機選擇的節(jié)點。信息會周期性的傳遞給N個目標節(jié)點济蝉。這個N被稱為fanout(扇出) gossip協(xié)議包含多種消息,包括meet杰刽、ping、pong王滤、fail贺嫂、publish等等。

命令 說明
meet sender向receiver發(fā)出雁乡,請求receiver加入sender的集群
ping 節(jié)點檢測其他節(jié)點是否在線
pong receiver收到meet或ping后的回復信息;在failover后第喳,新的Master也會廣播pong
fail 節(jié)點A判斷節(jié)點B下線后,A節(jié)點廣播B的fail信息踱稍,其他收到節(jié)點會將B節(jié)點標記為下線
publish 節(jié)點A收到publish命令曲饱,節(jié)點A執(zhí)行該命令,并向集群廣播publish命令珠月,收到publish 命令的節(jié)點都會執(zhí)行相同的publish命令

通過gossip協(xié)議扩淀,cluster可以提供集群間狀態(tài)同步更新、選舉自助failover等重要的集群功能啤挎。

slot(Hash槽)

redis-cluster把所有的物理節(jié)點映射到[0-16383]個slot上,基本上采用平均分配和連續(xù)分配的方式驻谆。 比如上圖中有5個主節(jié)點,這樣在RedisCluster創(chuàng)建時侵浸,slot槽可按下表分配:

節(jié)點名稱 slot范圍
Redis1 0-3270
Redis2 3271-6542
Redis3 6543-9814
Redis4 9815-13087
Redis5 13088-16383

cluster 負責維護節(jié)點和slot槽的對應關系 value------>slot-------->節(jié)點

當需要在 Redis 集群中放置一個 key-value 時旺韭,redis 先對 key 使用 crc16 算法算出一個結果,然后把結果對 16384 求余數(shù)掏觉,這樣每個 key 都會對應一個編號在 0-16383 之間的哈希槽区端,redis 會根據(jù)節(jié)點數(shù)量大致均等的將哈希槽映射到不同的節(jié)點。

比如:

set name zhaoyun
hash("name")采用crc16算法澳腹,得到值:1324203551%16384=15903 根據(jù)上表15903在13088-16383之間织盼,所以name被存儲在Redis5節(jié)點。 slot槽必須在節(jié)點上連續(xù)分配酱塔,如果出現(xiàn)不連續(xù)的情況沥邻,則RedisCluster不能工作,詳見容錯羊娃。

RedisCluster的優(yōu)勢
  • 高性能

    • Redis Cluster 的性能與單節(jié)點部署是同級別的唐全。
    • 多主節(jié)點、負載均衡、讀寫分離
  • 高可用

    • Redis Cluster 支持標準的 主從復制配置來保障高可用和高可靠邮利。

    • failover

    • Redis Cluster 也實現(xiàn)了一個類似 Raft 的共識方式弥雹,來保障整個集群的可用性。

  • 易擴展

    • 向 Redis Cluster 中添加新節(jié)點延届,或者移除節(jié)點剪勿,都是透明的,不需要停機方庭。

    • 水平厕吉、垂直方向都非常容易擴展。

    • 數(shù)據(jù)分區(qū)械念,海量數(shù)據(jù)头朱,數(shù)據(jù)存儲

  • 原生

    • 部署 Redis Cluster 不需要其他的代理或者工具,而且 Redis Cluster 和單機 Redis 幾乎完全兼 容订讼。
集群搭建

RedisCluster最少需要三臺主服務器髓窜,三臺從服務器扇苞。 端口號分別為:7001~7006

mkdir /mnt/module/redis-cluster/7001
cd /mnt/module/redis-5.0.5/src/
make install PREFIX=/mnt/module/redis-cluster/7001
cd ../
cp redis.conf /mnt/module/redis-cluster/7001/bin/
cd /mnt/module/redis-cluster/7001/bin/
vim redis.conf


#bind 127.0.0.1
protected-mode no
port 7001
daemonize yes
cluster-enabled yes
保存后退出

cd /mnt/module/redis-cluster/
cp -r 7001/ 7002
cp -r 7001/ 7003
cp -r 7001/ 7004
cp -r 7001/ 7005
cp -r 7001/ 7006
cp -r 7001/ 7007
cp -r 7001/ 7008

修改各自的端口號后使用腳本啟動

腳本

#!/bin/bash

for((fileUrl=7001;fileUrl<7007;fileUrl++));
do
cd /mnt/module/redis-cluster/${fileUrl}/bin;
./redis-server redis.conf
done

創(chuàng)建Redis集群

# cluster-replicas : 1 1從機 前三個為主
# 采用機器ip而不采用127.0.0.1 不然外界無法訪問
cd /mnt/module/redis-cluster/7001/bin
#內(nèi)網(wǎng)
./redis-cli --cluster create 172.17.178.97:7001 172.17.178.97:7002 172.17.178.97:7003 172.17.178.97:7004 172.17.178.97:7005 172.17.178.97:7006 --cluster-replicas 1
#外網(wǎng)
./redis-cli --cluster create 59.110.241.53:7001 59.110.241.53:7002 59.110.241.53:7003 59.110.241.53:7004 59.110.241.53:7005 59.110.241.53:7006 --cluster-replicas 1
 
 
 >>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.17.178.97:7005 to 172.17.178.97:7001
Adding replica 172.17.178.97:7006 to 172.17.178.97:7002
Adding replica 172.17.178.97:7004 to 172.17.178.97:7003
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 8b1c6dc5f91f2ddb4f7ff0c23c6a501a56b60b20 172.17.178.97:7001
   slots:[0-5460] (5461 slots) master
M: 877873f64d477171bbca95d00c31645c6b8da6b6 172.17.178.97:7002
   slots:[5461-10922] (5462 slots) master
M: 2e83fbb78a23979f8bfe8951a4481c4208fa6011 172.17.178.97:7003
   slots:[10923-16383] (5461 slots) master
S: 52fa7da53aa2e4bb1f269518217c5b5797b487a4 172.17.178.97:7004
   replicates 877873f64d477171bbca95d00c31645c6b8da6b6
S: 6e6e94c96e233f60856ab4542af657804218036d 172.17.178.97:7005
   replicates 2e83fbb78a23979f8bfe8951a4481c4208fa6011
S: d9ff1122796730b899e64d5893b2a5d56a9e3853 172.17.178.97:7006
   replicates 8b1c6dc5f91f2ddb4f7ff0c23c6a501a56b60b20
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
....
>>> Performing Cluster Check (using node 172.17.178.97:7001)
M: 8b1c6dc5f91f2ddb4f7ff0c23c6a501a56b60b20 172.17.178.97:7001
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
M: 2e83fbb78a23979f8bfe8951a4481c4208fa6011 172.17.178.97:7003
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: d9ff1122796730b899e64d5893b2a5d56a9e3853 172.17.178.97:7006
   slots: (0 slots) slave
   replicates 8b1c6dc5f91f2ddb4f7ff0c23c6a501a56b60b20
S: 6e6e94c96e233f60856ab4542af657804218036d 172.17.178.97:7005
   slots: (0 slots) slave
   replicates 2e83fbb78a23979f8bfe8951a4481c4208fa6011
M: 877873f64d477171bbca95d00c31645c6b8da6b6 172.17.178.97:7002
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 52fa7da53aa2e4bb1f269518217c5b5797b487a4 172.17.178.97:7004
   slots: (0 slots) slave
   replicates 877873f64d477171bbca95d00c31645c6b8da6b6
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

鏈接集群

cd /mnt/module/redis-cluster/7001/bin
./redis-cli -h 127.0.0.1 -p 7001 -c

set k1 v1
-> Redirected to slot [12706] located at 172.17.178.97:7003
OK
172.17.178.97:7003>

查看集群狀態(tài)

cluster info

查看集群中的節(jié)點

cluster nodes

分片

不同節(jié)點分組服務于相互無交集的分片(sharding)欺殿,Redis Cluster不存在單獨的proxy或配置服務器,所以需要將客戶端路由到目標的分片鳖敷。

客戶端路由

Redis Cluster的客戶端相比單機Redis 需要具備路由語義的識別能力脖苏,且具備一定的路由緩存能力。

moved重定向
  1. 每個節(jié)點通過通信都會共享Redis Cluster中槽和集群中對應節(jié)點的關系
  2. 客戶端向Redis Cluster的任意節(jié)點發(fā)送命令定踱,接收命令的節(jié)點會根據(jù)CRC16規(guī)則進行hash運算與16384取余棍潘,計算自己的槽和對應節(jié)點
  3. 如果保存數(shù)據(jù)的槽被分配給當前節(jié)點,則去槽中執(zhí)行命令崖媚,并把命令執(zhí)行結果返回給客戶端
  4. 如果保存數(shù)據(jù)的槽不在當前節(jié)點的管理范圍內(nèi)亦歉,則向客戶端返回moved重定向異常
  5. 客戶端接收到節(jié)點返回的結果,如果是moved異常畅哑,則從moved異常中獲取目標節(jié)點的信息
  6. 客戶端向目標節(jié)點發(fā)送命令肴楷,獲取命令執(zhí)行結果


    moved重定向.png
set k1 v1
-> Redirected to slot [12706] located at 172.17.178.97:7003
OK
172.17.178.97:7003> set k2 v2
-> Redirected to slot [449] located at 172.17.178.97:7001
OK
172.17.178.97:7001> get ke
-> Redirected to slot [11219] located at 172.17.178.97:7003
(nil)
172.17.178.97:7003> get k2
-> Redirected to slot [449] located at 172.17.178.97:7001
"v2"
172.17.178.97:7001>
ask重定向

在對集群進行擴容和縮容時,需要對槽及槽中數(shù)據(jù)進行遷移荠呐。當客戶端向某個節(jié)點發(fā)送命令赛蔫,節(jié)點向客戶端返回moved異常,告訴客戶端數(shù)據(jù)對應的槽的節(jié)點信息泥张。如果此時正在進行集群擴展或者縮空操作呵恢,當客戶端向正確的節(jié)點發(fā)送命令時,槽及槽中數(shù)據(jù)已經(jīng)被遷 移到別的節(jié)點了媚创,就會返回ask渗钉,這就是ask重定向機制。

  1. 客戶端向目標節(jié)點發(fā)送命令钞钙,目標節(jié)點中的槽已經(jīng)遷移支別的節(jié)點上了鳄橘,此時目標節(jié)點會返回ask轉(zhuǎn) 向給客戶端
  2. 客戶端向新的節(jié)點發(fā)送Asking命令給新的節(jié)點粤剧,然后再次向新節(jié)點發(fā)送命令
  3. 新節(jié)點執(zhí)行命令,把命令執(zhí)行結果返回給客戶端


    ask重定向.png

moved和ask的區(qū)別

  1. moved:槽已確認轉(zhuǎn)移

  2. ask:槽還在轉(zhuǎn)移過程中

Smart智能客戶端

JedisCluster

  • JedisCluster是Jedis根據(jù)RedisCluster的特性提供的集群智能客戶端
  • JedisCluster為每個節(jié)點創(chuàng)建連接池挥唠,并跟節(jié)點建立映射關系緩存(Cluster slots)
  • JedisCluster將每個主節(jié)點負責的槽位一一與主節(jié)點連接池建立映射緩存
  • JedisCluster啟動時抵恋,已經(jīng)知道key,slot和node之間的關系,可以找到目標節(jié)點
  • JedisCluster對目標節(jié)點發(fā)送命令宝磨,目標節(jié)點直接響應給JedisCluster
  • 如果JedisCluster與目標節(jié)點連接出錯弧关,則JedisCluster會知道連接的節(jié)點是一個錯誤的節(jié)點此時節(jié)點返回moved異常給JedisCluster
  • JedisCluster會重新初始化slot與node節(jié)點的緩存關系,然后向新的目標節(jié)點發(fā)送命令唤锉,目標命令執(zhí)行 命令并向JedisCluster響應
  • 如果命令發(fā)送次數(shù)超過5次世囊,則拋出異常"Too many cluster redirection!"
JedisCluster.png
package com.hhb.redis;

import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPoolConfig;

import java.util.HashSet;
import java.util.Set;

/**
 * @description:
 * @author: huanghongbo
 * @date: 2020-08-08 20:34
 **/
public class TestRedisCluster {


    public static void main(String[] args) {
        JedisPoolConfig config = new JedisPoolConfig();
        Set<HostAndPort> jedisClusterNode = new HashSet<>();
        jedisClusterNode.add(new HostAndPort("59.110.241.53", 7001));
        jedisClusterNode.add(new HostAndPort("59.110.241.53", 7002));
        jedisClusterNode.add(new HostAndPort("59.110.241.53", 7003));
        jedisClusterNode.add(new HostAndPort("59.110.241.53", 7004));
        jedisClusterNode.add(new HostAndPort("59.110.241.53", 7005));
        jedisClusterNode.add(new HostAndPort("59.110.241.53", 7006));
        JedisCluster jcd = new JedisCluster(jedisClusterNode, config);
        jcd.set("k3", "v3");
        String value = jcd.get("k3");
        System.err.println(value);
    }
}

遷移

在RedisCluster中每個slot 對應的節(jié)點在初始化后就是確定的。在某些情況下窿祥,節(jié)點和分片需要變更:

  • 新的節(jié)點作為master加入;
  • 某個節(jié)點分組需要下線;
  • 負載不均衡需要調(diào)整slot 分布株憾。

此時需要進行分片的遷移,遷移的觸發(fā)和過程控制由外部系統(tǒng)完成晒衩。包含下面 2 種:

  • 節(jié)點遷移狀態(tài)設置:遷移前標記源/目標節(jié)點嗤瞎。
  • key遷移的原子化命令:遷移的具體步驟。


    遷移.png
  1. 向節(jié)點B發(fā)送狀態(tài)變更命令听系,將B的對應slot 狀態(tài)置為importing贝奇。
  2. 向節(jié)點A發(fā)送狀態(tài)變更命令, 將A對應的slot 狀態(tài)置為migrating靠胜。
  3. 向A 發(fā)送migrate 命令掉瞳,告知A 將要遷移的slot對應的key 遷移 到B。
  4. 當所有key 遷移完成后浪漠,cluster setslot 重新設置槽位陕习。
擴容

添加7007節(jié)點作為新節(jié)點并啟動,執(zhí)行命令:

./redis-cli --cluster add-node 59.110.241.53:7007 59.110.241.53:7001

查看節(jié)點是否已經(jīng)在集群中,并查看槽的占用情況

./redis-cli -p 7001 -c 
CLUSTER nodes
13f77364e10de63db22063560e20724125e20b42 59.110.241.53:7007@17007 master - 0 1596890975494 0 connected
225800182356811bb577b66f270e49c04af80f9a 59.110.241.53:7005@17005 slave 5113642d08eab63f4602b99204cb6bab28444ec2 0 1596890972488 5 connected
5113642d08eab63f4602b99204cb6bab28444ec2 59.110.241.53:7003@17003 master - 0 1596890974000 3 connected 10923-16383
ffb0706238bf1e7ea4ea209d7199fe03e9a05b16 59.110.241.53:7004@17004 slave 6cdaeb5164b965de2d4d62e1df9802133b2526af 0 1596890974492 4 connected
6cdaeb5164b965de2d4d62e1df9802133b2526af 59.110.241.53:7002@17002 master - 0 1596890972000 2 connected 5461-10922
c0e12e62ef75b7a8e002403157174e52de90f589 59.110.241.53:7006@17006 slave 479aa58ee3ca3717a95a1dc9678ab23c1b39a538 0 1596890973490 6 connected
479aa58ee3ca3717a95a1dc9678ab23c1b39a538 172.17.178.97:7001@17001 myself,master - 0 1596890973000 1 connected 0-5460

hash槽重新分配(數(shù)據(jù)遷移)

  1. 添加完主節(jié)點需要對主節(jié)點進行hash槽分配,這樣該主節(jié)才可以存儲數(shù)據(jù)。

查看集群中槽占用情況

cluster nodes

redis集群有16384個槽狈究,集群中的每個結點分配自已槽域醇,通過查看集群結點可以看到槽占用情況。

  1. 給剛添加的7007結點分配槽
./redis-cli --cluster reshard 59.110.241.53:7007
  1. 輸入要分配的槽數(shù)量
How many slots do you want to move (from 1 to 16384)? 3000

輸入:3000,表示要給目標節(jié)點分配3000個槽

  1. 輸入接收槽的結點id
What is the receiving node ID?

輸入:13f77364e10de63db22063560e20724125e20b42

PS:這里準備給7007分配槽,通過cluster nodes查看7007結點id為: 13f77364e10de63db22063560e20724125e20b42
  1. 輸入源結點id
Please enter all the source node IDs.
Type 'all' to use all the nodes as source nodes for the hash slots. Type 'done' once you entered all the source nodes IDs.

輸入all

  1. 輸入yes
Do you want to proceed with the proposed reshard plan (yes/no)? yes
  1. 查看結果
./redis-cli -h 127.0.0.1 -p 7001 -c
cluster nodes
  1. 添加從節(jié)點

命令

./redis-cli --cluster add-node 新節(jié)點的ip和端口 舊節(jié)點ip和端口 --cluster-slave -- cluster-master-id 主節(jié)點id

例如

./redis-cli --cluster add-node 59.110.241.53:7008 59.110.241.53:7007 --cluster-slave --cluster-master-id 13f77364e10de63db22063560e20724125e20b42

注意:如果原來該結點在集群中的配置信息已經(jīng)生成到cluster-config-file指定的配置文件中(如果 cluster-config-file沒有指定則默認為nodes.conf),這時可能會報錯:

[ERR] Node XXXXXX is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0

解決方法是刪除生成的配置文件nodes.conf塌忽,刪除后再執(zhí)行./redis-cli --cluster add-node指令

縮容

命令:

./redis-cli --cluster del-node 59.110.241.53:7008 bfe7657c5fc812dcf6498feea19871eed641a349

刪除已經(jīng)占有hash槽的結點會失敗,報錯如下:

[ERR] Node 127.0.0.1:7008 is not empty! Reshard data away and try again.

需要將該結點占用的hash槽分配出去失驶。

容災

故障檢測

集群中的每個節(jié)點都會定期地(每秒)向集群中的其他節(jié)點發(fā)送PING消息土居,如果在一定時間內(nèi)(cluster-node-timeout),發(fā)送ping的節(jié)點A沒有收到某節(jié)點B的pong回應,則A將B標識為pfail擦耀。A在后續(xù)發(fā)送ping時棉圈,會帶上B的pfail信息, 通知給其他節(jié)點眷蜓。如果B被標記為pfail的個數(shù)大于集群主節(jié)點個數(shù)的一半(N/2 + 1)時分瘾,B會被標記為fail,A向整個集群 廣播吁系,該節(jié)點已經(jīng)下線德召。其他節(jié)點收到廣播,標記B為fail汽纤。

從節(jié)點選舉

raft上岗,每個從節(jié)點,都根據(jù)自己對master復制數(shù)據(jù)的offset蕴坪,來設置一個選舉時間肴掷,offset越大(復制數(shù)據(jù)越多)的從節(jié)點,選舉時間越靠前背传,優(yōu)先進行選舉呆瞻。slave 通過向其他master發(fā)送FAILVOER_AUTH_REQUEST 消息發(fā)起競選, master 收到后回復FAILOVER_AUTH_ACK 消息告知是否同意续室。slave 發(fā)送FAILOVER_AUTH_REQUEST 前會將currentEpoch 自增栋烤,并將最新的Epoch 帶入到FAILOVER_AUTH_REQUEST 消息中,如果自己未投過票挺狰,則回復同意,否則回復拒絕买窟。所有的Master開始slave選舉投票丰泊,給要進行選舉的slave進行投票,如果大部分master node(N/2 + 1)都投票給了某個從節(jié)點始绍,那么選舉通過瞳购,那個從節(jié)點可以切換成master。

RedisCluster失效的判定:

  1. 集群中半數(shù)以上的主節(jié)點都宕機(無法投票)
  2. 宕機的主節(jié)點的從節(jié)點也宕機了(slot槽分配不連續(xù))
變更通知

當slave 收到過半的master 同意時亏推,會成為新的master学赛。此時會以最新的Epoch 通過PONG 消息廣播,自己成為master吞杭,讓Cluster 的其他節(jié)點盡快的更新拓撲結構(node.conf)盏浇。

主從切換

自動切換

就是上面講的從節(jié)點選舉

手動切換

人工故障切換是預期的操作,而非發(fā)生了真正的故障芽狗,目的是以一種安全的方式(數(shù)據(jù)無丟失)將當前 master節(jié)點和其中一個slave節(jié)點(執(zhí)行cluster-failover的節(jié)點)交換角色

  1. 向從節(jié)點發(fā)送cluster failover 命令(slaveof no one)
  2. 從節(jié)點告知其主節(jié)點要進行手動切換(CLUSTERMSG_TYPE_MFSTART)
  3. 主節(jié)點會阻塞所有客戶端命令的執(zhí)行(10s)
  4. 從節(jié)點從主節(jié)點的ping包中獲得主節(jié)點的復制偏移量
  5. 從節(jié)點復制達到偏移量绢掰,發(fā)起選舉、統(tǒng)計選票、贏得選舉滴劲、升級為主節(jié)點并更新配置
  6. 切換完成后攻晒,原主節(jié)點向所有客戶端發(fā)送moved指令重定向到新的主節(jié)點

以上是在主節(jié)點在線情況下。

如果主節(jié)點下線了班挖,則采用cluster failover force或cluster failover takeover 進行強制切換鲁捏。

副本漂移

我們知道在一主一從的情況下,如果主從同時掛了萧芙,那整個集群就掛了碴萧。 為了避免這種情況我們可以做一主多從,但這樣成本就增加了末购。 Redis提供了一種方法叫副本漂移破喻,這種方法既能提高集群的可靠性又不用增加太多的從機。 如圖:


副本漂移.png

Master1宕機盟榴,則Slaver11提升為新的Master1,集群檢測到新的Master1是單點的(無從機)曹质。集群從擁有最多的從機的節(jié)點組(Master3)中,選擇節(jié)點名稱字母順序最小的從機(Slaver31)漂移 到單點的主從節(jié)點組(Master1)擎场。具體流程如下(以上圖為例):

  1. 將Slaver31的從機記錄從Master3中刪除
  2. 將Slaver31的的主機改為Master1
  3. 在Master1中添加Slaver31為從節(jié)點
  4. 將Slaver31的復制源改為Master1
  5. 通過ping包將信息同步到集群的其他節(jié)點
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末羽德,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子迅办,更是在濱河造成了極大的恐慌宅静,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件站欺,死亡現(xiàn)場離奇詭異姨夹,居然都是意外死亡,警方通過查閱死者的電腦和手機矾策,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門磷账,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人贾虽,你說我怎么就攤上這事逃糟。” “怎么了蓬豁?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵绰咽,是天一觀的道長。 經(jīng)常有香客問我地粪,道長取募,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任驶忌,我火速辦了婚禮矛辕,結果婚禮上笑跛,老公的妹妹穿的比我還像新娘。我一直安慰自己聊品,他們只是感情好飞蹂,可當我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著翻屈,像睡著了一般陈哑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上伸眶,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天惊窖,我揣著相機與錄音,去河邊找鬼厘贼。 笑死界酒,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的嘴秸。 我是一名探鬼主播毁欣,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼岳掐!你這毒婦竟也來了凭疮?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤串述,失蹤者是張志新(化名)和其女友劉穎执解,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體纲酗,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡衰腌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了耕姊。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片桶唐。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖茉兰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情欣簇,我是刑警寧澤规脸,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站熊咽,受9級特大地震影響莫鸭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜横殴,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一被因、第九天 我趴在偏房一處隱蔽的房頂上張望卿拴。 院中可真熱鬧,春花似錦梨与、人聲如沸堕花。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缘挽。三九已至,卻和暖如春呻粹,著一層夾襖步出監(jiān)牢的瞬間壕曼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工等浊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留腮郊,地道東北人。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓筹燕,卻偏偏與公主長得像轧飞,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子庄萎,可洞房花燭夜當晚...
    茶點故事閱讀 43,446評論 2 348

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