Redis高可用方案

Redis高可用常見的有兩種方式:

  • 主從復(fù)制(Replication-Sentinel模式)
  • Redis集群(Redis-Cluster模式)

下面將分別介紹這兩種高可用方案。

搭建環(huán)境:
redis版本:redis-5.0.4
服務(wù)器環(huán)境:centos7

主從復(fù)制(Replication-Sentinel模式)

工作原理-Replication

Redis主從結(jié)構(gòu)如下圖所示,主節(jié)點(master)負責(zé)讀寫,從節(jié)點(slave)負責(zé)讀。這個系統(tǒng)的運行依靠三個主要的機制:

  • 當(dāng)一個 master 實例和一個 slave 實例連接正常時, master 會發(fā)送一連串的命令流來保持對 slave 的更新,以便于將自身數(shù)據(jù)集的改變復(fù)制給 slave ,包括客戶端的寫入、key 的過期或被逐出等等。
  • 當(dāng) master 和 slave 之間的連接斷開之后,因為網(wǎng)絡(luò)問題、或者是主從意識到連接超時, slave 重新連接上 master 并會嘗試進行部分重同步:這意味著它會嘗試只獲取在斷開連接期間內(nèi)丟失的命令流。
  • 當(dāng)無法進行部分重同步時, slave 會請求進行全量重同步。這會涉及到一個更復(fù)雜的過程,例如 master 需要創(chuàng)建所有數(shù)據(jù)的快照,將之發(fā)送給 slave ,之后在數(shù)據(jù)集更改時持續(xù)發(fā)送命令流到 slave 。
搭建步驟

安裝Redis

$ yum -y install gcc $ yum -y install gcc-c++
$ wget http://download.redis.io/releases/redis-5.0.4.tar.gz 
$ tar -zvxf redis-5.0.4.tar.gz 
$ cd redis-5.0.4 
$ make

這里我們將redis.conf文件復(fù)制兩份slave.conf和slave2.conf并修改配置

# 服務(wù)器端口號,主從分別修改為7001 7002 7003 
port 7001 
# 使得Redis可以跨網(wǎng)絡(luò)訪問 
bind 0.0.0.0 
# 配置reids的密碼 
requirepass "111111" 
# 下面兩個配置只需要配置從節(jié)點(slave) 
# 配置主服務(wù)器地址、端口號 
replicaof 127.0.0.1 7001 
# 主服務(wù)器密碼 
masterauth "111111"

分別啟動這三個Redis服務(wù)

$ ./src/redis-server redis.conf 
$ ./src/redis-server slave.conf 
$ ./src/redis-server slave2.conf

使用redis-cli工具連接redis服務(wù)查看主從節(jié)點是否搭建成功

$ ./src/redis-cli -h <主機名> -p <端口號> 
$ auth <password> 
$ info replication

看到類似如下所示信息則主從搭建成功

############主節(jié)點(master)信息#############
"# Replication
role:master
connected_slaves:2
slave0:ip=192.168.1.164,port=7002,state=online,offset=1015511,lag=0
slave1:ip=192.168.1.164,port=7003,state=online,offset=1015511,lag=0
master_replid:ffff866d17e11dcc9a9fd7bf3a487ad9e499fca9
master_replid2:1c8a6f05891dc72bbe4fefd9a54ff65eb46ce35d
master_repl_offset:1015511
second_repl_offset:424773
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:99239
repl_backlog_histlen:916273
"
############從節(jié)點(slave)信息#############
"# Replication
role:slave
master_host:192.168.1.164
master_port:7001
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_repl_offset:560709
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:ffff866d17e11dcc9a9fd7bf3a487ad9e499fca9
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:560709
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:549628
repl_backlog_histlen:11082
"
工作原理-Sentinel

簡單的主從集群有個問題,就是主節(jié)點掛了之后,無法從新選舉新的節(jié)點作為主節(jié)點進行寫操作,導(dǎo)致服務(wù)不可用。所以接下來介紹Sentinel(哨兵)功能的使用。哨兵是一個獨立的進程,哨兵會實時監(jiān)控master節(jié)點的狀態(tài),當(dāng)master不可用時會從slave節(jié)點中選出一個作為新的master,并修改其他節(jié)點的配置指向到新的master。

