日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

Redis集群入门实践教程

發(fā)布時間:2023/12/14 数据库 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Redis集群入门实践教程 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、Redis 集群概述

Redis 主從復(fù)制

到目前為止,我們所學(xué)習(xí)的 Redis 都是單機(jī)版的,這也就意味著一旦我們所依賴的 Redis 服務(wù)宕機(jī)了,我們的主流程也會受到一定的影響。
所以一開始我們的想法是:搞一臺備用機(jī)。這樣我們就可以在一臺服務(wù)器出現(xiàn)問題的時候切換動態(tài)地到另一臺去:

幸運(yùn)的是,兩個節(jié)點(diǎn)數(shù)據(jù)的同步我們可以使用 Redis 的 主從同步 功能幫助到我們。

后來因為某種神秘力量,Redis 老會在莫名其妙的時間點(diǎn)出問題 (比如半夜 2 點(diǎn)),我總不能 24 小時時刻守在電腦旁邊切換節(jié)點(diǎn)吧,于是另一個想法又開始了:給所有的節(jié)點(diǎn)找一個 “管家”,自動幫我監(jiān)聽照顧節(jié)點(diǎn)的狀態(tài)并切換:


這大概就是 Redis 哨兵 (Sentinel) 的簡單理解啦。什么?管家宕機(jī)了怎么辦?相較于有大量請求的 Redis 服務(wù)來說,管家宕機(jī)的概率就要小得多啦… 如果真的宕機(jī)了,我們也可以直接切換成當(dāng)前可用的節(jié)點(diǎn)保證可用…

Redis 集群化

但單臺節(jié)點(diǎn)的計算能力始終有限,所謂人多力量大,如果我們把 多個節(jié)點(diǎn)組合 成 一個可用的工作節(jié)點(diǎn),那就大大增加了 Redis 的 高可用、可擴(kuò)展、分布式、容錯 等特性:

二、主從復(fù)制


**主從復(fù)制,是指將一臺 Redis 服務(wù)器的數(shù)據(jù),復(fù)制到其他的 Redis 服務(wù)器。前者稱為 主節(jié)點(diǎn)(master),后者稱為 從節(jié)點(diǎn)(slave)。**且數(shù)據(jù)的復(fù)制是 單向 的,只能由主節(jié)點(diǎn)到從節(jié)點(diǎn)。Redis 主從復(fù)制支持 主從同步 和 從從同步 兩種,后者是 Redis 后續(xù)版本新增的功能,以減輕主節(jié)點(diǎn)的同步負(fù)擔(dān)。

主從復(fù)制主要的作用

?數(shù)據(jù)冗余: 主從復(fù)制實現(xiàn)了數(shù)據(jù)的熱備份,是持久化之外的一種數(shù)據(jù)冗余方式。
?故障恢復(fù): 當(dāng)主節(jié)點(diǎn)出現(xiàn)問題時,可以由從節(jié)點(diǎn)提供服務(wù),實現(xiàn)快速的故障恢復(fù) (實際上是一種服務(wù)的冗余)。
?負(fù)載均衡: 在主從復(fù)制的基礎(chǔ)上,配合讀寫分離,可以由主節(jié)點(diǎn)提供寫服務(wù),由從節(jié)點(diǎn)提供讀服務(wù) (即寫 Redis 數(shù)據(jù)時應(yīng)用連接主節(jié)點(diǎn),讀 Redis 數(shù)據(jù)時應(yīng)用連接從節(jié)點(diǎn)),分擔(dān)服務(wù)器負(fù)載。尤其是在寫少讀多的場景下,通過多個從節(jié)點(diǎn)分擔(dān)讀負(fù)載,可以大大提高 Redis 服務(wù)器的并發(fā)量。
?高可用基石: 除了上述作用以外,主從復(fù)制還是哨兵和集群能夠?qū)嵤┑?基礎(chǔ),因此說主從復(fù)制是 Redis 高可用的基礎(chǔ)。

快速體驗

在 Redis 中,用戶可以通過執(zhí)行 SLAVEOF 命令或者設(shè)置 slaveof 選項,讓一個服務(wù)器去復(fù)制另一個服務(wù)器,以下三種方式是 完全等效 的:

**配置文件:**在從服務(wù)器的配置文件中加入:slaveof
**啟動命令:**redis-server 啟動命令后加入 --slaveof
**客戶端命令:**Redis 服務(wù)器啟動后,直接通過客戶端執(zhí)行命令:slaveof ,讓該 Redis 實例成為從節(jié)點(diǎn)。
需要注意的是:主從復(fù)制的開啟,完全是在從節(jié)點(diǎn)發(fā)起的,不需要我們在主節(jié)點(diǎn)做任何事情

第一步:本地啟動兩個節(jié)點(diǎn)
在正確安裝好 Redis 之后,我們可以使用 redis-server --port 的方式指定創(chuàng)建兩個不同端口的 Redis 實例,例如,下方我分別創(chuàng)建了一個 6379 和 6380 的兩個 Redis 實例:

# 創(chuàng)建一個端口為 6379 的 Redis 實例 redis-server --port 6379 # 創(chuàng)建一個端口為 6380 的 Redis 實例 redis-server --port 6380

此時兩個 Redis 節(jié)點(diǎn)啟動后,都默認(rèn)為 主節(jié)點(diǎn)

第二步:建立復(fù)制
我們在 6380 端口的節(jié)點(diǎn)中執(zhí)行 slaveof 命令,使之變?yōu)閺墓?jié)點(diǎn):

# 在 6380 端口的 Redis 實例中使用控制臺 redis-cli -p 6380 # 成為本地 6379 端口實例的從節(jié)點(diǎn) 127.0.0.1:6380> SLAVEOF 127.0.0.1? 6379 OK

第三步:觀察效果
下面我們來驗證一下,主節(jié)點(diǎn)的數(shù)據(jù)是否會復(fù)制到從節(jié)點(diǎn)之中:

