redis 系列(三)- redis主從復制和哨兵模式

普通主從架構(gòu)(讀寫分離)

問題

持久化解決了單機redis的數(shù)據(jù)保存問題,但是redis還是存在以下兩個問題:

  1. 假如某天這臺redis服務器掛了,redis服務將徹底喪失
  2. redis的讀和寫都集中到一臺機上,如果請求量比較大時,將可能被擊潰
解決

為了解決上述兩個問題,redis提供了主從架構(gòu),在主從架構(gòu)中,主服務器負責寫服務,多臺從服務器負責讀服務,緩解了單個redis服務器的壓力;主服務器將所有數(shù)據(jù)源源不斷的同步到從服務器上,一旦主服務器掛了,還有從服務器可以提供服務,redis服務將不會間斷。

特點
  1. 一臺主服務器可以連接多臺從服務器
  2. 從服務器也可以連接其他redis服務器,作為其他redis服務器的主服務器,從而形成一條鏈
  3. 主從同步是異步的,從服務器不會阻塞,但是在數(shù)據(jù)寫到從服務器內(nèi)存的這段期間,從服務器對外提供的還是舊的數(shù)據(jù)
類型
  1. 全量同步 指主服務器每次與從服務器同步都是同步全部數(shù)據(jù)。主服務器持久化數(shù)據(jù)為一個rdb文件,在此期間用緩存區(qū)把所有對主服務器的寫操作命令存儲起來了,然后再rdb傳給從服務器,再把儲存起來的命令也傳過去;從服務器從接收到的rdb文件加載數(shù)據(jù),然后再加載傳過來的命令。
  2. 部分同步 指主服務器每次與從服務器同步都是只同步增量數(shù)據(jù)。

原理

  1. 從服務器收到客戶端的saveof命令,檢查是否存儲了主服務器的運行id和復制偏移量。
  2. 如果沒綁存儲,從服務器發(fā)送 psync 1命令和主服務器進行一次全量復制,并且保存主服務器發(fā)過來的運行id 和 復制偏移量
  3. 如果有存儲了,從服務器發(fā)送運行id和復制偏移量,主服務器比較運行id是否一致,復制偏移量是否正常,如果不一致或者不正常,進行全量同步
  4. 如果運行id和復制偏移量正常,那么二者進行增量同步,同步根據(jù)傳過來復制偏移量,到復制緩存區(qū)找到對應的字節(jié),并且把該字節(jié)對應的之后的命令都同步過去
操作
  • slaveof master-ip master-port 在從服務器的redis.conf中配置主服務器的host,port
  • slave-read-only yes 從服務器默認只讀,這里可改成no為可寫(可選)
  • auth 從服務器配置主服務器的密碼(可選)
  • requirepass 主服務器配置密碼(可選)

從服務器配置好slaveof master-ip master-port 后重啟,就可以與主服務器進行同步了。

# 主服務器信息
172.17.0.3:6379> info replication
role:master
connected_slaves:2
# 兩個從服務器復制偏移量為4908
slave0:ip=172.17.0.4,port=6379,state=online,offset=4908,lag=1
slave1:ip=172.17.0.5,port=6379,state=online,offset=4908,lag=1
# 主服務器的復制偏移量也為4908
master_repl_offset:4908
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:4907

哨兵模式

作用
  • 不斷的檢查主從架構(gòu)中的redis服務中正常運行。
  • 如果出現(xiàn)問題會發(fā)信息提示你。
  • 如果主服務器掛了的話,會從從服務器中重新選舉一臺作為主服務器。