該系統(tǒng)執(zhí)行以下三個任務(wù):

  • 監(jiān)控(Monitoring):Sentinel 會不斷地檢查你的主服務(wù)器和從服務(wù)器是否運作正常。
  • 提醒(Notification):當(dāng)被監(jiān)控的某個 Redis 服務(wù)器出現(xiàn)問題時, Sentinel 可以通過 API 向管理員或者其他應(yīng)用程序發(fā)送通知。
  • 自動故障遷移(Automatic failover): 當(dāng)一個主服務(wù)器不能正常工作時, Sentinel 會開始一次自動故障遷移操作, 它會將失效主服務(wù)器的其中一個從服務(wù)器升級為新的主服務(wù)器, 并讓失效主服務(wù)器的其他從服務(wù)器改為復(fù)制新的主服務(wù)器; 當(dāng)客戶端試圖連接失效的主服務(wù)器時, 集群也會向客戶端返回新主服務(wù)器的地址, 使得集群可以使用新主服務(wù)器代替失效服務(wù)器。
搭建步驟

新開一臺服務(wù)器,并按上面的步驟下載安裝Redis。

將sentinel.conf文件復(fù)制兩份為sentinel2.conf、sentinel3.conf,并分別修改配置

# 三個配置文件分別配置不同的端口號
port <端口號>
# 設(shè)置為后臺啟動
daemonize yes
# 主節(jié)點的名稱(可以自定義,與后面的配置保持一致即可)
# 主機地址
# 端口號
# 數(shù)量(2代表只有兩個或兩個以上的哨兵認為主服務(wù)器不可用的時候,才會進行failover操作)
sentinel monitor mymaster 127.0.0.1 6379 2
# 多長時間沒有響應(yīng)認為主觀下線(SDOWN)
sentinel down-after-milliseconds mymaster 60000
# 表示如果15秒后,mysater仍沒活過來,則啟動failover,從剩下從節(jié)點序曲新的主節(jié)點
sentinel failover-timeout mymaster 15000
# 指定了在執(zhí)行故障轉(zhuǎn)移時, 最多可以有多少個從服務(wù)器同時對新的主服務(wù)器進行同步, 這個數(shù)字越小, 完成故障轉(zhuǎn)移所需的時間就越長
sentinel parallel-syncs mymaster 1

啟動三個sentinel

$ ./src/server-sentinel sentinel.conf 
$ ./src/server-sentinel sentinel2.conf 
$ ./src/server-sentinel sentinel3.conf

然后手動關(guān)閉主節(jié)點的redis服務(wù),并查看兩個slave信息是否有一個變成了master。

程序中使用

SpringBoot連接Redis主從集群配置

spring:
  redis:
    sentinel:
      master: mymaster
      nodes: 192.168.1.167:26379,192.168.1.167:26380,192.168.1.167:26381
    host: 192.168.1.164
    port: 7003
    database: 0
    password: <password>

參考文檔
http://www.redis.cn/topics/replication.html
http://www.redis.cn/topics/sentinel.html

Redis集群(Redis-Cluster)

工作原理

Redis 集群是一個提供在多個Redis節(jié)點間共享數(shù)據(jù)的程序集。下圖以三個master節(jié)點和三個slave節(jié)點作為示例。Redis 集群有16384個哈希槽,每個key通過CRC16校驗后對16384取模來決定放置哪個槽。集群的每個節(jié)點負責(zé)一部分hash槽,如圖中slots所示。

為了使在部分節(jié)點失敗或者大部分節(jié)點無法通信的情況下集群仍然可用,所以集群使用了主從復(fù)制模型,每個節(jié)點都會有1-n個從節(jié)點。例如master-A節(jié)點不可用了,集群便會選舉slave-A節(jié)點作為新的主節(jié)點繼續(xù)服務(wù)。


搭建步驟

Redis5.0之后的版本放棄了 Ruby 的集群方式,改為使用 C 語言編寫的redis-cli的方式,使集群的構(gòu)建方式復(fù)雜度大大降低。

下載安裝Redis(見主從復(fù)制模式的搭建步驟)。

創(chuàng)建6個Redis的配置文件,如下所示:

/usr/local/redis-5.0.4/redis-cluster-conf/7001/redis.conf
/usr/local/redis-5.0.4/redis-cluster-conf/7002/redis.conf
/usr/local/redis-5.0.4/redis-cluster-conf/7003/redis.conf
/usr/local/redis-5.0.4/redis-cluster-conf/7004/redis.conf
/usr/local/redis-5.0.4/redis-cluster-conf/7005/redis.conf
/usr/local/redis-5.0.4/redis-cluster-conf/7006/redis.conf

配置文件內(nèi)容:

port 7001  # 端口,每個配置文件不同7001-7006
cluster-enabled yes # 啟用集群模式
cluster-config-file nodes.conf #節(jié)點配置文件
cluster-node-timeout 5000 # 超時時間
appendonly yes # 打開aof持久化
daemonize yes # 后臺運行
protected-mode no # 非保護模式
pidfile  /var/run/redis_7001.pid # 根據(jù)端口修改

啟動6個Redis節(jié)點。

./src/redis-server redis-cluster-conf/7001/redis.conf
./src/redis-server redis-cluster-conf/7002/redis.conf
./src/redis-server redis-cluster-conf/7003/redis.conf
./src/redis-server redis-cluster-conf/7004/redis.conf
./src/redis-server redis-cluster-conf/7005/redis.conf
./src/redis-server redis-cluster-conf/7006/redis.conf

此時啟動的6個Redis服務(wù)是相互獨立運行的,通過以下命令配置集群。

./src/redis-cli --cluster create --cluster-replicas 1 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006

配置完看到如下圖所示信息表示集群搭建成功:

從圖中可以看到啟動了3個master節(jié)點,3個slave節(jié)點,16384個槽點平均分配到了3個master節(jié)點上。圖中很長的一串字母數(shù)字的組合(07000b3a90......)為節(jié)點的ID。后面對節(jié)點的操作中會用到。

集群重新分片

如果對默認的平均分配不滿意,我們可以對集群進行重新分片。執(zhí)行如下命令,只需要指定集群中的其中一個節(jié)點地址即可,它會自動找到集群中的其他節(jié)點。(如果設(shè)置了密碼則需要加上 -a <password>,沒有密碼則不需要,后面的命令我會省略這個,設(shè)置了密碼的自己加上就好)。

./src/redis-cli -a <password> --cluster reshard 127.0.0.1:7001

輸入你想重新分配的哈希槽數(shù)量

How many slots do you want to move (from 1 to 16384)?

輸入你想接收這些哈希槽的節(jié)點ID

What is the receiving node ID?

輸入想從哪個節(jié)點移動槽點,選擇all表示所有其他節(jié)點,也可以依次輸入節(jié)點ID,以done結(jié)束。

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.
Source node #1:

輸入yes執(zhí)行重新分片

Do you want to proceed with the proposed reshard plan (yes/no)?
自動故障轉(zhuǎn)移

當(dāng)運行中的master節(jié)點掛掉了,集群會在該master節(jié)點的slave節(jié)點中選出一個作為新的master節(jié)點。這里不做演示。

手動故障轉(zhuǎn)移

有的時候在主節(jié)點沒有任何問題的情況下強制手動故障轉(zhuǎn)移也是很有必要的,比如想要升級主節(jié)點的Redis進程,我們可以通過故障轉(zhuǎn)移將其轉(zhuǎn)為slave再進行升級操作來避免對集群的可用性造成很大的影響。

Redis集群使用 cluster failover 命令來進行故障轉(zhuǎn)移,不過要在被轉(zhuǎn)移的主節(jié)點的從節(jié)點上執(zhí)行該命令(使用redis-cli連接slave節(jié)點并執(zhí)行 cluster failover命令進行轉(zhuǎn)移)。

添加一個主節(jié)點

按之前的方式再復(fù)制一份配置文件,并修改配置

/usr/local/redis-5.0.4/redis-cluster-conf/7007/redis.conf

然后啟動該Redis服務(wù),執(zhí)行以下命令將該節(jié)點添加到集群中去

./src/redis-cli --cluster add-node 127.0.0.1:7007 127.0.0.1:7001

第一個參數(shù)為新增加的節(jié)點的IP和端口,第二個參數(shù)為任意一個已經(jīng)存在的節(jié)點的IP和端口。

此時該新節(jié)點已經(jīng)成為集群的一份子

127.0.0.1:7007> cluster nodes
90c78386c39c8258435b5b61f49a623b358ec8a6 127.0.0.1:7007@17007 myself,master - 0 1553239975877 10 connected
c4180e02149e2d853a80683433773ef4bceffc78 127.0.0.1:7001@17001 master - 0 1553240017595 11 connected 0-5544 10923-11004
b6584514edbf57331a65f367304f33ad1bd0903e 192.168.1.164:7005@17005 slave 3bdbc4ac0d5902dcf8a5ebbfb88db8fad224c066 0 1553240016992 2 connected
07000b3a905df0ab0c86361adcb2774a487ce650 192.168.1.164:7004@17004 slave c4180e02149e2d853a80683433773ef4bceffc78 0 1553240016000 11 connected
28fa7bbf6b2a46991c7a5fe8eec53db1a5f1e9f6 192.168.1.164:7003@17003 master - 0 1553240017595 3 connected 11005-16383
3bdbc4ac0d5902dcf8a5ebbfb88db8fad224c066 192.168.1.164:7002@17002 master - 0 1553240017000 2 connected 5545-10922
fd9cba359d94ba6c9beecc91fbd491f9cf7a39ca 192.168.1.164:7006@17006 slave 28fa7bbf6b2a46991c7a5fe8eec53db1a5f1e9f6 0 1553240016000 3 connected

但是該節(jié)點沒有包含任何的哈希槽,所以沒有數(shù)據(jù)會存到該主節(jié)點。我們可以通過上面的集群重新分片給該節(jié)點分配哈希槽,那么該節(jié)點就成為了一個真正的主節(jié)點了。

添加一個從節(jié)點

跟添加主節(jié)點一樣添加一個節(jié)點7008,然后連接上該節(jié)點并執(zhí)行如下命令

cluster replicate <nodeId>

這樣就可以指定該節(jié)點成為哪個節(jié)點的從節(jié)點。

節(jié)點的移除

可以使用如下命令來移除節(jié)點

./src/redis-cli --cluster del-node 127.0.0.1:7001 <nodeId>

第一個參數(shù)是任意一個節(jié)點的地址,第二個參數(shù)是你想要移除的節(jié)點ID。如果是移除主節(jié)點,需要確保這個節(jié)點是空的,如果不是空的則需要將這個節(jié)點上的數(shù)據(jù)重新分配到其他節(jié)點上。

程序中使用

SpringBoot中連接Redis集群配置

spring:
  redis:
    cluster:
      nodes: 192.168.1.164:7001,192.168.1.164:7002,192.168.1.164:7003,192.168.1.164:7004,192.168.1.164:7005,192.168.1.164:7006
    database: 0
    password: <password>

參考文檔

http://www.redis.cn/topics/cluster-tutorial.html

Redis常見數(shù)據(jù)丟失情況分析及解決

情況分析

(1)異步復(fù)制導(dǎo)致的數(shù)據(jù)丟失

因為master->slave的數(shù)據(jù)同步是異步的,所以可能存在部分數(shù)據(jù)還沒有同步到slave,master就宕機了,此時這部分數(shù)據(jù)就丟失了。

(2)腦裂導(dǎo)致的數(shù)據(jù)丟失

當(dāng)master所在的機器突然脫離的正常的網(wǎng)絡(luò),與其他slave、sentinel失去了連接,但是master還在運行著。此時sentinel就會認為master宕機了,會開始選舉把slave提升為新的master,這個時候集群中就會出現(xiàn)兩個master,也就是所謂的腦裂。

此時雖然產(chǎn)生了新的master節(jié)點,但是客戶端可能還沒來得及切換到新的master,會繼續(xù)向舊的master寫入數(shù)據(jù)。

當(dāng)網(wǎng)絡(luò)恢復(fù)正常時,舊的master會變成新的master的從節(jié)點,自己的數(shù)據(jù)會清空,重新從新的master上復(fù)制數(shù)據(jù)。

解決方案

Redis提供了這兩個配置用來降低數(shù)據(jù)丟失的可能性

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

上面兩行配置的意思是,要求至少有1個slave,數(shù)據(jù)復(fù)制和同步的延遲不能超過10秒,如果不符合這個條件,那么master將不會接收任何請求。

(1)減少異步復(fù)制的數(shù)據(jù)丟失

有了min-slaves-max-lag這個配置,就可以確保,一旦slave復(fù)制數(shù)據(jù)和ack延時太長,就認為master宕機后損失的數(shù)據(jù)太多了,那么就拒絕寫請求,這樣可以把master宕機時由于部分數(shù)據(jù)未同步到slave導(dǎo)致的數(shù)據(jù)丟失降低到可控范圍內(nèi)。

(2)減少腦裂的數(shù)據(jù)丟失

如果一個master出現(xiàn)了腦裂,跟其他slave丟了連接,那么上面兩個配置可以確保,如果不能繼續(xù)給指定數(shù)量的slave發(fā)送數(shù)據(jù),而且slave超過10秒沒有給自己ack消息,那么就直接拒絕客戶端的寫請求

這樣腦裂后的舊master就不會接受client的新數(shù)據(jù),也就避免了數(shù)據(jù)丟失。

Redis并不能保證數(shù)據(jù)的強一致性,看官方文檔的說明


參考文檔:

http://www.redis.cn/topics/cluster-tutorial.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容