先在 從節(jié)點(diǎn) 中查詢一個 不存在 的 key: 127.0.0.1:6380> GET mykey (nil) 再在 主節(jié)點(diǎn) 中添加這個 key: 127.0.0.1:6379> SET mykey myvalue OK 此時再從 從節(jié)點(diǎn) 中查詢,會發(fā)現(xiàn)已經(jīng)從 主節(jié)點(diǎn) 同步到 從節(jié)點(diǎn): 127.0.0.1:6380> GET mykey "myvalue"

第四步:斷開復(fù)制
通過 slaveof 命令建立主從復(fù)制關(guān)系以后,可以通過 slaveof no one 斷開。需要注意的是,從節(jié)點(diǎn)斷開復(fù)制后,不會刪除已有的數(shù)據(jù),只是不再接受主節(jié)點(diǎn)新的數(shù)據(jù)變化。

從節(jié)點(diǎn)執(zhí)行 slaveof no one 之后,從節(jié)點(diǎn)和主節(jié)點(diǎn)分別打印日志如下:、

# 從節(jié)點(diǎn)打印日志 61496:M 17 Mar 2020 08:10:22.749 # Connection with master lost. 61496:M 17 Mar 2020 08:10:22.749 * Caching the disconnected master state. 61496:M 17 Mar 2020 08:10:22.749 * Discarding previously cached master state. 61496:M 17 Mar 2020 08:10:22.749 * MASTER MODE enabled (user request from 'id=4 addr=127.0.0.1:55096 fd=8 name= age=1664 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=34 qbuf-free=32734 obl=0 oll=0 omem=0 events=r cmd=slaveof')# 主節(jié)點(diǎn)打印日志 61467:M 17 Mar 2020 08:10:22.749 # Connection with replica 127.0.0.1:6380 lost.

實現(xiàn)原理簡析

可以 簡化成三個階段:準(zhǔn)備階段-數(shù)據(jù)同步階段-命令傳播階段。下面我們來進(jìn)行一些必要的說明。

身份驗證 | 主從復(fù)制安全問題
在上面的 快速體驗 過程中,你會發(fā)現(xiàn) slaveof 這個命令居然不需要驗證?這意味著只要知道了 ip 和端口就可以隨意拷貝服務(wù)器上的數(shù)據(jù)了?

那當(dāng)然不能夠了,我們可以通過在 主節(jié)點(diǎn) 配置 requirepass 來設(shè)置密碼,這樣就必須在 從節(jié)點(diǎn) 中對應(yīng)配置好 masterauth 參數(shù) (與主節(jié)點(diǎn) requirepass 保持一致) 才能夠進(jìn)行正常復(fù)制了。

SYNC 命令是一個非常耗費(fèi)資源的操作
每次執(zhí)行 SYNC 命令,主從服務(wù)器需要執(zhí)行如下動作:

主服務(wù)器 需要執(zhí)行 BGSAVE 命令來生成 RDB 文件,這個生成操作會 消耗 主服務(wù)器大量的 CPU、內(nèi)存和磁盤 I/O 的資源;

主服務(wù)器 需要將自己生成的 RDB 文件 發(fā)送給從服務(wù)器,這個發(fā)送操作會 消耗 主服務(wù)器 大量的網(wǎng)絡(luò)資源 (帶寬和流量),并對主服務(wù)器響應(yīng)命令請求的時間產(chǎn)生影響;

接收到 RDB 文件的 從服務(wù)器 需要載入主服務(wù)器發(fā)來的 RBD 文件,并且在載入期間,從服務(wù)器 會因為阻塞而沒辦法處理命令請求;
特別是當(dāng)出現(xiàn) 斷線重復(fù)制 的情況是時,為了讓從服務(wù)器補(bǔ)足斷線時確實的那一小部分?jǐn)?shù)據(jù),卻要執(zhí)行一次如此耗資源的 SYNC 命令,顯然是不合理的。

PSYNC 命令的引入
所以在 Redis 2.8 中引入了 PSYNC 命令來代替 SYNC,它具有兩種模式:

全量復(fù)制: 用于初次復(fù)制或其他無法進(jìn)行部分復(fù)制的情況,將主節(jié)點(diǎn)中的所有數(shù)據(jù)都發(fā)送給從節(jié)點(diǎn),是一個非常重型的操作;
部分復(fù)制: 用于網(wǎng)絡(luò)中斷等情況后的復(fù)制,只將 中斷期間主節(jié)點(diǎn)執(zhí)行的寫命令 發(fā)送給從節(jié)點(diǎn),與全量復(fù)制相比更加高效。需要注意 的是,如果網(wǎng)絡(luò)中斷時間過長,導(dǎo)致主節(jié)點(diǎn)沒有能夠完整地保存中斷期間執(zhí)行的寫命令,則無法進(jìn)行部分復(fù)制,仍使用全量復(fù)制;
部分復(fù)制的原理主要是靠主從節(jié)點(diǎn)分別維護(hù)一個 復(fù)制偏移量,有了這個偏移量之后斷線重連之后一比較,之后就可以僅僅把從服務(wù)器斷線之后確實的這部分?jǐn)?shù)據(jù)給補(bǔ)回來了。

三、Redis Sentinel 哨兵

上圖 展示了一個典型的哨兵架構(gòu)圖,它由兩部分組成,哨兵節(jié)點(diǎn)和數(shù)據(jù)節(jié)點(diǎn):

哨兵節(jié)點(diǎn): 哨兵系統(tǒng)由一個或多個哨兵節(jié)點(diǎn)組成,哨兵節(jié)點(diǎn)是特殊的 Redis 節(jié)點(diǎn),不存儲數(shù)據(jù);
數(shù)據(jù)節(jié)點(diǎn): 主節(jié)點(diǎn)和從節(jié)點(diǎn)都是數(shù)據(jù)節(jié)點(diǎn);
在復(fù)制的基礎(chǔ)上,哨兵實現(xiàn)了 自動化的故障恢復(fù) 功能:

