摸透 Redis 主从复制、哨兵、Cluster 三种模式
?
概述
Redis作為緩存的高效中間件,在我們日常的開發中被頻繁的使用,今天就來說一說Redis的四種模式,分別是「單機版、主從復制、哨兵、以及集群模式」。
可能,在一般公司的程序員使用單機版基本都能解決問題,在Redis的官網給出的數據是10W QPS,這對于應付一般的公司綽綽有余了,再不行就來個主從模式,實現讀寫分離,性能又大大提高。
但是,我們作為有抱負的程序員,僅限于單機版和主從模式的crud是不行的,至少也要了解「哨兵」和「集群模式」的原理,這樣面試的時候才能和面試官扯皮啊。
之前對于Redis方面也是寫了比較多的文章,如:「Redis的基本數據類型和底層的實現原理、事務、持久化、分布式鎖、訂閱預發布」等,可以說是比較全面的教程了,這篇講完基本就全了,我會把文章系統的整理成pdf,分享給大家。
先來個整理的Redis大綱,可能還有不完整的地方,若是有不完整的,可以在留言區補充,我后續會加進去。
單機
單機版的Redis就比較簡單了,基本90%的程序員都是用過
優點
單機版的Redis也有很多優點,比如實現實現簡單、維護簡單、部署簡單、維護成本非常低,不需要其它額外的開支。
缺點
但是,因為是單機版的Redis所以也存在很多的問題,比如最明顯的單點故障問題,一個Redis掛了,所有的請求就會直接打在了DB上。
并且一個Redis抗并發數量也是有限的,同時要兼顧讀寫兩種請求,只要訪問量一上來,Redis就受不了了,另一方面單機版的Redis數據量存儲也是有限的,數據量一大,再重啟Redis的時候,就會非常的慢,所以局限性也是比較大的。
由于,單機版的Redis在并發量比較大的時候,并且需要較高性能和可靠性的時候,單機版基本就不適合了,于是就出現了「主從模式」。
主從模式
原理
主從的原理還算是比較簡單的,一主多從,「主數據庫(master)可以讀也可以寫(read/write),從數據庫僅讀(only read)」。
但是,主從模式一般實現「讀寫分離」,「主數據庫僅寫(only write)」,減輕主數據庫的壓力,下面一張圖搞懂主從模式的原理:
主從模式原理就是那么簡單,那他執行的過程(工作機制)又是怎么樣的呢?再來一張圖:
當開啟主從模式的時候,他的具體工作機制如下:
當slave啟動后會向master發送SYNC命令,master節點收到從數據庫的命令后通過bgsave保存快照(「RDB持久化」),并且期間的執行的些命令會被緩存起來。
然后master會將保存的快照發送給slave,并且繼續緩存期間的寫命令。
slave收到主數據庫發送過來的快照就會加載到自己的數據庫中。
最后master講緩存的命令同步給slave,slave收到命令后執行一遍,這樣master與slave數據就保持一致了。
優點
之所以運用主從,是因為主從一定程度上解決了單機版并發量大,導致請求延遲或者redis宕機服務停止的問題。
從數據庫分擔主數據庫的讀壓力,若是主數據庫是只寫模式,那么實現讀寫分離,主數據庫就沒有了讀壓力了。
另一方面解決了單機版單點故障的問題,若是主數據庫掛了,那么從數據庫可以隨時頂上來,綜上來說,主從模式一定程度上提高了系統的可用性和性能,是實現哨兵和集群的基礎。
主從同步以異步方式進行同步,期間Redis仍然可以響應客戶端提交的查詢和更新的請求。
缺點
主從模式好是好,他也有自己的缺點,比如數據的一致性問題,假如主數據庫寫操作完成,那么他的數據會被復制到從數據庫,若是還沒有即使復制到從數據庫,讀請求又來了,此時讀取的數據就不是最新的數據。
若是從主同步的過程網絡出故障了,導致主從同步失敗,也會出現問題數據一致性的問題。
主從模式不具備自動容錯和恢復的功能,一旦主數據庫,從節點晉升為主數據庫的過程需要人為操作,維護的成本就會升高,并且主節點的寫能力、存儲能力都會受到限制。
實操搭建
安裝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文件復制兩份slave.conf和slave2.conf并修改配置
注意:不要用? bind 127.0.0.1,也不要這樣配置:bind 127.0.0.1 10.234.1.10?而要用?bind 10.234.1.10 127.0.0.1
# 服務器端口號,主從分別修改為7001 7002 7003 port 7001 # 使得Redis可以跨網絡訪問 bind 0.0.0.0 # 配置reids的密碼 requirepass "111111" # 下面兩個配置只需要配置從節點(slave) # 配置主服務器地址、端口號 replicaof 127.0.0.1 7001 # 主服務器密碼 masterauth "111111"分別啟動這三個Redis服務
$ ./src/redis-server redis.conf $ ./src/redis-server slave.conf $ ./src/redis-server slave2.conf使用redis-cli工具連接redis服務查看主從節點是否搭建成功
$ ./src/redis-cli -h <主機名> -p <端口號> $ auth <password> $ info replication看到類似如下所示信息則主從搭建成功
############主節點(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 " ############從節點(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?
哨兵模式
原理
哨兵模式是主從的升級版,因為主從的出現故障后,不會自動恢復,需要人為干預,這就很蛋疼啊。
在主從的基礎上,實現哨兵模式就是為了監控主從的運行狀況,對主從的健壯進行監控,就好像哨兵一樣,只要有異常就發出警告,對異常狀況進行處理。
?
所以,總的概括來說,哨兵模式有以下的優點(功能點):
「監控」:監控master和slave是否正常運行,以及哨兵之間也會相互監控
「自動故障恢復」:當master出現故障的時候,會自動選舉一個slave作為master頂上去。
哨兵模式的監控配置信息,是通過配置從數據庫的sentinel monitor <master-name> <ip> <redis-port> <quorum>?來指定的,比如:
//?mymaster?表示給master數據庫定義了一個名字,后面的是master的ip和端口,1表示至少需要一個Sentinel進程同意才能將master判斷為失效,如果不滿足這個條件,則自動故障轉移(failover)不會執行
sentinel?monitor?mymaster?127.0.0.1?6379?1
節點通信
當哨兵啟動后,會與master建立一條連接,用于訂閱master的_sentinel_:hello頻道。該頻道用于獲取監控該master的其它哨兵的信息。哨兵定期向_sentinel_:hello頻道發送自己的信息,以便其它的哨兵能夠訂閱獲取自己的信息,發送的內容包含「哨兵的ip和端口、運行id、配置版本、master名字、master的ip端口還有master的配置版本」等信息。
哨兵還會與mastter建立一條獲取master信息的連接,定時向master發送INFO命令。「當哨兵與master建立連接后,定期會向(10秒一次)master和slave發送INFO命令,若是master被標記為主觀下線,頻率就會變為1秒一次。」
以及,「定期的向master、slave和其它哨兵發送PING命令(每秒一次),以便檢測對象是否存活」,若是對方接收到了PING命令,無故障情況下,會回復PONG命令。
所以,哨兵通過建立這兩條連接、通過定期發送INFO、PING命令來實現哨兵與哨兵、哨兵與master之間的通信。
這里涉及到一些概念需要理解,INFO、PING、PONG等命令,后面還會有MEET、FAIL命令,以及主觀下線,當然還會有客觀下線,這里主要說一下這幾個概念的理解:
INFO:該命令可以獲取主從數據庫的最新信息,可以實現新結點的發現。
PING:該命令被使用最頻繁,該命令封裝了自身節點和其它節點的狀態數據。
PONG:當節點收到MEET和PING,會回復PONG命令,也把自己的狀態發送給對方。
MEET:該命令在新結點加入集群的時候,會向老節點發送該命令,表示自己是個新人。
FAIL:當節點下線,會向集群中廣播該消息。
上線和下線
當哨兵與master相通之后就會定期一直保持聯系,若是某一時刻哨兵發送的PING在指定時間內沒有收到回復(sentinel down-after-milliseconds master-name milliseconds?配置),那么發送PING命令的哨兵就會認為該master「主觀下線」(Subjectively Down)。
因為有可能是哨兵與該master之間的網絡問題造成的,而不是master本身的原因,所以哨兵同時會詢問其它的哨兵是否也認為該master下線,若是認為該節點下線的哨兵達到一定的數量(「前面的quorum字段配置」),就會認為該節點「客觀下線」(Objectively Down)。
若是沒有足夠數量的sentinel同意該master下線,則該master客觀下線的標識會被移除;若是master重新向哨兵的PING命令回復了客觀下線的標識也會被移除。
選舉算法
當master被認為客觀下線后,又是怎么進行故障恢復的呢?原來哨兵中首先選舉出一個老大哨兵來進行故障恢復,選舉老大哨兵的算法叫做「Raft算法」:
發現master下線的哨兵(sentinelA)會向其它的哨兵發送命令進行拉票,要求選擇自己為哨兵大佬。
若是目標哨兵沒有選擇其它的哨兵,就會選擇該哨兵(sentinelA)為大佬。
若是選擇sentinelA的哨兵超過半數(半數原則),該大佬非sentinelA莫屬。
如果有多個哨兵同時競選,并且可能存在票數一致的情況,就會等待下次的一個隨機時間再次發起競選請求,進行新的一輪投票,直到大佬被選出來。
選出大佬哨兵后,大佬哨兵就會對故障進行自動回復,從slave中選出一名slave作為主數據庫,選舉的規則如下所示:
所有的slave中slave-priority優先級最高的會被選中。
若是優先級相同,會選擇偏移量最大的,因為偏移量記錄著數據的復制的增量,越大表示數據越完整。
若是以上兩者都相同,選擇ID最小的。
通過以上的層層篩選最終實現故障恢復,當選的slave晉升為master,其它的slave會向新的master復制數據,若是down掉的master重新上線,會被當作slave角色運行。
優點
哨兵模式是主從模式的升級版,所以在系統層面提高了系統的可用性和性能、穩定性。當master宕機的時候,能夠自動進行故障恢復,需不要人為的干預。
哨兵與哨兵之間、哨兵與master之間能夠進行及時的監控,心跳檢測,及時發現系統的問題,這都是彌補了主從的缺點。
缺點
哨兵一主多從的模式同樣也會遇到寫的瓶頸,已經存儲瓶頸,若是master宕機了,故障恢復的時間比較長,寫的業務就會受到影響。
增加了哨兵也增加了系統的復雜度,需要同時維護哨兵模式。
實操搭建
新開一臺服務器,并按上面的步驟下載安裝Redis。
將sentinel.conf文件復制兩份為sentinel2.conf、sentinel3.conf,并分別修改配置
# 三個配置文件分別配置不同的端口號 port <端口號> # 設置為后臺啟動 daemonize yes # 主節點的名稱(可以自定義,與后面的配置保持一致即可) # 主機地址 # 端口號 # 數量(2代表只有兩個或兩個以上的哨兵認為主服務器不可用的時候,才會進行failover操作) sentinel monitor mymaster 127.0.0.1 6379 2 # 多長時間沒有響應認為主觀下線(SDOWN) sentinel down-after-milliseconds mymaster 60000 # 表示如果15秒后,mysater仍沒活過來,則啟動failover,從剩下從節點序曲新的主節點 sentinel failover-timeout mymaster 15000 # 指定了在執行故障轉移時, 最多可以有多少個從服務器同時對新的主服務器進行同步, 這個數字越小, 完成故障轉移所需的時間就越長 sentinel parallel-syncs mymaster 1啟動三個sentinel
$ ./src/server-sentinel sentinel.conf $ ./src/server-sentinel sentinel2.conf $ ./src/server-sentinel sentinel3.conf然后手動關閉主節點的redis服務,并查看兩個slave信息是否有一個變成了master。
程序中使用
SpringBoot連接Redis主從集群配置
spring:redis:sentinel:master: mymasternodes: 192.168.1.167:26379,192.168.1.167:26380,192.168.1.167:26381host: 192.168.1.164port: 7003database: 0password: <password>?
Redis集群(Redis-Cluster)
Cluster模式
最后,Cluster是真正的集群模式了,哨兵解決和主從不能自動故障恢復的問題,但是同時也存在難以擴容以及單機存儲、讀寫能力受限的問題,并且集群之前都是一臺redis都是全量的數據,這樣所有的redis都冗余一份,就會大大消耗內存空間。
集群模式實現了Redis數據的分布式存儲,實現數據的分片,每個redis節點存儲不同的內容,并且解決了在線的節點收縮(下線)和擴容(上線)問題。
集群模式真正意義上實現了系統的高可用和高性能,但是集群同時進一步使系統變得越來越復雜,接下來我們來詳細的了解集群的運作原理。
數據分區原理
集群的原理圖還是很好理解的,在Redis集群中采用的是虛擬槽分區算法,會把redis集群分成16384 個槽(0 -16383)。
比如:下圖所示三個master,會把0 -16383范圍的槽可能分成三部分(0-5000)、(5001-11000)、(11001-16383)分別數據三個緩存節點的槽范圍。
當客戶端請求過來,會首先通過對key進行CRC16 校驗并對 16384 取模(CRC16(key)%16383)計算出key所在的槽,然后再到對應的槽上進行取數據或者存數據,這樣就實現了數據的訪問更新。
之所以進行分槽存儲,是將一整堆的數據進行分片,防止單臺的redis數據量過大,影響性能的問題。
節點通信
節點之間實現了將數據進行分片存儲,那么節點之間又是怎么通信的呢?這個和前面哨兵模式講的命令基本一樣。
首先新上線的節點,會通過 Gossip 協議向老成員發送Meet消息,表示自己是新加入的成員。
老成員收到Meet消息后,在沒有故障的情況下會恢復PONG消息,表示歡迎新結點的加入,除了第一次發送Meet消息后,之后都會發送定期PING消息,實現節點之間的通信。
通信的過程中會為每一個通信的節點開通一條tcp通道,之后就是定時任務,不斷的向其它節點發送PING消息,這樣做的目的就是為了了解節點之間的元數據存儲情況,以及健康狀況,以便及時發現問題。
數據請求
上面說到了槽信息,在Redis的底層維護了unsigned char myslots[CLUSTER_SLOTS/8]?一個數組存放每個節點的槽信息。因為他是一個二進制數組,只有存儲0和1值,如下圖所示:
這樣數組只表示自己是否存儲對應的槽數據,若是1表示存在該數據,0表示不存在該數據,這樣查詢的效率就會非常的高,類似于布隆過濾器,二進制存儲。
比如:集群節點1負責存儲0-5000的槽數據,但是此時只有0、1、2存儲有數據,其它的槽還沒有存數據,所以0、1、2對應的值為1。
并且,每個redis底層還維護了一個clusterNode數組,大小也是16384,用于儲存負責對應槽的節點的ip、端口等信息,這樣每一個節點就維護了其它節點的元數據信息,便于及時的找到對應的節點。
當新結點加入或者節點收縮,通過PING命令通信,及時的更新自己clusterNode數組中的元數據信息,這樣有請求過來也就能及時的找到對應的節點。
有兩種其它的情況就是,若是請求過來發現,數據發生了遷移,比如新節點加入,會使舊的緩存節點數據遷移到新結點。
請求過來發現舊節點已經發生了數據遷移并且數據被遷移到新結點,由于每個節點都有clusterNode信息,通過該信息的ip和端口。此時舊節點就會向客戶端發一個MOVED 的重定向請求,表示數據已經遷移到新結點上,你要訪問這個新結點的ip和端口就能拿到數據,這樣就能重新獲取到數據。
倘若正在發生數據遷移呢?舊節點就會向客戶端發送一個ASK 重定向請求,并返回給客戶端遷移的目標節點的ip和端口,這樣也能獲取到數據。
擴容和收縮
擴容和收縮也就是節點的上線和下線,可能節點發生故障了,故障自動恢復的過程(節點收縮)。
節點的收縮和擴容時,會重新計算每一個節點負責的槽范圍,并根據虛擬槽算法,將對應的數據更新到對應的節點。
還有前面的講的新加入的節點會首先發送Meet消息,詳細可以查看前面講的內容,基本一樣的模式。
以及發生故障后,哨兵老大節點的選舉,master節點的重新選舉,slave怎樣晉升為master節點,可以查看前面哨兵模式選舉過程。
優點
集群模式是一個無中心的架構模式,將數據進行分片,分布到對應的槽中,每個節點存儲不同的數據內容,通過路由能夠找到對應的節點負責存儲的槽,能夠實現高效率的查詢。
并且集群模式增加了橫向和縱向的擴展能力,實現節點加入和收縮,集群模式是哨兵的升級版,哨兵的優點集群都有。
缺點
緩存的最大問題就是帶來數據一致性問題,在平衡數據一致性的問題時,兼顧性能與業務要求,大多數都是以最終一致性的方案進行解決,而不是強一致性。
并且集群模式帶來節點數量的劇增,一個集群模式最少要6臺機,因為要滿足半數原則的選舉方式,所以也帶來了架構的復雜性。
slave只充當冷備,并不能緩解master的讀的壓力。
搭建步驟
Redis5.0之后的版本放棄了 Ruby 的集群方式,改為使用 C 語言編寫的redis-cli的方式,使集群的構建方式復雜度大大降低。
下載安裝Redis(見主從復制模式的搭建步驟)。
創建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配置文件內容:
port 7001 # 端口,每個配置文件不同7001-7006 cluster-enabled yes # 啟用集群模式 cluster-config-file nodes.conf #節點配置文件 cluster-node-timeout 5000 # 超時時間 appendonly yes # 打開aof持久化 daemonize yes # 后臺運行 protected-mode no # 非保護模式 pidfile /var/run/redis_7001.pid # 根據端口修改啟動6個Redis節點。
./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服務是相互獨立運行的,通過以下命令配置集群。
./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節點,3個slave節點,16384個槽點平均分配到了3個master節點上。圖中很長的一串字母數字的組合(07000b3a90......)為節點的ID。后面對節點的操作中會用到。
集群重新分片
如果對默認的平均分配不滿意,我們可以對集群進行重新分片。執行如下命令,只需要指定集群中的其中一個節點地址即可,它會自動找到集群中的其他節點。(如果設置了密碼則需要加上 -a <password>,沒有密碼則不需要,后面的命令我會省略這個,設置了密碼的自己加上就好)。
./src/redis-cli -a <password> --cluster reshard 127.0.0.1:7001輸入你想重新分配的哈希槽數量
How many slots do you want to move (from 1 to 16384)?輸入你想接收這些哈希槽的節點ID
What is the receiving node ID?輸入想從哪個節點移動槽點,選擇all表示所有其他節點,也可以依次輸入節點ID,以done結束。
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執行重新分片
Do you want to proceed with the proposed reshard plan (yes/no)?自動故障轉移
當運行中的master節點掛掉了,集群會在該master節點的slave節點中選出一個作為新的master節點。這里不做演示。
手動故障轉移
有的時候在主節點沒有任何問題的情況下強制手動故障轉移也是很有必要的,比如想要升級主節點的Redis進程,我們可以通過故障轉移將其轉為slave再進行升級操作來避免對集群的可用性造成很大的影響。
Redis集群使用 cluster failover 命令來進行故障轉移,不過要在被轉移的主節點的從節點上執行該命令(使用redis-cli連接slave節點并執行 cluster failover命令進行轉移)。
添加一個主節點
按之前的方式再復制一份配置文件,并修改配置
/usr/local/redis-5.0.4/redis-cluster-conf/7007/redis.conf然后啟動該Redis服務,執行以下命令將該節點添加到集群中去
./src/redis-cli --cluster add-node 127.0.0.1:7007 127.0.0.1:7001第一個參數為新增加的節點的IP和端口,第二個參數為任意一個已經存在的節點的IP和端口。
此時該新節點已經成為集群的一份子
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但是該節點沒有包含任何的哈希槽,所以沒有數據會存到該主節點。我們可以通過上面的集群重新分片給該節點分配哈希槽,那么該節點就成為了一個真正的主節點了。
添加一個從節點
跟添加主節點一樣添加一個節點7008,然后連接上該節點并執行如下命令
cluster replicate <nodeId>這樣就可以指定該節點成為哪個節點的從節點。
節點的移除
可以使用如下命令來移除節點
./src/redis-cli --cluster del-node 127.0.0.1:7001 <nodeId>第一個參數是任意一個節點的地址,第二個參數是你想要移除的節點ID。如果是移除主節點,需要確保這個節點是空的,如果不是空的則需要將這個節點上的數據重新分配到其他節點上。
程序中使用
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:7006database: 0password: <password>Redis常見數據丟失情況分析及解決
情況分析
(1)異步復制導致的數據丟失
因為master->slave的數據同步是異步的,所以可能存在部分數據還沒有同步到slave,master就宕機了,此時這部分數據就丟失了。
(2)腦裂導致的數據丟失
當master所在的機器突然脫離的正常的網絡,與其他slave、sentinel失去了連接,但是master還在運行著。此時sentinel就會認為master宕機了,會開始選舉把slave提升為新的master,這個時候集群中就會出現兩個master,也就是所謂的腦裂。
此時雖然產生了新的master節點,但是客戶端可能還沒來得及切換到新的master,會繼續向舊的master寫入數據。
當網絡恢復正常時,舊的master會變成新的master的從節點,自己的數據會清空,重新從新的master上復制數據。
解決方案
Redis提供了這兩個配置用來降低數據丟失的可能性
min-slaves-to-write 1 min-slaves-max-lag 10上面兩行配置的意思是,要求至少有1個slave,數據復制和同步的延遲不能超過10秒,如果不符合這個條件,那么master將不會接收任何請求。
(1)減少異步復制的數據丟失
有了min-slaves-max-lag這個配置,就可以確保,一旦slave復制數據和ack延時太長,就認為master宕機后損失的數據太多了,那么就拒絕寫請求,這樣可以把master宕機時由于部分數據未同步到slave導致的數據丟失降低到可控范圍內。
(2)減少腦裂的數據丟失
如果一個master出現了腦裂,跟其他slave丟了連接,那么上面兩個配置可以確保,如果不能繼續給指定數量的slave發送數據,而且slave超過10秒沒有給自己ack消息,那么就直接拒絕客戶端的寫請求
這樣腦裂后的舊master就不會接受client的新數據,也就避免了數據丟失。
Redis并不能保證數據的強一致性,看官方文檔的說明
?
參考文檔:
http://www.redis.cn/topics/cluster-tutorial.html
總結
以上是生活随笔為你收集整理的摸透 Redis 主从复制、哨兵、Cluster 三种模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: go应用程序写kafka阻塞(大坑)
- 下一篇: Kafka配置offsets.reten