Redis多集群主從部署

jopen 9年前發布 | 14K 次閱讀 Redis NoSQL數據庫

 

一、背景

這兩天在策劃一套Redis多機房多集群的部署方案,一主多從部署;目的是將一些重要、核心、且敏感的數據放在Redis中進行管理,通過異步消息隊列的方式,同步到db。

二、方案

1、機房與主庫的設定

  • 假設有三個機房,分別是JF-A、JF-B、JF-C,其中涉及到Redis數據寫操作的只在JF-A,其余兩個機房只存有讀操作,所以Redis主庫部署在JF-A。
  • 為了提前考慮到災備的情況,假設Redis主庫宕機,此時需要有快速的主庫恢復方案,所以在JF-A同步部署一個Redis master backup,通過運維手段,控制master與backup之間的自動切換。
  • 進一步再考慮到JF-A斷網的情況(比如光釬被挖斷了:just a joke),需要在B機房也部署一套Redis master backup,通過運維手段控制所有Redis的主從對應關系
  • </ul>

    2、各機房部署方案

    考慮到主庫的同步壓力,以及跨機房大數據進行V*N同步耗資源的情況,采取樹狀主從同步方案:

    • A機房主庫:JF-A-Master
    • A級方備用主庫:JF-A-M-Backup(slaveof JF-A-Master)
    • B級方備用主庫:JF-B-M-Backup(slaveof JF-A-Master)
    • A機房從庫1:JF-A-S-1(slaveof JF-A-Master)
    • A機房從庫2:JF-A-S-2(slaveof JF-A-S-1),S-1和S-2屬于同一臺機器的不同實例
    • B機房從庫1:JF-B-S-1(slaveof JF-A-Master)
    • B機房從庫2:JF-B-S-2(slaveof JF-B-S-1),S-1和S-2屬于同一臺機器的不同實例
    • C機房從庫1:JF-C-S-1(slaveof JF-A-Master)
    • C機房從庫2:JF-C-S-2(slaveof JF-C-S-1),S-1和S-2屬于同一臺機器的不同實例
    • </ul>

      三、通過Master配置檢測主從關系

      Redis多集群主從部署

      如上圖,從slave0~4,分別表示各機房的備用主庫以及從庫,各從庫機器上,還有另外一個從庫實例,通過樹狀方式掛載到這些從庫上。

      四、主從同步方案

      Redis的主從同步基本是采取Pull&Push搭配的方式:

      • 新增Redis從庫時,從庫采取Pull方式,發送SYNC信號從主庫拉數據(第一次)
      • 主庫有數據更新時,會遍歷connected_slaves主動將數據Push到從庫
      • 從庫維持一個與主庫的連接心跳,以此保證主庫能實時將數據同步從庫(repl-ping-slave-peroid配置,默認10s)
      • </ul>

        ------------以下內容轉載自: Redis主從實現分析原理

        一、主從實現原理

        • Slave服務器連接到Master服務器.
        • Slave服務器發送SYNC命令.
        • Master服務器備份數據庫到.rdb文件.
        • Master服務器把.rdb文件傳輸給Slave服務器.
        • Slave服務器把.rdb文件數據導入到數據庫中.
        • </ul>

          上面的這5步是同步的第一階段, 接下來在Master服務器上調用每一個命令都使用replicationFeedSlaves()來同步到Slave服務器.

          二、主從同步實現細節

          1、Slave服務器連接到Master服務器 / 發送SYNC命令

          Slave服務器通過syncWithMaster()函數來連接Master服務器(如果Master服務器需要密碼登陸的話, 先登陸), 并且發送SYNC命令請求同步, 接著打開rdb文件(用于存儲由Master發送過來的數據), 創建讀rdb的IO事件(readSyncBulkPayload). 代碼如下:

          int syncWithMaster(void) {
            ......
            //登陸master服務器
            if(server.masterauth) {
            syncWrite(fd, "AUTH xxx\r\n", strlen(server.masterauth)+7, 5);
              ......
            }
            //發送SYNC命令
            syncWrite(fd,"SYNC \r\n",7,5);
            ......
            //打開rdb文件
            dfd = open(tmpfile,O_CREAT|O_WRONLY|O_EXCL,0644);
            ......
            //創建讀rdb的IO事件
            aeCreateFileEvent(server.el, fd, AE_READABLE, readSyncBulkPayload, NULL);
            ......
            return REDIS_OK;
          }

          2、Master服務器備份數據庫到.rdb文件

          當Slave服務器發送SYNC命令到Master服務器時, Master服務器便會調用syncCommand()函數來進行同步. 同步的第一步是把數據庫的數據存儲為rdb文件, 存儲完畢后調用updateSlavesWaitingBgsave()函數來發送rdb文件給所有的Slave服務器.代碼如下:

          void syncCommand(redisClient *c) {
            //如果正在保存rdb文件
            if (server.bgsavechildpid != -1) {
              ......
              //主要判斷當前存儲rdb文件是不是由SYNC命令觸發的
              //如果當前存儲rdb文件不是由SYNC命令觸發, 則要等到下一次
              ......
            } else {//否則調用rdbSaveBackground()存儲rdb文件
              rdbSaveBackground(server.dbfilename);
            }
          }

          當rdbSaveBackground()函數執行完畢, 就會調用updateSlavesWaitingBgsave()來發送rdb文件到所有的Slave服務器, 代碼如下:
          void updateSlavesWaitingBgsave(int bgsaveerr) {
            listRewind(server.slaves,&li);
            while((ln = listNext(&li))) {
              slave->repldbfd = open(server.dbfilename,O_RDONLY);
              .......
              aeCreateFileEvent(server.el,slave->fd,AE_WRITABLE, sendBulkToSlave,slave);
            }
          }

          updateSlavesWaitingBgsave()要做的事情是, 打開rdb文件, 創建發送rdb文件IO事件(sendBulkToSlave). 而sendBulkToSlave()主要的工作就是把rdb文件發送給Slave服務器。而當Slave服務器接收rdb文件完畢之后 (readSyncBulkPayload()函數處理), 會清空原來數據庫的數據, 然后把rdb文件的數據導入到數據庫中。

          3、增量同步

          完成上面的步驟之后, 同步基本完成. 接下來的工作就是增量同步, 也就是當Master服務器有數據更新的時候, 會立刻同步到所有的Slave服務器. 由replicationFeedSlaves()函數完成。當我們在Master服務器增減數據的時候, 就會觸發replicationFeedSlaves(), 代碼如下:

          void call(redisClient *c, struct redisCommand *cmd) {
              ......
              if ((dirty || cmd->flags & REDIS_CMD_FORCE_REPLICATION) && listLength(server.slaves)) {
                  replicationFeedSlaves(server.slaves,c->db->id,c->argv,c->argc);
              }
              ......
          }

          call()函數就是當用戶執行命令的時候觸發. 而dirty表示是否有數據更新, 如果有數據更新而且slave服務器不為空, 就執行replicationFeedSlaves()。而replicationFeedSlaves()主要做的工作就是把用戶執行的命令發送到所有 的Slave服務器, 讓Slave服務器執行,這樣就可以實施同步功能。

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!