監(jiān)控(Monitoring): 哨兵會不斷地檢查主節(jié)點(diǎn)和從節(jié)點(diǎn)是否運(yùn)作正常。
自動故障轉(zhuǎn)移(Automatic failover): 當(dāng) 主節(jié)點(diǎn) 不能正常工作時,哨兵會開始 自動故障轉(zhuǎn)移操作,它會將失效主節(jié)點(diǎn)的其中一個 從節(jié)點(diǎn)升級為新的主節(jié)點(diǎn),并讓其他從節(jié)點(diǎn)改為復(fù)制新的主節(jié)點(diǎn)。
配置提供者(Configuration provider): 客戶端在初始化時,通過連接哨兵來獲得當(dāng)前 Redis 服務(wù)的主節(jié)點(diǎn)地址。
通知(Notification): 哨兵可以將故障轉(zhuǎn)移的結(jié)果發(fā)送給客戶端。
其中,監(jiān)控和自動故障轉(zhuǎn)移功能,使得哨兵可以及時發(fā)現(xiàn)主節(jié)點(diǎn)故障并完成轉(zhuǎn)移。而配置提供者和通知功能,則需要在與客戶端的交互中才能體現(xiàn)。

快速體驗

第一步:創(chuàng)建主從節(jié)點(diǎn)配置文件并啟動
正確安裝好 Redis 之后,我們?nèi)サ?Redis 的安裝目錄 (mac 默認(rèn)在 /usr/local/),找到 redis.conf 文件復(fù)制三份分別命名為 redis-master.conf/redis-slave1.conf/redis-slave2.conf,分別作為 1 個主節(jié)點(diǎn)和 2 個從節(jié)點(diǎn)的配置文件 (下圖演示了我本機(jī)的 redis.conf 文件的位置)

打開可以看到這個 .conf 后綴的文件里面有很多說明的內(nèi)容,全部刪除然后分別改成下面的樣子:

#redis-master.conf port 6379 daemonize yes logfile "6379.log" dbfilename "dump-6379.rdb"#redis-slave1.conf port 6380 daemonize yes logfile "6380.log" dbfilename "dump-6380.rdb" slaveof 127.0.0.1 6379#redis-slave2.conf port 6381 daemonize yes logfile "6381.log" dbfilename "dump-6381.rdb" slaveof 127.0.0.1 6379

然后我們可以執(zhí)行 redis-server 來根據(jù)配置文件啟動不同的 Redis 實例,依次啟動主從節(jié)點(diǎn):

redis-server /usr/local/redis-5.0.3/redis-master.conf redis-server /usr/local/redis-5.0.3/redis-slave1.conf redis-server /usr/local/redis-5.0.3/redis-slave2.conf 節(jié)點(diǎn)啟動后,我們執(zhí)行 redis-cli 默認(rèn)連接到我們端口為 6379 的主節(jié)點(diǎn)執(zhí)行 info Replication 檢查一下主從狀態(tài)是否正常:(可以看到下方正確地顯示了兩個從節(jié)點(diǎn))

第二步:創(chuàng)建哨兵節(jié)點(diǎn)配置文件并啟動
按照上面同樣的方法,我們給哨兵節(jié)點(diǎn)也創(chuàng)建三個配置文件。(哨兵節(jié)點(diǎn)本質(zhì)上是特殊的 Redis 節(jié)點(diǎn),所以配置幾乎沒什么差別,只是在端口上做區(qū)分就好)

# redis-sentinel-1.conf port 26379 daemonize yes logfile "26379.log" sentinel monitor mymaster 127.0.0.1 6379 2# redis-sentinel-2.conf port 26380 daemonize yes logfile "26380.log" sentinel monitor mymaster 127.0.0.1 6379 2# redis-sentinel-3.conf port 26381 daemonize yes logfile "26381.log" sentinel monitor mymaster 127.0.0.1 6379 2

其中,sentinel monitor mymaster 127.0.0.1 6379 2 配置的含義是:該哨兵節(jié)點(diǎn)監(jiān)控 127.0.0.1:6379 這個主節(jié)點(diǎn),該主節(jié)點(diǎn)的名稱是 mymaster,最后的 2 的含義與主節(jié)點(diǎn)的故障判定有關(guān):至少需要 2 個哨兵節(jié)點(diǎn)同意,才能判定主節(jié)點(diǎn)故障并進(jìn)行故障轉(zhuǎn)移。

執(zhí)行下方命令將哨兵節(jié)點(diǎn)啟動起來:

redis-server /usr/local/redis-5.0.3/redis-sentinel-1.conf --sentinel redis-server /usr/local/redis-5.0.3/redis-sentinel-2.conf --sentinel redis-server /usr/local/redis-5.0.3/redis-sentinel-3.conf --sentinel

使用 redis-cil 工具連接哨兵節(jié)點(diǎn),并執(zhí)行 info Sentinel 命令來查看是否已經(jīng)在監(jiān)視主節(jié)點(diǎn)了:

# 連接端口為 26379 的 Redis 節(jié)點(diǎn) ? ~ redis-cli -p 26379 127.0.0.1:26379> info Sentinel # Sentinel sentinel_masters:1 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 sentinel_simulate_failure_flags:0 master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=3

此時你打開剛才寫好的哨兵配置文件,你還會發(fā)現(xiàn)出現(xiàn)了一些變化:

第三步:演示故障轉(zhuǎn)移
首先,我們使用 kill -9 命令來殺掉主節(jié)點(diǎn),同時 在哨兵節(jié)點(diǎn)中執(zhí)行 info Sentinel 命令來觀察故障節(jié)點(diǎn)的過程:

? ~ ps aux | grep 6379 longtao 74529 0.3 0.0 4346936 2132 ?? Ss 10:30上午 0:03.09 redis-server *:26379 [sentinel] longtao 73541 0.2 0.0 4348072 2292 ?? Ss 10:18上午 0:04.79 redis-server *:6379 longtao 75521 0.0 0.0 4286728 728 s008 S+ 10:39上午 0:00.00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn 6379 longtao 74836 0.0 0.0 4289844 944 s006 S+ 10:32上午 0:00.01 redis-cli -p 26379 ? ~ kill -9 73541

如果 剛殺掉瞬間 在哨兵節(jié)點(diǎn)中執(zhí)行 info 命令來查看,會發(fā)現(xiàn)主節(jié)點(diǎn)還沒有切換過來,因為哨兵發(fā)現(xiàn)主節(jié)點(diǎn)故障并轉(zhuǎn)移需要一段時間:

# 第一時間查看哨兵節(jié)點(diǎn)發(fā)現(xiàn)并未轉(zhuǎn)移,還在 6379 端口 127.0.0.1:26379> info Sentinel # Sentinel sentinel_masters:1 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 sentinel_simulate_failure_flags:0 master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=3

一段時間之后你再執(zhí)行 info 命令,查看,你就會發(fā)現(xiàn)主節(jié)點(diǎn)已經(jīng)切換成了 6381 端口的從節(jié)點(diǎn):

# 過一段時間之后在執(zhí)行,發(fā)現(xiàn)已經(jīng)切換了 6381 端口 127.0.0.1:26379> info Sentinel # Sentinel sentinel_masters:1 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 sentinel_simulate_failure_flags:0 master0:name=mymaster,status=ok,address=127.0.0.1:6381,slaves=2,sentinels=3

但同時還可以發(fā)現(xiàn),**哨兵節(jié)點(diǎn)認(rèn)為新的主節(jié)點(diǎn)仍然有兩個從節(jié)點(diǎn) (上方 slaves=2),**這是因為哨兵在將 6381 切換成主節(jié)點(diǎn)的同時,將 6379 節(jié)點(diǎn)置為其從節(jié)點(diǎn)。雖然 6379 從節(jié)點(diǎn)已經(jīng)掛掉,但是由于 哨兵并不會對從節(jié)點(diǎn)進(jìn)行客觀下線,因此認(rèn)為該從節(jié)點(diǎn)一直存在。當(dāng) 6379 節(jié)點(diǎn)重新啟動后,會自動變成 6381 節(jié)點(diǎn)的從節(jié)點(diǎn)。

另外,在故障轉(zhuǎn)移的階段,哨兵和主從節(jié)點(diǎn)的配置文件都會被改寫:

對于主從節(jié)點(diǎn): 主要是 slaveof 配置的變化,新的主節(jié)點(diǎn)沒有了 slaveof 配置,其從節(jié)點(diǎn)則 slaveof 新的主節(jié)點(diǎn)。
對于哨兵節(jié)點(diǎn): 除了主從節(jié)點(diǎn)信息的變化,紀(jì)元(epoch) (記錄當(dāng)前集群狀態(tài)的參數(shù)) 也會變化,紀(jì)元相關(guān)的參數(shù)都 +1 了。

客戶端訪問哨兵系統(tǒng)代碼演示

上面我們在 快速體驗 中主要感受到了服務(wù)端自己對于當(dāng)前主從節(jié)點(diǎn)的自動化治理,下面我們以 Java 代碼為例,來演示一下客戶端如何訪問我們的哨兵系統(tǒng):