原理
  • 哨兵系統(tǒng)的分類
    哨兵系統(tǒng)可以分為單哨兵和多哨兵;單哨兵是指只有一個哨兵進程,多哨兵是指有多個哨兵進程;單哨兵掛了的話,哨兵系統(tǒng)也就掛了,多哨兵只有一個哨兵掛了不影響哨兵系統(tǒng)繼續(xù)提供服務。

  • 大概原理
    哨兵也是一個Linux的進程;各個哨兵分布在不同的Linux服務器或者同一個Linux服務器上(一個風險比較大),它們不停的監(jiān)控redis的主服務器和從服務器與其它的哨兵進程,一旦察覺redis主服務掛了,就會從從服務器中選出一個作為新的主服務器提供服務。

  • 哨兵怎么知道監(jiān)控其它哨兵和redis服務?
    哨兵每秒鐘會向其它哨兵或者redis服務器發(fā)送ping命令,根據(jù)是否有返回來判斷服務是否已經(jīng)下線。

  • 我們只是在哨兵的配置文件里配置了主服務器信息,但是它怎么知道從服務器信息?
    哨兵每十秒鐘會向redis主服務器或者從服務器執(zhí)行info replication的命令,來確認它們的主從關系。

  • 我們沒有配置其他哨兵的地址,哨兵怎么知道其他哨兵地址?
    哨兵每隔兩秒就會向redis主節(jié)點的sentinel:hello頻道發(fā)布哨兵對于主節(jié)點的判斷以及當前哨兵的信息,其它哨兵也會也會如此,并且從中獲取所有的哨兵信息。

  • 確認一臺redis服務器下線經(jīng)歷了什么流程?
    哨兵不斷的PING redis服務器,當發(fā)現(xiàn)服務器超過配置的down-after-milliseconds的時間都沒有響應,就會認為這臺主觀下線;這時候哨兵會向其他哨兵發(fā)送is-master-down-by-addr命令詢問是否可以標記為客觀下線,當認為這臺redis服務器主觀下線的哨兵超過我們配置的quorum(一般設為哨兵數(shù)量的一半加1)的值的時候,我們就可以認為這臺redis服務器客觀下線。為什么還要去詢問其他哨兵呢?這是因為哨兵和redis服務器之間沒有ping成功也可以能網(wǎng)絡之間的問題。

  • 為什么要對哨兵進行領導者選舉?
    當確定redis服務器確實掛了以后,哨兵要進行故障轉(zhuǎn)移,并且只能有一個哨兵去完成該操作,所以這時候就要選舉出一名哨兵來當此重任。那怎么選舉呢?

  1. 哨兵向其它哨兵發(fā)送is-master-down-by-addr除了確認是否機器是否可以下線以外,會有發(fā)起選舉的作用
  2. 其它哨兵收到命令以后,如果如果沒有答應其它哨兵的選舉請求就會答應該哨兵的請求
  3. 當同意(包括自己)的哨兵個數(shù)達到quorum,該哨兵就會成為領導者
  • 怎么完成故障轉(zhuǎn)移?
    當確定原來的redis主服務器已經(jīng)客觀下線以后,就會從從服務器中選出一臺作為新的主服務器,選擇順序如下:
  1. 看配置的slave-priority,如果從服務器不相等,返回最高的那臺,如果相同看下一步
  2. 看offset,即復制偏移量,如果復制偏移量不同,返回最高那臺,如果相同看下一步
  3. 看runid,程序id,runid越低可以看做是越早開啟,返回越低那臺

確定完是哪臺從服務器作為新的主服務器以后,會修改新的從服務器的slaveof與各個哨兵的監(jiān)控的主服務器的地址和ip。

舉個栗子

開啟三個redis服務,一主兩從;開啟三個哨兵;把主服務器關閉掉,查看從服務器是否會產(chǎn)生新的主服務器。

redis配置
# 主服務器 redis-6379.conf
port 6379 
daemonize yes
protected-mode no
logfile "6379.log"
dbfilename "dump-6379.rdb"

# 從服務器 redis-6380.conf
port 6380 
daemonize yes
protected-mode no
logfile "6380.log"
dbfilename "dump-6380.rdb"
slaveof 139.199.168.61 6379

# 從服務器 redis-6381.conf
port 6381 
daemonize yes
protected-mode no
logfile "6381.log"
dbfilename "dump-6381.rdb"
slaveof 139.199.168.61 6379

redis-server redis-6379.conf 啟動各個redis服務。

哨兵配置
#sentinel-26379.conf
port 26379
daemonize yes
protected-mode no
logfile "26379.log"
#監(jiān)控的redis主服務器,最后面的2就是上面提到的quorum,當哨兵響應的個數(shù)超過這個數(shù),redis服務器才會被認為是客觀下線
sentinel monitor mymaster 139.199.168.61 6379 2
#當超過這個值redis服務器對哨兵的ping不做出響應會被哨兵認為是主觀下線
sentinel down-after-milliseconds mymaster 10000

#sentinel-26380.conf
port 26380
daemonize yes
protected-mode no
logfile "26380.log"
sentinel monitor mymaster 139.199.168.61 6379 2
sentinel down-after-milliseconds mymaster 10000

#sentinel-26381.conf
port 26381
daemonize yes
protected-mode no
logfile "26381.log"
sentinel monitor mymaster 139.199.168.61 6379 2
sentinel down-after-milliseconds mymaster 10000

redis-sentinel sentinel-26379.conf 啟動各個哨兵服務。

哨兵啟動后我們可以查看sentinel-26379.conf:

port 26379
daemonize yes
protected-mode no
logfile "26379.log"
sentinel myid f65e6f01127c838e023f48b73c0f9642548a176d
sentinel monitor mymaster 139.199.168.61 6379 2
# Generated by CONFIG REWRITE
dir "/usr/local/redis-3.2.6"
sentinel down-after-milliseconds mymaster 10000
sentinel config-epoch mymaster 0
sentinel leader-epoch mymaster 0
sentinel known-slave mymaster 139.199.168.61 6381
sentinel known-slave mymaster 139.199.168.61 6380
sentinel known-sentinel mymaster 10.104.90.159 26381 a8ca7a98c5b462c5c341650386be589cf31a5761
sentinel known-sentinel mymaster 10.104.90.159 26380 eb637934a6e00a26c36fef681cb1e194127b7d2c
sentinel current-epoch 0

會發(fā)現(xiàn)哨兵已經(jīng)把redis的從服務器和其他哨兵加進來了,我們關閉主服務器kill redis主服務器的進程,再查看sentinel-26379.conf

port 26379
daemonize yes
protected-mode no
logfile "26379.log"
sentinel myid f65e6f01127c838e023f48b73c0f9642548a176d
sentinel monitor mymaster 139.199.168.61 6381 2
# Generated by CONFIG REWRITE
dir "/usr/local/redis-3.2.6"
sentinel down-after-milliseconds mymaster 10000
sentinel config-epoch mymaster 1
sentinel leader-epoch mymaster 1
sentinel known-slave mymaster 139.199.168.61 6379
sentinel known-slave mymaster 139.199.168.61 6380
sentinel known-sentinel mymaster 10.104.90.159 26381 a8ca7a98c5b462c5c341650386be589cf31a5761
sentinel known-sentinel mymaster 10.104.90.159 26380 eb637934a6e00a26c36fef681cb1e194127b7d2c
sentinel current-epoch 1

我們可以看到監(jiān)控的主服務器已經(jīng)切換成 6381而不是之前的6379了。

Java使用
  • Java使用redis的一般做法:
  Jedis jedis = new Jedis("139.199.168.61", 6379);
  System.out.println(jedis.get("hello"));

這種做法最大的弊端在于萬一139.199.168.61:6379 這個redis掛了,這個Java應用就廢了。

  • 如果我們現(xiàn)在知道了哨兵模式,我們可以改寫成以下的做法:
        //聲明一個set 存放哨兵集群的地址和端口
        Set<String> sentinels = new HashSet<String>();
        sentinels.add("139.199.168.61:26379");
        sentinels.add("139.199.168.61:26380");
        sentinels.add("139.199.168.61:26381");
        JedisSentinelPool sentinelPool = new JedisSentinelPool("mymaster", sentinels);
        // 使用sentinelPool獲取jedis對象
        Jedis master = sentinelPool.getResource();
        System.out.println(master.get("hello"));
        master.close();
        sentinelPool.destroy();

這樣子,當主服務器掛掉以后,哨兵集群會返回給程序新的主服務器地址,保證服務不會掛掉。

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

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

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