哨兵模式
- 官方文檔:https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel
- 關(guān)聯(lián)博客:Redis主從復(fù)制(下文能用到)
- 極簡概括:自動監(jiān)控Redis主節(jié)點是否故障的一種方案旋膳,若主節(jié)點故障,則Redis會根據(jù)投票數(shù)自動將從庫切換為主庫(這個過程途事,叫仲裁)溺忧。
- 解決問題:在主從復(fù)制的架構(gòu)模式下咏连,Redis主節(jié)點掛掉后,從節(jié)點無任何補償操作鲁森,無人工干預(yù)的情況下導(dǎo)致整個緩存鏈路的寫功能喪失祟滴。而哨兵模式有哨兵看守機制,可以做到主機的檢測與自動切換從機為主機的功能歌溉。
- 適用場景:對于需要7 * 24高可用垄懂,且公司原意投資相關(guān)運維成本的服務(wù)端應(yīng)用。畢竟作為哨兵節(jié)點的Redis實例痛垛,將無法使用緩存服務(wù)草慧,只能作為哨兵,而且要求哨兵數(shù)量往往是奇數(shù)個匙头。
- 優(yōu)點:
- 降低運維成本:強大的高可用機制漫谷,適當(dāng)降低運維成本。
- 自動恢復(fù)機制:當(dāng)主節(jié)點掛掉后蹂析,哨兵會自動選出一個從節(jié)點作為主節(jié)點舔示,繼續(xù)對外提供服務(wù),無人值守电抚。
- 缺點:
- 場景限制:小型公司Redis都可以不用惕稻,中型公司也不一定能用到Redis主從,更何況是哨兵這么嚴(yán)謹(jǐn)?shù)倪\維策略蝙叛。
- 資金問題:Redis需要堆多個服務(wù)器俺祠,對公司而言是不小的支出,有一定門檻借帘。
- 延遲問題:主節(jié)點掛掉時蜘渣,雖然可以做到自動切換,但是多個哨兵認(rèn)為Redis能夠客觀下線時肺然,這個過程需要時間的宋梧,雖然可以調(diào)整,但是這個時間內(nèi)的Redis寫操作是失效的狰挡,因此有了集群策略捂龄。
- 數(shù)據(jù)丟失問題:Redis主從是異步復(fù)制,哨兵只是增加了自動化的切換功能加叁,沒有像MySQL的redo log機制倦沧,無法保證數(shù)據(jù)100%不丟失。
- 會引發(fā)腦裂問題(下文有說)它匕。
- 誤區(qū):哨兵是哨兵展融,集群是集群,兩者無關(guān)豫柬。哨兵是主從復(fù)制架構(gòu)的高可用優(yōu)化方案告希,不是集群部署的高可用的方案扑浸。
- 訪問流程:由原先的編程語言客戶端訪問Redis主節(jié)點或從節(jié)點,變成了客戶端訪問哨兵節(jié)點(通常不止一個哨兵燕偶,有奇數(shù)個哨兵組成一個哨兵集群喝噪,奇數(shù)好投票),由哨兵節(jié)點告訴客戶端訪問那個主節(jié)點或從節(jié)點指么,從而區(qū)分讀寫操作酝惧。
實操(1主+2從+3哨)
- 三哨理由:一個小區(qū)也不只一個保安,至少2個保安輪班倒伯诬。但是哨兵有類似投票機制晚唇,最好用奇數(shù)個哨兵。
- 環(huán)境決策:部署3個哨兵+1個Master+2個Slave盗似,共6臺服務(wù)器哩陕。
- Docker方案:當(dāng)內(nèi)存扛不住的時候可以用這個,但是拉鏡像時發(fā)現(xiàn)被墻了(國內(nèi)混蛋專家搞的混蛋規(guī)則)赫舒,鏡像拉不下來悍及,本地也沒有,棄用了這條路線号阿。
- 運行環(huán)境:CentOS 7.6并鸵,每個系統(tǒng)分配256M內(nèi)存鸳粉,Linux可輕松啟動扔涧,共占用內(nèi)存1.5GB,設(shè)備能承受届谈,但磁盤占用挺高(特別是開機時)枯夜,每個Redis實例都配置好了遠(yuǎn)程連接功能(防火墻、遠(yuǎn)程連接權(quán)限艰山、保護模式湖雹,都配置到位)。
- IP分配:192.168.0.180(主)曙搬、192.168.0.181(從1)摔吏、192.168.0.182(從2)、192.168.0.183(哨1)纵装、192.168.0.184(哨2)征讲、192.168.0.185(哨3),如圖
192.168.0.183 192.168.0.181
/ \ /
/ \ /
/ \ /
192.168.0.xxx -->-> 192.168.0.184 ---> 192.168.0.180
\ / \
\ / \
\ / \
192.168.0.185 192.168.0.182
- 主要配置說明:
配置3臺哨兵:
高版本Redis自帶sentinel.conf文件橡娄,可直接用诗箍。
目前用的5,沒有獨立的配置文件挽唉,可在redis.conf中配置如下:
sentinel monitor 主節(jié)點名 主節(jié)點IP 主節(jié)點端口 確認(rèn)客觀下線的最少哨兵數(shù)量滤祖。 // 配置鏈接筷狼,用于定位主機網(wǎng)絡(luò)位置。
sentinel auth-pass 主節(jié)點名 主節(jié)點密碼 //配置主機密碼
sentinel down-after-milliseconds 主節(jié)點名 毫秒 //配置心跳失去連接多少毫秒后匠童,讓哨兵認(rèn)為主機宕機
sentinel failover-timeout 主節(jié)點名 毫秒 //配置執(zhí)行故障轉(zhuǎn)移所需的超時時間
sentinel parallel-syncs 主節(jié)點名 數(shù)量 //切換新的主節(jié)點之后埂材,可同時同步其余從節(jié)點的個數(shù),數(shù)字越小性能越低俏让。
其它配置不常用楞遏,可參考官方手冊。
對于sentinel monitor 配置項首昔,“確認(rèn)客觀下線的最少哨兵數(shù)量”參數(shù)的介紹:
至少有N個哨兵節(jié)點寡喝,認(rèn)為主節(jié)點有故障,才會將其下線勒奇。
由于哨兵節(jié)點自身負(fù)載問題预鬓,或網(wǎng)絡(luò)鏈路有抖動問題,這會直接影響Redis哨兵檢測主節(jié)點的心跳赊颠,誤認(rèn)為主節(jié)點是宕機格二。
但是容易有誤判,所以設(shè)定了指定的容錯額度竣蹦,減少誤判率顶猜。
- 實操:
第一步,先配置1主(不用配置)2從(需要在192.168.0.181和192.168.0.182上):
我之前寫過完整文章痘括,可參考https://blog.csdn.net/weixin_42100387/article/details/139667697
vim /usr/local/redis/etc/redis.conf
#配置主節(jié)點IP和端口
replicaof 192.168.0.180 6379
#配置主節(jié)點密碼
masterauth 123456
之后重啟redis
需要注意长窄,必須把每個從機的密碼設(shè)置一致,方便哨兵模式主從切換實例纲菌,避免整個緩存模塊故障挠日。
第二步,配置3哨兵:
以Redis 5為例,3臺哨兵節(jié)點(192.168.0.183~185)配置以下主要內(nèi)容翰舌,其余配置可按需添加(因為不配置也有默認(rèn)值嚣潜,不會導(dǎo)致錯誤):
補充:一個哨兵是可以監(jiān)控多個master的,但是這對于開發(fā)幾乎用不上椅贱。
firewall-cmd --add-port=26379/tcp --zone=public --permanent && systemctl restart firewalld
vim /usr/local/redis/etc/sentinel.conf
bind 0.0.0.0
#哨兵默認(rèn)端口
port 26379
daemonize yes
protected-mode no
pidfile /usr/local/redis/redis_26379.pid
logfile /usr/local/redis/redis_26379.log
sentinel monitor zs_master 192.168.0.180 6379 2
sentinel auth-pass zs_master 123456
之后啟動哨兵懂算,用以下二選一命令的方式
/usr/local/redis/bin/redis-sentinel /usr/local/redis/etc/sentinel.conf
/usr/local/redis/bin/redis-server /usr/local/redis/etc/sentinel.conf --sentinel
啟動后,redis 會為sentinel.conf額外增加如下配置:
sentinel myid 3feb02e7fd5a96152e217b76f11e72236c23668c
sentinel deny-scripts-reconfig yes
# Generated by CONFIG REWRITE
dir "/usr/local/redis/etc"
sentinel config-epoch zs_master 0
sentinel leader-epoch zs_master 0
sentinel known-replica zs_master 192.168.0.182 6379
sentinel known-replica zs_master 192.168.0.181 6379
sentinel current-epoch 0
它們的含義分別是:
sentinel myid:起個唯一標(biāo)識庇麦,方便定位哨兵计技,類比MySQL表id。
sentinel deny-scripts-reconfig yes: 拒絕通過腳本進行配置更改女器,這有助于提高安全性酸役,防止非授權(quán)的更改。
dir "/usr/local/redis/etc": 指定rdb文件的位置。
sentinel config-epoch zs_master 0: 記錄主節(jié)點zs_master的情況涣澡,用于判斷哨兵配置的更新情況贱呐。
sentinel leader-epoch zs_master 0: 記錄主節(jié)點zs_master的情況,用于判斷哨兵的領(lǐng)導(dǎo)者入桂。
sentinel known-replica zs_master 192.168.0.182 6379: 記錄已知的從節(jié)點奄薇,這里指定了IP地址和端口號。
sentinel current-epoch 0: 記錄當(dāng)前的紀(jì)元(直譯就這意思)抗愁,用于標(biāo)識哨兵配置的更新情況馁蒂。
第三步,檢測1主2從是否能正常使用
可在主節(jié)點Redis會話中執(zhí)行 set a 123蜘腌,從節(jié)點執(zhí)行 get a 判斷主從是否正常復(fù)制沫屡。
第四步,檢測3臺哨兵是否啟動(3臺設(shè)備分別執(zhí)行撮珠,逐一檢測):
ps aux | grep redis
root 81041 0.4 0.5 153996 2788 ? Ssl 00:34 0:00 /usr/local/redis/bin/redis-sentinel 0.0.0.0:26379 [sentinel]
root 81084 0.0 0.2 112828 992 pts/0 R+ 00:35 0:00 grep --color=auto redis
netstat -nlp | grep redis
tcp 0 0 0.0.0.0:26379 0.0.0.0:* LISTEN 81041/redis-sentin
模擬主節(jié)點故障
經(jīng)過實測:
- 主節(jié)點執(zhí)行
shutdown
模擬宕機沮脖,半分鐘內(nèi),Redis哨兵自動完成了主節(jié)點的下線芯急,并將某個從節(jié)點切換為主節(jié)點勺届。 - 從節(jié)點升級為主節(jié)點的節(jié)點,以及其它從節(jié)點娶耍,的配置文件都會被強制更改免姿,作用到配置文件,是持久化的榕酒。
- 哨兵切換流程執(zhí)行完畢后原主節(jié)點恢復(fù)訪問胚膊,那么原主節(jié)點會變成從節(jié)點,不會因為本身的恢復(fù)奈应,就再次改為主節(jié)點澜掩。
- 一些情情況下购披,當(dāng)掛掉的主節(jié)點再次啟動時杖挣,可成功啟動,但是redis的機制會把master-auth配置項干丟掉刚陡,造成啟動之后(變?yōu)閺墓?jié)點)連不上主節(jié)點的情況惩妇。
哨兵對應(yīng)日志說明
- 由于Redis哨兵模式趨向于自動化運維,所以日常日志的排查七分重要筐乳,故在此說明其中1個哨兵(192.168.0.184)的日志記錄概況(最起碼要看懂【何時主節(jié)點掛】歌殃、【讓誰做主節(jié)點】)。
- 速查方法(根據(jù)關(guān)鍵字搜索蝙云,若搜索出多個值氓皱,最后一個為最新):
- 查詢何時主節(jié)點掛:odown master 原主節(jié)點名 原主節(jié)點IP 原主節(jié)點端口 #quorum
- 查詢讓誰做主節(jié)點:+switch-master 原主節(jié)點名 原主節(jié)點IP 原主節(jié)點端口(后面緊跟新主節(jié)點)
- 哨兵啟動時:
Redis哨兵初始化
99136:X 19 Jun 2024 05:58:54.697 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
99136:X 19 Jun 2024 05:58:54.697 # Redis version=5.0.9, bits=64, commit=00000000, modified=0, pid=99136, just started
99136:X 19 Jun 2024 05:58:54.697 # Configuration loaded
99137:X 19 Jun 2024 05:58:54.701 * Running mode=sentinel, port=26379.
99137:X 19 Jun 2024 05:58:54.701 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
99137:X 19 Jun 2024 05:58:54.702 # Sentinel ID is 0da5ef8126894b04e2572c4f2f9e1e636543ceee
主節(jié)點從節(jié)點信息獲取
99137:X 19 Jun 2024 05:58:54.702 # +monitor master zs_master 192.168.0.180 6379 quorum 2
99137:X 19 Jun 2024 05:58:54.720 * +slave slave 192.168.0.181:6379 192.168.0.181 6379 @ zs_master 192.168.0.180 6379
99137:X 19 Jun 2024 05:58:54.722 * +slave slave 192.168.0.182:6379 192.168.0.182 6379 @ zs_master 192.168.0.180 6379
添加了兩個哨兵
99137:X 19 Jun 2024 05:58:56.059 * +sentinel sentinel 91e7adffd0d7fc08fc1eb622001eb78b7c230901 192.168.0.183 26379 @ zs_master 192.168.0.180 6379
99137:X 19 Jun 2024 05:58:59.359 * +sentinel sentinel cadd108977e5634179172b6a2f84f4712f2c9185 192.168.0.185 26379 @ zs_master 192.168.0.180 6379
- 主節(jié)點宕機時:
當(dāng)前哨兵認(rèn)為主機已下線,主觀下線。
99137:X 19 Jun 2024 05:59:59.572 # +sdown master zs_master 192.168.0.180 6379
其它哨兵也認(rèn)為主機已下線波材,并達(dá)到了sentinel monitor設(shè)定的閾值股淡,主觀下線升級為客觀下線
99137:X 19 Jun 2024 05:59:59.627 # +odown master zs_master 192.168.0.180 6379 #quorum 2/2
故障切換,當(dāng)前切換廷区,讓0da5ef8126894b04e2572c4f2f9e1e636543ceee 哨兵說了算唯灵,其余哨兵投票。
99137:X 19 Jun 2024 05:59:59.627 # +new-epoch 1
99137:X 19 Jun 2024 05:59:59.627 # +try-failover master zs_master 192.168.0.180 6379
99137:X 19 Jun 2024 05:59:59.628 # +vote-for-leader 0da5ef8126894b04e2572c4f2f9e1e636543ceee 1
99137:X 19 Jun 2024 05:59:59.630 # cadd108977e5634179172b6a2f84f4712f2c9185 voted for 0da5ef8126894b04e2572c4f2f9e1e636543ceee 1
99137:X 19 Jun 2024 05:59:59.630 # 91e7adffd0d7fc08fc1eb622001eb78b7c230901 voted for 0da5ef8126894b04e2572c4f2f9e1e636543ceee 1
選擇192.168.0.182作為主節(jié)點隙轻,客觀下線老主節(jié)點埠帕,并將180的主節(jié)點轉(zhuǎn)移為182的主節(jié)點,換人和交換權(quán)利的動作不重復(fù)玖绿。
99137:X 19 Jun 2024 05:59:59.732 # +elected-leader master zs_master 192.168.0.180 6379
99137:X 19 Jun 2024 05:59:59.732 # +failover-state-select-slave master zs_master 192.168.0.180 6379
99137:X 19 Jun 2024 05:59:59.837 # +selected-slave slave 192.168.0.182:6379 192.168.0.182 6379 @ zs_master 192.168.0.180 6379
99137:X 19 Jun 2024 05:59:59.837 * +failover-state-send-slaveof-noone slave 192.168.0.182:6379 192.168.0.182 6379 @ zs_master 192.168.0.180 6379
99137:X 19 Jun 2024 05:59:59.907 * +failover-state-wait-promotion slave 192.168.0.182:6379 192.168.0.182 6379 @ zs_master 192.168.0.180 6379
99137:X 19 Jun 2024 06:00:00.239 # +promoted-slave slave 192.168.0.182:6379 192.168.0.182 6379 @ zs_master 192.168.0.180 6379
99137:X 19 Jun 2024 06:00:00.239 # +failover-state-reconf-slaves master zs_master 192.168.0.180 6379
99137:X 19 Jun 2024 06:00:00.298 * +slave-reconf-sent slave 192.168.0.181:6379 192.168.0.181 6379 @ zs_master 192.168.0.180 6379
99137:X 19 Jun 2024 06:00:00.788 # -odown master zs_master 192.168.0.180 6379
99137:X 19 Jun 2024 06:00:01.280 * +slave-reconf-inprog slave 192.168.0.181:6379 192.168.0.181 6379 @ zs_master 192.168.0.180 6379
99137:X 19 Jun 2024 06:00:01.281 * +slave-reconf-done slave 192.168.0.181:6379 192.168.0.181 6379 @ zs_master 192.168.0.180 6379
99137:X 19 Jun 2024 06:00:01.353 # +failover-end master zs_master 192.168.0.180 6379
99137:X 19 Jun 2024 06:00:01.353 # +switch-master zs_master 192.168.0.180 6379 192.168.0.182 6379
將180敛瓷,181節(jié)點,當(dāng)做從節(jié)點處理斑匪。
99137:X 19 Jun 2024 06:00:01.354 * +slave slave 192.168.0.181:6379 192.168.0.181 6379 @ zs_master 192.168.0.182 6379
99137:X 19 Jun 2024 06:00:01.354 * +slave slave 192.168.0.180:6379 192.168.0.180 6379 @ zs_master 192.168.0.182 6379
當(dāng)前哨兵發(fā)現(xiàn)180從節(jié)點仍不能用琐驴,主觀下線。
99137:X 19 Jun 2024 06:00:31.393 # +sdown slave 192.168.0.180:6379 192.168.0.180 6379 @ zs_master 192.168.0.182 6379
編程語言調(diào)用
- 以原生PHP為例:
// 創(chuàng)建Redis哨兵對象秤标,通過這個對象獲取主節(jié)點的地址绝淡,這里是寫死的,可以寫成數(shù)組苍姜,隨機訪問1個節(jié)點牢酵,用于減輕壓力。
$sentinel = new RedisSentinel('tcp://192.168.0.183', 26379);
//主機名
$master = $sentinel->getMasterAddrByName('zs_master');
if(! $master) {
echo '主節(jié)點獲取失敗';
die;
}
//獲取到的主節(jié)點地址進行寫操作
$redis = new Redis();
$redis->connect($master[0], $master[1]);
$redis->auth('123456');
$redis->set('key', 'val');
//從節(jié)點讀操作衙猪。
$slave = $sentinel->slaves('zs_master');
if(empty($slave)) {
echo '從節(jié)點獲取失敗';
die;
}
//隨機從節(jié)點配置數(shù)組的下標(biāo)馍乙,使其讀寫趨向于均衡,防止訪問壓力傾斜
$slave_arr_key = mt_rand(0, count($slave));
//這塊的從節(jié)點配置不能寫死垫释,一定要寫活丝格,萬一這里的從節(jié)點,變成了主節(jié)點呢棵譬?
$redis->connect($slave[$slave_arr_key]['ip'], $slave[$slave_arr_key]['port']);
$redis->auth('123456');
$res = $redis->get('key');
print_r($res);
// 關(guān)閉連接
$redis->close();
- 以Laravel為例:
Laravel官網(wǎng)并未搜索對應(yīng)的解決方案显蝌,想要在Laravel內(nèi)用Redis哨兵模式,可原生去實現(xiàn)订咸,或者使用某些composer包曼尊,例如monospice/laravel-redis-sentinel-drivers
,詳細(xì)內(nèi)容可看官方文檔:
Github地址:https://github.com/monospice/laravel-redis-sentinel-drivers
Packagist地址:https://packagist.org/packages/monospice/laravel-redis-sentinel-drivers
composer require monospice/laravel-redis-sentinel-drivers
Laravel5.5以下版本需要再config/app.php中配置如下內(nèi)容
'providers' => [
...
Monospice\LaravelRedisSentinel\RedisSentinelServiceProvider::class,
...
],
在config/database.php的Redis段的同級中添加脏嚷,這是演示骆撇,生產(chǎn)環(huán)境推薦參數(shù)寫活
這插件支持直接配置到.env,不用配config父叙,但是一旦執(zhí)行php artisan optimize加速神郊,那么env('config_key')將會是null肴裙,所以不用。
'redis-sentinel' => [
'default' => [
['host' => '192.168.0.183', 'port' => 26379,],
['host' => '192.168.0.184', 'port' => 26379,],
['host' => '192.168.0.185', 'port' => 26379,],
],
'options' => [
//主機名
'service' => 'zs_master',
'parameters' => [
'password' => '123456',
'database' => 0,
],
],
若要配置Redos Facdeds:
.env文件添加REDIS_DRIVER=redis-sentinel
這個不用配置config涌乳,因為沒有地方配置践宴,插件直接讀取env文件,測試php artisan optimize,發(fā)現(xiàn)能照常讀取爷怀。
若要配置Cache Facdeds:
config/cache.php的'default' => env('CACHE_DRIVER', 'file'), 此處最好修改env阻肩,將值改為redis-sentinel
并添加:
'stores' => [ //注意層級關(guān)系
...
'redis-sentinel' => [
'driver' => 'redis-sentinel',
'connection' => 'default',
],
],
若要配置Session Facdeds:
config/session.php的'driver' => env('SESSION_DRIVER', 'file'),,此處最好修改env运授,將值改為redis-sentinel,
并將connection項設(shè)置為'',否則報錯烤惊。
若要配置Queue隊列:
config/queue.php的'default' => env('QUEUE_CONNECTION', 'sync'),, 此處最好修改env吁朦,將值改為redis-sentinel,
并在connections項內(nèi)部配置:
'redis-sentinel' => [
'driver' => 'redis-sentinel',
'connection' => 'default',
'queue' => 'default',
'retry_after' => 90, // Laravel >= 5.4.30
'expire' => 90, // Laravel < 5.4.30
],
測試延時隊列柒室,可正常使用。
其余常用配置說明
- redis.conf
-
min-replicas-to-write 1與min-replicas-max-lag 10
:這個是高可用的兩個配置項逗宜,它根筷子一樣通常在一塊出現(xiàn)雄右,默認(rèn)是禁用的配置,配置在主節(jié)點和從節(jié)點纺讲。表示主節(jié)點一個寫操作擂仍,至少有1個確認(rèn)接收,且確認(rèn)回復(fù)(ack)時間不能超過10毫秒熬甚,否則返回的既不是nil也不是0逢渔,而是錯誤異常。它能夠保證主從復(fù)制時的高可用乡括,弊端就是若有從節(jié)點掛掉肃廓,達(dá)不到min-replicas-to-write設(shè)置的數(shù)量,主節(jié)點寫入將會報錯诲泌,導(dǎo)致寫功能廢掉盲赊。一般情況下,前者設(shè)置看情況敷扫,后者設(shè)置為3000~5000哀蘑。 - replica-priority 100:被選中作為從節(jié)點的優(yōu)先級,從1設(shè)置為10呻澜,從2設(shè)置為100递礼,則主節(jié)點掛掉惨险,從1(小的優(yōu)先)優(yōu)先被設(shè)定為從機羹幸。
-
主觀下線(Sdown)與客觀下線(Odown)
- 主觀下線:當(dāng)前(單個)哨兵認(rèn)為主節(jié)點掛了,有可能沒掛辫愉。
- 客觀下線:多個哨兵都認(rèn)為主節(jié)點掛了栅受,則主節(jié)點大概率掛了,準(zhǔn)備開始選舉其它從節(jié)點做主節(jié)點。
仲裁(Failover)
哨兵在主節(jié)點失效時進行自動故障轉(zhuǎn)移(failover)的行為屏镊。
腦裂(Split-brain)導(dǎo)致的數(shù)據(jù)丟失問題
- 分布式系統(tǒng)的概念:腦裂是一種在分布式系統(tǒng)中可能出現(xiàn)的情況依疼,指當(dāng)網(wǎng)絡(luò)故障導(dǎo)致部分節(jié)點無法與其它節(jié)點通信時,這些節(jié)點可能會錯誤地認(rèn)為自己是系統(tǒng)中唯一的活躍實例而芥,從而導(dǎo)致數(shù)據(jù)的不一致(一個地方每個山頭都獨立為王律罢,那就亂了,多了個頭頭棍丐,好比腦裂了)误辑。
- 在哨兵模式下(極少出現(xiàn)):客戶端與主節(jié)點連接正常,但是哨兵與主節(jié)點連接異常歌逢,那么哨兵可能就會讓其它從節(jié)點變?yōu)橹鞴?jié)點巾钉,此時分布式節(jié)點中,就會有多個主節(jié)點秘案。然而客戶端仍舊和舊的數(shù)據(jù)通信(執(zhí)行寫操作)砰苍,在這個過程下,數(shù)據(jù)會丟失阱高。
若此時舊主節(jié)點恢復(fù)與哨兵的連接赚导,舊主節(jié)點就會降級為從節(jié)點,舊主節(jié)點未同步的數(shù)據(jù)也會被新節(jié)點強制覆蓋赤惊。
腦裂(Split-brain)導(dǎo)致的數(shù)據(jù)丟失原因
- 感謝IT老奇的架構(gòu)600講:https://www.bilibili.com/video/BV1eF41147iL/?vd_source=19da1fcedca1050975549448303b95c2
- 背景:接上文辟癌,因為腦裂問題引發(fā)的哨兵模式自動故障轉(zhuǎn)移(failover)的行為(仲裁),荐捻,舊的主節(jié)點黍少,有著最近的進度。
- 過程:當(dāng)這個舊主節(jié)點與哨兵恢復(fù)連接后处面,并且已經(jīng)有新的主節(jié)點誕生厂置,舊主節(jié)點會被降級為從節(jié)點。
- 數(shù)據(jù)丟失內(nèi)部機制:舊主節(jié)點魂角,會向新的主節(jié)點申請全量數(shù)據(jù)昵济,新主節(jié)點會通過bgsave命令,異步生成rdb文件野揪,回傳到從節(jié)點访忿,從節(jié)點拿到后先清空自身的所有數(shù)據(jù),用一個干凈的數(shù)據(jù)倉庫承接主節(jié)點發(fā)來的數(shù)據(jù)斯稳,這個清空的過程海铆,造成了數(shù)據(jù)的丟失。
- 結(jié)論:客戶端與Redis服務(wù)端通信期間挣惰,最新的進度沒了卧斟,數(shù)據(jù)自然也就丟失了殴边。
腦裂(Split-brain)問題改善
由于網(wǎng)絡(luò)的隔離,通信無法像MySQL本地事務(wù)那樣做到幾乎完美的一致性策略珍语,說白了哨兵與主節(jié)點的通信斷了就是網(wǎng)斷了锤岸,斷了就是斷了,不可控也無規(guī)律板乙,代碼寫的再好也不能解決這個問題是偷。所以這個問題的解決方案,不能用解決修飾募逞,用改善修飾會更好晓猛。
可在主節(jié)點和其它從節(jié)點添加min-replicas-to-write 與min-replicas-max-lag
參數(shù)去改善,參數(shù)含義上文有講凡辱。
思路就是:判斷主節(jié)點寫入數(shù)據(jù)時從節(jié)點發(fā)送ack的數(shù)量戒职,以及延遲,若發(fā)生腦裂透乾,則主節(jié)點數(shù)量+1洪燥,從節(jié)點數(shù)量-1,達(dá)不到min-replicas-to-write閾值時乳乌,就會報錯捧韵。這也會導(dǎo)致緩存寫功能不可用,將異常兜底下推到代碼層汉操。
哨兵監(jiān)控主節(jié)點(哨兵切換步驟0)
哨兵探測主節(jié)點是否異常是用的心跳機制再来,就是每隔一段時間,向主節(jié)點發(fā)送ping進而保持連接磷瘤。
哨兵長(Zhang)選舉(哨兵切換步驟1)
- 動作:當(dāng)主節(jié)點掛掉芒篷,哨兵進行選舉主節(jié)點時,實際上進行了兩步:第一步采缚,哨兵會投票選舉一個哨兵長针炉,第二步,哨兵長選舉主節(jié)點扳抽。
- 選舉算法:Redis使用的時Raft算法篡帕。
- 算法流程(個人理解):
時序不同,數(shù)量不同會得到不同的結(jié)果贸呢,具體讀者可深入理解Raft算法官方說明镰烧。
步驟 | 哨1 | 哨2 | 哨3 |
---|---|---|---|
1 | 給自己投1票,將投票請求發(fā)送給哨2哨3楞陷,告訴他們自己要成為哨兵長 | ||
2 | 給自己投1票怔鳖,將投票請求發(fā)送給哨1哨2,告訴他們自己要成為哨兵長 | ||
3 | 收到哨3請求猜谚,拒絕 | 收到哨3請求败砂,同意 | |
4 | 收到哨1請求赌渣,拒絕 | ||
5 | 1票同意 | 2票同意魏铅,成為哨兵長 |
主節(jié)點選舉(哨兵切換步驟2)
- 引言:多個從節(jié)點昌犹,都有可能被選擇其中一個為主節(jié)點,隨機算法可以實現(xiàn)览芳,但是有更好的判斷策略斜姥,所以不用隨機,而這些策略受一些因素限制沧竟。
- 因素1:如果發(fā)現(xiàn)副本服務(wù)器與主服務(wù)器斷開連接的時間超過所配置的主服務(wù)器超時時間(down-after-milliseconds)的10倍铸敏,再加上從執(zhí)行故障切換的哨兵的角度來看主服務(wù)器也不可用的時間,則副本服務(wù)器被認(rèn)為不適合進行故障切換悟泵,并被跳過杈笔。
- 因素2:優(yōu)先級:replica-priority參數(shù),redis.conf中默認(rèn)為100糕非,開發(fā)人員根據(jù)當(dāng)前實例情況可設(shè)置不同的值蒙具,有較小值的節(jié)點會被設(shè)置為主節(jié)點。
- 因素3:進度大行喾省:若優(yōu)先級一樣禁筏,就看進度,進度就是所謂的偏移量衡招。偶爾受網(wǎng)絡(luò)抖動或者其它因素影響篱昔,會有一個進度快(進度新),進度慢的始腾,進度新的就做了主節(jié)點州刽。若讓進度舊的做主節(jié)點,則會讓整個鏈路整體丟失小部分?jǐn)?shù)據(jù)浪箭。
- 因素4:若權(quán)限和進度一樣怀伦,則就看Run ID,這個每個Redis實例上都會有山林,根據(jù)Run ID做ASICC的字典排序房待,最終選取一臺從節(jié)點實例做主節(jié)點。
- 當(dāng)某個從節(jié)點被哨兵選中當(dāng)做主節(jié)點時驼抹,哨兵會發(fā)送replicaof no one命令給從節(jié)點桑孩,讓其不做任何節(jié)點的從節(jié)點。
從節(jié)點換綁主節(jié)點(哨兵切換步驟3)
能走到這個階段框冀,就說明新的主節(jié)點已經(jīng)誕生流椒,其它從節(jié)點已經(jīng)有了切換主節(jié)點的目標(biāo)。
哨兵使用Redis發(fā)布/訂閱消息功能明也,在主服務(wù)器和所有從服務(wù)器中連續(xù)廣播相關(guān)配置宣虾,直到每個節(jié)點配置完成惯裕。