public static void testSentinel() throws Exception {String masterName = "mymaster";Set<String> sentinels = new HashSet<>();sentinels.add("127.0.0.1:26379");sentinels.add("127.0.0.1:26380");sentinels.add("127.0.0.1:26381");// 初始化過程做了很多工作JedisSentinelPool pool = new JedisSentinelPool(masterName, sentinels); Jedis jedis = pool.getResource();jedis.set("key1", "value1");pool.close(); }

客戶端原理

Jedis 客戶端對哨兵提供了很好的支持。如上述代碼所示,我們只需要向 Jedis 提供哨兵節(jié)點(diǎn)集合和 masterName ,構(gòu)造 JedisSentinelPool 對象,然后便可以像使用普通 Redis 連接池一樣來使用了:通過 pool.getResource() 獲取連接,執(zhí)行具體的命令。

在整個過程中,我們的代碼不需要顯式的指定主節(jié)點(diǎn)的地址,就可以連接到主節(jié)點(diǎn);代碼中對故障轉(zhuǎn)移沒有任何體現(xiàn),就可以在哨兵完成故障轉(zhuǎn)移后自動的切換主節(jié)點(diǎn)。之所以可以做到這一點(diǎn),是因為在 JedisSentinelPool 的構(gòu)造器中,進(jìn)行了相關(guān)的工作;主要包括以下兩點(diǎn):

遍歷哨兵節(jié)點(diǎn),獲取主節(jié)點(diǎn)信息: 遍歷哨兵節(jié)點(diǎn),通過其中一個哨兵節(jié)點(diǎn) + masterName 獲得主節(jié)點(diǎn)的信息;該功能是通過調(diào)用哨兵節(jié)點(diǎn)的 sentinel get-master-addr-by-name 命令實現(xiàn);
增加對哨兵的監(jiān)聽: 這樣當(dāng)發(fā)生故障轉(zhuǎn)移時,客戶端便可以收到哨兵的通知,從而完成主節(jié)點(diǎn)的切換。具體做法是:利用 Redis 提供的 發(fā)布訂閱 功能,為每一個哨兵節(jié)點(diǎn)開啟一個單獨(dú)的線程,訂閱哨兵節(jié)點(diǎn)的 + switch-master 頻道,當(dāng)收到消息時,重新初始化連接池。

新的主服務(wù)器是怎樣被挑選出來的?

故障轉(zhuǎn)移操作的第一步 要做的就是在已下線主服務(wù)器屬下的所有從服務(wù)器中,挑選出一個狀態(tài)良好、數(shù)據(jù)完整的從服務(wù)器,然后向這個從服務(wù)器發(fā)送 slaveof no one 命令,將這個從服務(wù)器轉(zhuǎn)換為主服務(wù)器。但是這個從服務(wù)器是怎么樣被挑選出來的呢?

簡單來說 Sentinel 使用以下規(guī)則來選擇新的主服務(wù)器:

在失效主服務(wù)器屬下的從服務(wù)器當(dāng)中, 那些被標(biāo)記為主觀下線、已斷線、或者最后一次回復(fù) PING 命令的時間大于五秒鐘的從服務(wù)器都會被 淘汰

在失效主服務(wù)器屬下的從服務(wù)器當(dāng)中, 那些與失效主服務(wù)器連接斷開的時長超過 down-after 選項指定的時長十倍的從服務(wù)器都會被 淘汰。

在 經(jīng)歷了以上兩輪淘汰之后 剩下來的從服務(wù)器中, 我們選出 復(fù)制偏移量(replication offset)最大 的那個 從服務(wù)器 作為新的主服務(wù)器;如果復(fù)制偏移量不可用,或者從服務(wù)器的復(fù)制偏移量相同,那么 帶有最小運(yùn)行 ID 的那個從服務(wù)器成為新的主服務(wù)器。

四、Redis 集群

上圖 展示了 Redis Cluster 典型的架構(gòu)圖,集群中的每一個 Redis 節(jié)點(diǎn)都 互相兩兩相連,客戶端任意 直連 到集群中的 任意一臺,就可以對其他 Redis 節(jié)點(diǎn)進(jìn)行 讀寫 的操作。

基本原理

Redis 集群中內(nèi)置了 16384 個哈希槽。當(dāng)客戶端連接到 Redis 集群之后,會同時得到一份關(guān)于這個 集群的配置信息,當(dāng)客戶端具體對某一個 key 值進(jìn)行操作時,會計算出它的一個 Hash 值,然后把結(jié)果對 16384 求余數(shù),這樣每個 key 都會對應(yīng)一個編號在 0-16383 之間的哈希槽,Redis 會根據(jù)節(jié)點(diǎn)數(shù)量 大致均等 的將哈希槽映射到不同的節(jié)點(diǎn)。

再結(jié)合集群的配置信息就能夠知道這個 key 值應(yīng)該存儲在哪一個具體的 Redis 節(jié)點(diǎn)中,如果不屬于自己管,那么就會使用一個特殊的 MOVED 命令來進(jìn)行一個跳轉(zhuǎn),告訴客戶端去連接這個節(jié)點(diǎn)以獲取數(shù)據(jù):

GET x -MOVED 3999 127.0.0.1:6381

MOVED 指令第一個參數(shù) 3999 是 key 對應(yīng)的槽位編號,后面是目標(biāo)節(jié)點(diǎn)地址,MOVED 命令前面有一個減號,表示這是一個錯誤的消息。客戶端在收到 MOVED 指令后,就立即糾正本地的 槽位映射表,那么下一次再訪問 key 時就能夠到正確的地方去獲取了。

集群的主要作用

數(shù)據(jù)分區(qū): 數(shù)據(jù)分區(qū) (或稱數(shù)據(jù)分片) 是集群最核心的功能。集群將數(shù)據(jù)分散到多個節(jié)點(diǎn),一方面 突破了 Redis 單機(jī)內(nèi)存大小的限制,存儲容量大大增加;另一方面 每個主節(jié)點(diǎn)都可以對外提供讀服務(wù)和寫服務(wù),大大提高了集群的響應(yīng)能力。Redis 單機(jī)內(nèi)存大小受限問題,在介紹持久化和主從復(fù)制時都有提及,例如,如果單機(jī)內(nèi)存太大,bgsave 和 bgrewriteaof 的 fork 操作可能導(dǎo)致主進(jìn)程阻塞,主從環(huán)境下主機(jī)切換時可能導(dǎo)致從節(jié)點(diǎn)長時間無法提供服務(wù),全量復(fù)制階段主節(jié)點(diǎn)的復(fù)制緩沖區(qū)可能溢出……
高可用: 集群支持主從復(fù)制和主節(jié)點(diǎn)的 自動故障轉(zhuǎn)移 (與哨兵類似),當(dāng)任一節(jié)點(diǎn)發(fā)生故障時,集群仍然可以對外提供服務(wù)。

快速體驗

第一步:創(chuàng)建集群節(jié)點(diǎn)配置文件
首先我們找一個地方創(chuàng)建一個名為 redis-cluster 的目錄:

mkdir -p ~/Desktop/redis-cluster

然后按照上面的方法,創(chuàng)建六個配置文件,分別命名為:redis_7000.conf/redis_7001.conf…redis_7005.conf,然后根據(jù)不同的端口號修改對應(yīng)的端口值就好了:

# 后臺執(zhí)行 daemonize yes # 端口號 port 7000 # 為每一個集群節(jié)點(diǎn)指定一個 pid_file pidfile ~/Desktop/redis-cluster/redis_7000.pid # 啟動集群模式 cluster-enabled yes # 每一個集群節(jié)點(diǎn)都有一個配置文件,這個文件是不能手動編輯的。確保每一個集群節(jié)點(diǎn)的配置文件不通 cluster-config-file nodes-7000.conf # 集群節(jié)點(diǎn)的超時時間,單位:ms,超時后集群會認(rèn)為該節(jié)點(diǎn)失敗 cluster-node-timeout 5000 # 最后將 appendonly 改成 yes(AOF 持久化) appendonly yes

記得把對應(yīng)上述配置文件中根端口對應(yīng)的配置都修改掉 (port/ pidfile/ cluster-config-file)。

第二步:分別啟動 6 個 Redis 實例
redis-server ~/Desktop/redis-cluster/redis_7000.conf
redis-server ~/Desktop/redis-cluster/redis_7001.conf
redis-server ~/Desktop/redis-cluster/redis_7002.conf
redis-server ~/Desktop/redis-cluster/redis_7003.conf
redis-server ~/Desktop/redis-cluster/redis_7004.conf
redis-server ~/Desktop/redis-cluster/redis_7005.conf

然后執(zhí)行 ps -ef | grep redis 查看是否啟動成功:
可以看到 6 個 Redis 節(jié)點(diǎn)都以集群的方式成功啟動了,但是現(xiàn)在每個節(jié)點(diǎn)還處于獨(dú)立的狀態(tài),也就是說它們每一個都各自成了一個集群,還沒有互相聯(lián)系起來,我們需要手動地把他們之間建立起聯(lián)系。

第三步:建立集群
執(zhí)行下列命令:

redis-cli --cluster create --cluster-replicas 1 127.0.0.1:7000 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

這里稍微解釋一下這個 --replicas 1 的意思是:我們希望為集群中的每個主節(jié)點(diǎn)創(chuàng)建一個從節(jié)點(diǎn)。
觀察控制臺輸出:

看到 [OK] 的信息之后,就表示集群已經(jīng)搭建成功了,可以看到,這里我們正確地創(chuàng)建了三主三從的集群。

第四步:驗證集群
我們先使用 redic-cli 任意連接一個節(jié)點(diǎn):

redis-cli -c -h 127.0.0.1 -p 7000 127.0.0.1:7000>

-c表示集群模式;-h 指定 ip 地址;-p 指定端口。
然后隨便 set 一些值觀察控制臺輸入:

127.0.0.1:7000> SET name wmyskxz -> Redirected to slot [5798] located at 127.0.0.1:7001 OK 127.0.0.1:7001>

可以看到這里 Redis 自動幫我們進(jìn)行了 Redirected 操作跳轉(zhuǎn)到了 7001 這個實例上。

我們再使用 cluster info (查看集群信息) 和 cluster nodes (查看節(jié)點(diǎn)列表) 來分別看看:(任意節(jié)點(diǎn)輸入均可)

127.0.0.1:7001> CLUSTER INFO cluster_state:ok cluster_slots_assigned:16384 cluster_slots_ok:16384 cluster_slots_pfail:0 cluster_slots_fail:0 cluster_known_nodes:6 cluster_size:3 cluster_current_epoch:6 cluster_my_epoch:2 cluster_stats_messages_ping_sent:1365 cluster_stats_messages_pong_sent:1358 cluster_stats_messages_meet_sent:4 cluster_stats_messages_sent:2727 cluster_stats_messages_ping_received:1357 cluster_stats_messages_pong_received:1369 cluster_stats_messages_meet_received:1 cluster_stats_messages_received:2727127.0.0.1:7001> CLUSTER NODES 56a04742f36c6e84968cae871cd438935081e86f 127.0.0.1:7003@17003 slave 4ec8c022e9d546c9b51deb9d85f6cf867bf73db6 0 1584428884000 4 connected 4ec8c022e9d546c9b51deb9d85f6cf867bf73db6 127.0.0.1:7000@17000 master - 0 1584428884000 1 connected 0-5460 e2539c4398b8258d3f9ffa714bd778da107cb2cd 127.0.0.1:7005@17005 slave a3406db9ae7144d17eb7df5bffe8b70bb5dd06b8 0 1584428885222 6 connected d31cd1f423ab1e1849cac01ae927e4b6950f55d9 127.0.0.1:7004@17004 slave 236cefaa9cdc295bc60a5bd1aed6a7152d4f384d 0 1584428884209 5 connected 236cefaa9cdc295bc60a5bd1aed6a7152d4f384d 127.0.0.1:7001@17001 myself,master - 0 1584428882000 2 connected 5461-10922 a3406db9ae7144d17eb7df5bffe8b70bb5dd06b8 127.0.0.1:7002@17002 master - 0 1584428884000 3 connected 10923-16383 127.0.0.1:7001>

數(shù)據(jù)分區(qū)方案簡析

方案一:哈希值 % 節(jié)點(diǎn)數(shù)
哈希取余分區(qū)思路非常簡單:計算 key 的 hash 值,然后對節(jié)點(diǎn)數(shù)量進(jìn)行取余,從而決定數(shù)據(jù)映射到哪個節(jié)點(diǎn)上。

不過該方案最大的問題是,當(dāng)新增或刪減節(jié)點(diǎn)時,節(jié)點(diǎn)數(shù)量發(fā)生變化,系統(tǒng)中所有的數(shù)據(jù)都需要 重新計算映射關(guān)系,引發(fā)大規(guī)模數(shù)據(jù)遷移。

方案二:一致性哈希分區(qū)
一致性哈希算法將 整個哈希值空間 組織成一個虛擬的圓環(huán),范圍是 [0 , 232-1],對于每一個數(shù)據(jù),根據(jù) key 計算 hash 值,確數(shù)據(jù)在環(huán)上的位置,然后從此位置沿順時針行走,找到的第一臺服務(wù)器就是其應(yīng)該映射到的服務(wù)器:

與哈希取余分區(qū)相比,一致性哈希分區(qū)將 增減節(jié)點(diǎn)的影響限制在相鄰節(jié)點(diǎn)。以上圖為例,如果在 node1 和 node2 之間增加 node5,則只有 node2 中的一部分?jǐn)?shù)據(jù)會遷移到 node5;如果去掉 node2,則原 node2 中的數(shù)據(jù)只會遷移到 node4 中,只有 node4 會受影響。

一致性哈希分區(qū)的主要問題在于,當(dāng) 節(jié)點(diǎn)數(shù)量較少 時,增加或刪減節(jié)點(diǎn),對單個節(jié)點(diǎn)的影響可能很大,造成數(shù)據(jù)的嚴(yán)重不平衡。還是以上圖為例,如果去掉 node2,node4 中的數(shù)據(jù)由總數(shù)據(jù)的 1/4 左右變?yōu)?1/2 左右,與其他節(jié)點(diǎn)相比負(fù)載過高。

方案三:帶有虛擬節(jié)點(diǎn)的一致性哈希分區(qū)
該方案在 一致性哈希分區(qū)的基礎(chǔ)上,引入了 虛擬節(jié)點(diǎn) 的概念。Redis 集群使用的便是該方案,其中的虛擬節(jié)點(diǎn)稱為 槽(slot)。槽是介于數(shù)據(jù)和實際節(jié)點(diǎn)之間的虛擬概念,每個實際節(jié)點(diǎn)包含一定數(shù)量的槽,每個槽包含哈希值在一定范圍內(nèi)的數(shù)據(jù)。

在使用了槽的一致性哈希分區(qū)中,槽是數(shù)據(jù)管理和遷移的基本單位。槽 解耦 了 數(shù)據(jù)和實際節(jié)點(diǎn) 之間的關(guān)系,增加或刪除節(jié)點(diǎn)對系統(tǒng)的影響很小。仍以上圖為例,系統(tǒng)中有 4 個實際節(jié)點(diǎn),假設(shè)為其分配 16 個槽(0-15);

槽 0-3 位于 node1;4-7 位于 node2;以此類推…
如果此時刪除 node2,只需要將槽 4-7 重新分配即可,例如槽 4-5 分配給 node1,槽 6 分配給 node3,槽 7 分配給 node4;可以看出刪除 node2 后,數(shù)據(jù)在其他節(jié)點(diǎn)的分布仍然較為均衡。

節(jié)點(diǎn)通信機(jī)制簡析

集群的建立離不開節(jié)點(diǎn)之間的通信,例如我們上訪在 快速體驗 中剛啟動六個集群節(jié)點(diǎn)之后通過 redis-cli 命令幫助我們搭建起來了集群,實際上背后每個集群之間的兩兩連接是通過了 CLUSTER MEET 命令發(fā)送 MEET 消息完成的,下面我們展開詳細(xì)說說。

兩個端口
哨兵系統(tǒng) 中,節(jié)點(diǎn)分為 數(shù)據(jù)節(jié)點(diǎn) 和 哨兵節(jié)點(diǎn):前者存儲數(shù)據(jù),后者實現(xiàn)額外的控制功能。在 集群 中,沒有數(shù)據(jù)節(jié)點(diǎn)與非數(shù)據(jù)節(jié)點(diǎn)之分:所有的節(jié)點(diǎn)都存儲數(shù)據(jù),也都參與集群狀態(tài)的維護(hù)。為此,集群中的每個節(jié)點(diǎn),都提供了兩個 TCP 端口:

普通端口: 即我們在前面指定的端口 (7000等)。普通端口主要用于為客戶端提供服務(wù) (與單機(jī)節(jié)點(diǎn)類似);但在節(jié)點(diǎn)間數(shù)據(jù)遷移時也會使用。
集群端口: 端口號是普通端口 + 10000 (10000是固定值,無法改變),如 7000 節(jié)點(diǎn)的集群端口為 17000。集群端口只用于節(jié)點(diǎn)之間的通信,如搭建集群、增減節(jié)點(diǎn)、故障轉(zhuǎn)移等操作時節(jié)點(diǎn)間的通信;不要使用客戶端連接集群接口。為了保證集群可以正常工作,在配置防火墻時,要同時開啟普通端口和集群端口。

Gossip 協(xié)議
節(jié)點(diǎn)間通信,按照通信協(xié)議可以分為幾種類型:單對單、廣播、Gossip 協(xié)議等。重點(diǎn)是廣播和 Gossip 的對比。

廣播是指向集群內(nèi)所有節(jié)點(diǎn)發(fā)送消息。優(yōu)點(diǎn) 是集群的收斂速度快(集群收斂是指集群內(nèi)所有節(jié)點(diǎn)獲得的集群信息是一致的),缺點(diǎn) 是每條消息都要發(fā)送給所有節(jié)點(diǎn),CPU、帶寬等消耗較大。
Gossip 協(xié)議的特點(diǎn)是:在節(jié)點(diǎn)數(shù)量有限的網(wǎng)絡(luò)中,每個節(jié)點(diǎn)都 “隨機(jī)” 的與部分節(jié)點(diǎn)通信 (并不是真正的隨機(jī),而是根據(jù)特定的規(guī)則選擇通信的節(jié)點(diǎn)),經(jīng)過一番雜亂無章的通信,每個節(jié)點(diǎn)的狀態(tài)很快會達(dá)到一致。Gossip 協(xié)議的 優(yōu)點(diǎn) 有負(fù)載 (比廣播) 低、去中心化、容錯性高 (因為通信有冗余) 等;缺點(diǎn) 主要是集群的收斂速度慢。
消息類型
集群中的節(jié)點(diǎn)采用 固定頻率(每秒10次) 的 定時任務(wù) 進(jìn)行通信相關(guān)的工作:判斷是否需要發(fā)送消息及消息類型、確定接收節(jié)點(diǎn)、發(fā)送消息等。如果集群狀態(tài)發(fā)生了變化,如增減節(jié)點(diǎn)、槽狀態(tài)變更,通過節(jié)點(diǎn)間的通信,所有節(jié)點(diǎn)會很快得知整個集群的狀態(tài),使集群收斂。

節(jié)點(diǎn)間發(fā)送的消息主要分為 5 種:meet 消息、ping 消息、pong 消息、fail 消息、publish 消息。不同的消息類型,通信協(xié)議、發(fā)送的頻率和時機(jī)、接收節(jié)點(diǎn)的選擇等是不同的:

MEET 消息: 在節(jié)點(diǎn)握手階段,當(dāng)節(jié)點(diǎn)收到客戶端的 CLUSTER MEET 命令時,會向新加入的節(jié)點(diǎn)發(fā)送 MEET 消息,請求新節(jié)點(diǎn)加入到當(dāng)前集群;新節(jié)點(diǎn)收到 MEET 消息后會回復(fù)一個 PONG 消息。
PING 消息: 集群里每個節(jié)點(diǎn)每秒鐘會選擇部分節(jié)點(diǎn)發(fā)送 PING 消息,接收者收到消息后會回復(fù)一個 PONG 消息。PING 消息的內(nèi)容是自身節(jié)點(diǎn)和部分其他節(jié)點(diǎn)的狀態(tài)信息,作用是彼此交換信息,以及檢測節(jié)點(diǎn)是否在線。PING 消息使用 Gossip 協(xié)議發(fā)送,接收節(jié)點(diǎn)的選擇兼顧了收斂速度和帶寬成本,具體規(guī)則如下:(1)隨機(jī)找 5 個節(jié)點(diǎn),在其中選擇最久沒有通信的 1 個節(jié)點(diǎn);(2)掃描節(jié)點(diǎn)列表,選擇最近一次收到 PONG 消息時間大于 cluster_node_timeout / 2 的所有節(jié)點(diǎn),防止這些節(jié)點(diǎn)長時間未更新。
PONG消息: PONG 消息封裝了自身狀態(tài)數(shù)據(jù)。可以分為兩種:第一種 是在接到 MEET/PING 消息后回復(fù)的 PONG 消息;第二種 是指節(jié)點(diǎn)向集群廣播 PONG 消息,這樣其他節(jié)點(diǎn)可以獲知該節(jié)點(diǎn)的最新信息,例如故障恢復(fù)后新的主節(jié)點(diǎn)會廣播 PONG 消息。
FAIL 消息: 當(dāng)一個主節(jié)點(diǎn)判斷另一個主節(jié)點(diǎn)進(jìn)入 FAIL 狀態(tài)時,會向集群廣播這一 FAIL 消息;接收節(jié)點(diǎn)會將這一 FAIL 消息保存起來,便于后續(xù)的判斷。
PUBLISH 消息: 節(jié)點(diǎn)收到 PUBLISH 命令后,會先執(zhí)行該命令,然后向集群廣播這一消息,接收節(jié)點(diǎn)也會執(zhí)行該 PUBLISH 命令。

數(shù)據(jù)結(jié)構(gòu)簡析

節(jié)點(diǎn)需要專門的數(shù)據(jù)結(jié)構(gòu)來存儲集群的狀態(tài)。所謂集群的狀態(tài),是一個比較大的概念,包括:集群是否處于上線狀態(tài)、集群中有哪些節(jié)點(diǎn)、節(jié)點(diǎn)是否可達(dá)、節(jié)點(diǎn)的主從狀態(tài)、槽的分布……

節(jié)點(diǎn)為了存儲集群狀態(tài)而提供的數(shù)據(jù)結(jié)構(gòu)中,最關(guān)鍵的是 clusterNode 和 clusterState 結(jié)構(gòu):前者記錄了一個節(jié)點(diǎn)的狀態(tài),后者記錄了集群作為一個整體的狀態(tài)。

clusterNode 結(jié)構(gòu)
clusterNode 結(jié)構(gòu)保存了 一個節(jié)點(diǎn)的當(dāng)前狀態(tài),包括創(chuàng)建時間、節(jié)點(diǎn) id、ip 和端口號等。每個節(jié)點(diǎn)都會用一個 clusterNode 結(jié)構(gòu)記錄自己的狀態(tài),并為集群內(nèi)所有其他節(jié)點(diǎn)都創(chuàng)建一個 clusterNode 結(jié)構(gòu)來記錄節(jié)點(diǎn)狀態(tài)。

下面列舉了 clusterNode 的部分字段,并說明了字段的含義和作用:

typedef struct clusterNode {//節(jié)點(diǎn)創(chuàng)建時間mstime_t ctime;//節(jié)點(diǎn)idchar name[REDIS_CLUSTER_NAMELEN];//節(jié)點(diǎn)的ip和端口號char ip[REDIS_IP_STR_LEN];int port;//節(jié)點(diǎn)標(biāo)識:整型,每個bit都代表了不同狀態(tài),如節(jié)點(diǎn)的主從狀態(tài)、是否在線、是否在握手等int flags;//配置紀(jì)元:故障轉(zhuǎn)移時起作用,類似于哨兵的配置紀(jì)元uint64_t configEpoch;//槽在該節(jié)點(diǎn)中的分布:占用16384/8個字節(jié),16384個比特;每個比特對應(yīng)一個槽:比特值為1,則該比特對應(yīng)的槽在節(jié)點(diǎn)中;比特值為0,則該比特對應(yīng)的槽不在節(jié)點(diǎn)中unsigned char slots[16384/8];//節(jié)點(diǎn)中槽的數(shù)量int numslots;………… } clusterNode;

除了上述字段,clusterNode 還包含節(jié)點(diǎn)連接、主從復(fù)制、故障發(fā)現(xiàn)和轉(zhuǎn)移需要的信息等。

clusterState 結(jié)構(gòu)
clusterState 結(jié)構(gòu)保存了在當(dāng)前節(jié)點(diǎn)視角下,集群所處的狀態(tài)。主要字段包括:

typedef struct clusterState {//自身節(jié)點(diǎn)clusterNode *myself;//配置紀(jì)元uint64_t currentEpoch;//集群狀態(tài):在線還是下線int state;//集群中至少包含一個槽的節(jié)點(diǎn)數(shù)量int size;//哈希表,節(jié)點(diǎn)名稱->clusterNode節(jié)點(diǎn)指針dict *nodes;//槽分布信息:數(shù)組的每個元素都是一個指向clusterNode結(jié)構(gòu)的指針;如果槽還沒有分配給任何節(jié)點(diǎn),則為NULLclusterNode *slots[16384];………… } clusterState;

除此之外,clusterState 還包括故障轉(zhuǎn)移、槽遷移等需要的信息。

總結(jié)

以上是生活随笔為你收集整理的Redis集群入门实践教程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。