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é)點(diǎn)(master)負(fù)責(zé)讀寫,從節(jié)點(diǎn)(slave)負(fù)責(zé)讀。這個(gè)系統(tǒng)的運(yùn)行依靠三個(gè)主要的機(jī)制:
- 當(dāng)一個(gè) master 實(shí)例和一個(gè) slave 實(shí)例連接正常時(shí)眷唉, master 會(huì)發(fā)送一連串的命令流來保持對 slave 的更新,以便于將自身數(shù)據(jù)集的改變復(fù)制給 slave ,包括客戶端的寫入妓局、key 的過期或被逐出等等。
- 當(dāng) master 和 slave 之間的連接斷開之后雳旅,因?yàn)榫W(wǎng)絡(luò)問題跟磨、或者是主從意識(shí)到連接超時(shí), slave 重新連接上 master 并會(huì)嘗試進(jìn)行部分重同步:這意味著它會(huì)嘗試只獲取在斷開連接期間內(nèi)丟失的命令流攒盈。
- 當(dāng)無法進(jìn)行部分重同步時(shí)抵拘, slave 會(huì)請求進(jìn)行全量重同步。這會(huì)涉及到一個(gè)更復(fù)雜的過程型豁,例如 master 需要?jiǎng)?chuàng)建所有數(shù)據(jù)的快照僵蛛,將之發(fā)送給 slave ,之后在數(shù)據(jù)集更改時(shí)持續(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ù)器端口號(hào)充尉,主從分別修改為7001 7002 7003
port 7001
# 使得Redis可以跨網(wǎng)絡(luò)訪問
bind 0.0.0.0
# 配置reids的密碼
requirepass "111111"
# 下面兩個(gè)配置只需要配置從節(jié)點(diǎn)(slave)
# 配置主服務(wù)器地址、端口號(hào)
replicaof 127.0.0.1 7001
# 主服務(wù)器密碼
masterauth "111111"
分別啟動(dòng)這三個(gè)Redis服務(wù)
$ ./src/redis-server redis.conf
$ ./src/redis-server slave.conf
$ ./src/redis-server slave2.conf
使用redis-cli工具連接redis服務(wù)查看主從節(jié)點(diǎn)是否搭建成功
$ ./src/redis-cli -h <主機(jī)名> -p <端口號(hào)>
$ auth <password>
$ info replication
看到類似如下所示信息則主從搭建成功
############主節(jié)點(diǎn)(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é)點(diǎn)(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
簡單的主從集群有個(gè)問題衣形,就是主節(jié)點(diǎn)掛了之后驼侠,無法從新選舉新的節(jié)點(diǎn)作為主節(jié)點(diǎn)進(jìn)行寫操作,導(dǎo)致服務(wù)不可用谆吴。所以接下來介紹Sentinel(哨兵)功能的使用倒源。哨兵是一個(gè)獨(dú)立的進(jìn)程,哨兵會(huì)實(shí)時(shí)監(jiān)控master節(jié)點(diǎn)的狀態(tài)句狼,當(dāng)master不可用時(shí)會(huì)從slave節(jié)點(diǎn)中選出一個(gè)作為新的master笋熬,并修改其他節(jié)點(diǎn)的配置指向到新的master。
該系統(tǒng)執(zhí)行以下三個(gè)任務(wù):
- 監(jiān)控(Monitoring):Sentinel 會(huì)不斷地檢查你的主服務(wù)器和從服務(wù)器是否運(yùn)作正常腻菇。
- 提醒(Notification):當(dāng)被監(jiān)控的某個(gè) Redis 服務(wù)器出現(xiàn)問題時(shí)胳螟, Sentinel 可以通過 API 向管理員或者其他應(yīng)用程序發(fā)送通知昔馋。
- 自動(dòng)故障遷移(Automatic failover): 當(dāng)一個(gè)主服務(wù)器不能正常工作時(shí), Sentinel 會(huì)開始一次自動(dòng)故障遷移操作糖耸, 它會(huì)將失效主服務(wù)器的其中一個(gè)從服務(wù)器升級(jí)為新的主服務(wù)器秘遏, 并讓失效主服務(wù)器的其他從服務(wù)器改為復(fù)制新的主服務(wù)器; 當(dāng)客戶端試圖連接失效的主服務(wù)器時(shí)蔬捷, 集群也會(huì)向客戶端返回新主服務(wù)器的地址垄提, 使得集群可以使用新主服務(wù)器代替失效服務(wù)器。
搭建步驟
新開一臺(tái)服務(wù)器周拐,并按上面的步驟下載安裝Redis铡俐。
將sentinel.conf文件復(fù)制兩份為sentinel2.conf、sentinel3.conf妥粟,并分別修改配置
# 三個(gè)配置文件分別配置不同的端口號(hào)
port <端口號(hào)>
# 設(shè)置為后臺(tái)啟動(dòng)
daemonize yes
# 主節(jié)點(diǎn)的名稱(可以自定義审丘,與后面的配置保持一致即可)
# 主機(jī)地址
# 端口號(hào)
# 數(shù)量(2代表只有兩個(gè)或兩個(gè)以上的哨兵認(rèn)為主服務(wù)器不可用的時(shí)候,才會(huì)進(jìn)行failover操作)
sentinel monitor mymaster 127.0.0.1 6379 2
# 多長時(shí)間沒有響應(yīng)認(rèn)為主觀下線(SDOWN)
sentinel down-after-milliseconds mymaster 60000
# 表示如果15秒后,mysater仍沒活過來勾给,則啟動(dòng)failover滩报,從剩下從節(jié)點(diǎn)序曲新的主節(jié)點(diǎn)
sentinel failover-timeout mymaster 15000
# 指定了在執(zhí)行故障轉(zhuǎn)移時(shí), 最多可以有多少個(gè)從服務(wù)器同時(shí)對新的主服務(wù)器進(jìn)行同步播急, 這個(gè)數(shù)字越小脓钾, 完成故障轉(zhuǎn)移所需的時(shí)間就越長
sentinel parallel-syncs mymaster 1
啟動(dòng)三個(gè)sentinel
$ ./src/server-sentinel sentinel.conf
$ ./src/server-sentinel sentinel2.conf
$ ./src/server-sentinel sentinel3.conf
然后手動(dòng)關(guān)閉主節(jié)點(diǎn)的redis服務(wù),并查看兩個(gè)slave信息是否有一個(gè)變成了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 集群是一個(gè)提供在多個(gè)Redis節(jié)點(diǎn)間共享數(shù)據(jù)的程序集可训。下圖以三個(gè)master節(jié)點(diǎn)和三個(gè)slave節(jié)點(diǎn)作為示例。Redis 集群有16384個(gè)哈希槽捶枢,每個(gè)key通過CRC16校驗(yàn)后對16384取模來決定放置哪個(gè)槽握截。集群的每個(gè)節(jié)點(diǎn)負(fù)責(zé)一部分hash槽,如圖中slots所示烂叔。
為了使在部分節(jié)點(diǎn)失敗或者大部分節(jié)點(diǎn)無法通信的情況下集群仍然可用谨胞,所以集群使用了主從復(fù)制模型,每個(gè)節(jié)點(diǎn)都會(huì)有1-n個(gè)從節(jié)點(diǎn)蒜鸡。例如master-A節(jié)點(diǎn)不可用了胯努,集群便會(huì)選舉slave-A節(jié)點(diǎn)作為新的主節(jié)點(diǎn)繼續(xù)服務(wù)。
搭建步驟
Redis5.0之后的版本放棄了 Ruby 的集群方式逢防,改為使用 C 語言編寫的redis-cli的方式康聂,使集群的構(gòu)建方式復(fù)雜度大大降低。
下載安裝Redis(見主從復(fù)制模式的搭建步驟)胞四。
創(chuàng)建6個(gè)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 # 端口伶椿,每個(gè)配置文件不同7001-7006
cluster-enabled yes # 啟用集群模式
cluster-config-file nodes.conf #節(jié)點(diǎn)配置文件
cluster-node-timeout 5000 # 超時(shí)時(shí)間
appendonly yes # 打開aof持久化
daemonize yes # 后臺(tái)運(yùn)行
protected-mode no # 非保護(hù)模式
pidfile /var/run/redis_7001.pid # 根據(jù)端口修改
啟動(dòng)6個(gè)Redis節(jié)點(diǎn)辜伟。
./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
此時(shí)啟動(dòng)的6個(gè)Redis服務(wù)是相互獨(dú)立運(yùn)行的氓侧,通過以下命令配置集群。
./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
配置完看到如下圖所示信息表示集群搭建成功:
從圖中可以看到啟動(dòng)了3個(gè)master節(jié)點(diǎn)导狡,3個(gè)slave節(jié)點(diǎn)约巷,16384個(gè)槽點(diǎn)平均分配到了3個(gè)master節(jié)點(diǎn)上。圖中很長的一串字母數(shù)字的組合(07000b3a90......)為節(jié)點(diǎn)的ID旱捧。后面對節(jié)點(diǎn)的操作中會(huì)用到独郎。
集群重新分片
如果對默認(rèn)的平均分配不滿意,我們可以對集群進(jìn)行重新分片枚赡。執(zhí)行如下命令氓癌,只需要指定集群中的其中一個(gè)節(jié)點(diǎn)地址即可,它會(huì)自動(dòng)找到集群中的其他節(jié)點(diǎn)贫橙。(如果設(shè)置了密碼則需要加上 -a <password>贪婉,沒有密碼則不需要,后面的命令我會(huì)省略這個(gè)卢肃,設(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é)點(diǎn)ID
What is the receiving node ID?
輸入想從哪個(gè)節(jié)點(diǎn)移動(dòng)槽點(diǎn),選擇all表示所有其他節(jié)點(diǎn)莫湘,也可以依次輸入節(jié)點(diǎn)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)?
自動(dòng)故障轉(zhuǎn)移
當(dāng)運(yùn)行中的master節(jié)點(diǎn)掛掉了幅垮,集群會(huì)在該master節(jié)點(diǎn)的slave節(jié)點(diǎn)中選出一個(gè)作為新的master節(jié)點(diǎn)腰池。這里不做演示。
手動(dòng)故障轉(zhuǎn)移
有的時(shí)候在主節(jié)點(diǎn)沒有任何問題的情況下強(qiáng)制手動(dòng)故障轉(zhuǎn)移也是很有必要的军洼,比如想要升級(jí)主節(jié)點(diǎn)的Redis進(jìn)程巩螃,我們可以通過故障轉(zhuǎn)移將其轉(zhuǎn)為slave再進(jìn)行升級(jí)操作來避免對集群的可用性造成很大的影響。
Redis集群使用 cluster failover 命令來進(jìn)行故障轉(zhuǎn)移匕争,不過要在被轉(zhuǎn)移的主節(jié)點(diǎn)的從節(jié)點(diǎn)上執(zhí)行該命令(使用redis-cli連接slave節(jié)點(diǎn)并執(zhí)行 cluster failover命令進(jìn)行轉(zhuǎn)移)避乏。
添加一個(gè)主節(jié)點(diǎn)
按之前的方式再復(fù)制一份配置文件,并修改配置
/usr/local/redis-5.0.4/redis-cluster-conf/7007/redis.conf
然后啟動(dòng)該Redis服務(wù)甘桑,執(zhí)行以下命令將該節(jié)點(diǎn)添加到集群中去
./src/redis-cli --cluster add-node 127.0.0.1:7007 127.0.0.1:7001
第一個(gè)參數(shù)為新增加的節(jié)點(diǎn)的IP和端口拍皮,第二個(gè)參數(shù)為任意一個(gè)已經(jīng)存在的節(jié)點(diǎn)的IP和端口。
此時(shí)該新節(jié)點(diǎn)已經(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é)點(diǎn)沒有包含任何的哈希槽跑杭,所以沒有數(shù)據(jù)會(huì)存到該主節(jié)點(diǎn)铆帽。我們可以通過上面的集群重新分片給該節(jié)點(diǎn)分配哈希槽,那么該節(jié)點(diǎn)就成為了一個(gè)真正的主節(jié)點(diǎn)了德谅。
添加一個(gè)從節(jié)點(diǎn)
跟添加主節(jié)點(diǎn)一樣添加一個(gè)節(jié)點(diǎn)7008爹橱,然后連接上該節(jié)點(diǎn)并執(zhí)行如下命令
cluster replicate <nodeId>
這樣就可以指定該節(jié)點(diǎn)成為哪個(gè)節(jié)點(diǎn)的從節(jié)點(diǎn)。
節(jié)點(diǎn)的移除
可以使用如下命令來移除節(jié)點(diǎn)
./src/redis-cli --cluster del-node 127.0.0.1:7001 <nodeId>
第一個(gè)參數(shù)是任意一個(gè)節(jié)點(diǎn)的地址窄做,第二個(gè)參數(shù)是你想要移除的節(jié)點(diǎn)ID愧驱。如果是移除主節(jié)點(diǎn)慰技,需要確保這個(gè)節(jié)點(diǎn)是空的,如果不是空的則需要將這個(gè)節(jié)點(diǎn)上的數(shù)據(jù)重新分配到其他節(jié)點(diǎn)上组砚。
程序中使用
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ù)丟失
因?yàn)閙aster->slave的數(shù)據(jù)同步是異步的吻商,所以可能存在部分?jǐn)?shù)據(jù)還沒有同步到slave,master就宕機(jī)了糟红,此時(shí)這部分?jǐn)?shù)據(jù)就丟失了艾帐。
(2)腦裂導(dǎo)致的數(shù)據(jù)丟失
當(dāng)master所在的機(jī)器突然脫離的正常的網(wǎng)絡(luò),與其他slave盆偿、sentinel失去了連接柒爸,但是master還在運(yùn)行著。此時(shí)sentinel就會(huì)認(rèn)為master宕機(jī)了陈肛,會(huì)開始選舉把slave提升為新的master揍鸟,這個(gè)時(shí)候集群中就會(huì)出現(xiàn)兩個(gè)master,也就是所謂的腦裂句旱。
此時(shí)雖然產(chǎn)生了新的master節(jié)點(diǎn)阳藻,但是客戶端可能還沒來得及切換到新的master,會(huì)繼續(xù)向舊的master寫入數(shù)據(jù)谈撒。
當(dāng)網(wǎng)絡(luò)恢復(fù)正常時(shí)腥泥,舊的master會(huì)變成新的master的從節(jié)點(diǎn),自己的數(shù)據(jù)會(huì)清空啃匿,重新從新的master上復(fù)制數(shù)據(jù)蛔外。
解決方案
Redis提供了這兩個(gè)配置用來降低數(shù)據(jù)丟失的可能性
min-slaves-to-write 1
min-slaves-max-lag 10
上面兩行配置的意思是,要求至少有1個(gè)slave溯乒,數(shù)據(jù)復(fù)制和同步的延遲不能超過10秒夹厌,如果不符合這個(gè)條件,那么master將不會(huì)接收任何請求裆悄。
(1)減少異步復(fù)制的數(shù)據(jù)丟失
有了min-slaves-max-lag這個(gè)配置矛纹,就可以確保,一旦slave復(fù)制數(shù)據(jù)和ack延時(shí)太長光稼,就認(rèn)為master宕機(jī)后損失的數(shù)據(jù)太多了或南,那么就拒絕寫請求,這樣可以把master宕機(jī)時(shí)由于部分?jǐn)?shù)據(jù)未同步到slave導(dǎo)致的數(shù)據(jù)丟失降低到可控范圍內(nèi)艾君。
(2)減少腦裂的數(shù)據(jù)丟失
如果一個(gè)master出現(xiàn)了腦裂采够,跟其他slave丟了連接,那么上面兩個(gè)配置可以確保冰垄,如果不能繼續(xù)給指定數(shù)量的slave發(fā)送數(shù)據(jù)蹬癌,而且slave超過10秒沒有給自己ack消息,那么就直接拒絕客戶端的寫請求
這樣腦裂后的舊master就不會(huì)接受client的新數(shù)據(jù),也就避免了數(shù)據(jù)丟失逝薪。
Redis并不能保證數(shù)據(jù)的強(qiáng)一致性伴奥,看官方文檔的說明
參考文檔: