首頁  >  文章  >  資料庫  >  淺析Redis中的集群主從複製原理

淺析Redis中的集群主從複製原理

青灯夜游
青灯夜游轉載
2022-01-24 10:22:212550瀏覽

這篇文章帶大家深入理解下Redis集群主從複製原理,希望對大家有幫助!

淺析Redis中的集群主從複製原理

一、先思考一個問題,為什麼redis效能這麼高還需要分散式方案?

1、實現更高效能:高並發應用,單機效能會有影響,需要更多redis伺服器分擔壓力,實現負載平衡

2、實現高可用:如果單機,防止宕機/硬​​體故障

3、實現可擴展:單機記憶體和硬體有限制,實現橫向擴展

#冗餘或分片存儲實現如上特性。

二、主從複製-replication配置

和Kafka,Mysql,Rocketmq一樣,redis支援叢集部署,叢集節點有master和slave之分,主節點是master,從節點是slave(最新叫副本replica).slave會透過複製機制,從master同步最新的資料。 Redis提供了一個非常方便的命令開啟主從複製。 【相關建議:Redis影片教學

如何設定開啟主從複製?

以本機搭建偽集群為例,6379埠是從節點,6378作為主節點。

1、從節點redis.conf配置replicaof masterip masterport 從節點啟動後,自動連線到master節點,開始同步資料.

淺析Redis中的集群主從複製原理

如果換了新的master節點,這個配置會被重寫。

2、或在redis-server程式啟動時候指定

./redis-server --replicaof masterip masterport

3、或登入客戶端,執行下列指令

slaveof masterip masterport

注意這種方式是執行過程中修改,可以實現故障轉移

注意: 一個從節點也可以是其他節點的主節點,形成級聯複製的關係。但是其他節點也是從頂層主節點同步資料。

配置好叢集後,透過info replication查看叢集狀態

淺析Redis中的集群主從複製原理

#透過role指令,可以查看節點在叢集中的角色資訊

淺析Redis中的集群主從複製原理

注意從節點是唯讀的。寫命令會報錯。

淺析Redis中的集群主從複製原理

slave如何退出叢集?可以執行下列指令: 

slaveof no one

三、主從複製的流程

1、首先是副本-replica加入叢集 

淺析Redis中的集群主從複製原理

#2、與master建立連接,透過計時器定時檢查是否要從主節點同步數據

淺析Redis中的集群主從複製原理

原始碼說明:

//每1s执行这个方法
void replicationCron(void) {
    ...
    //检查是否需要连接到master 如果是REPL_STATE_CONNECT状态,必须连接到master
    //#define REPL_STATE_CONNECT 1  Must connect to master 
    if (server.repl_state == REPL_STATE_CONNECT) {
        serverLog(LL_NOTICE,"Connecting to MASTER %s:%d",
            server.masterhost, server.masterport);
        //和master创建连接    
        if (connectWithMaster() == C_OK) {
            serverLog(LL_NOTICE,"MASTER <-> REPLICA sync started");
        }
    }
    
    //发送ping命令给slave 
    if ((replication_cron_loops % server.repl_ping_slave_period) == 0 &&
        listLength(server.slaves))
    {
        /* Note that we don&#39;t send the PING if the clients are paused during
         * a Redis Cluster manual failover: the PING we send will otherwise
         * alter the replication offsets of master and slave, and will no longer
         * match the one stored into &#39;mf_master_offset&#39; state. */
        int manual_failover_in_progress =
            server.cluster_enabled &&
            server.cluster->mf_end &&
            clientsArePaused();

        if (!manual_failover_in_progress) {
            ping_argv[0] = createStringObject("PING",4);
            replicationFeedSlaves(server.slaves, server.slaveseldb,
                ping_argv, 1);
            decrRefCount(ping_argv[0]);
        }
    }
    
    //发送换行符到所有slave,告诉slave等待接收rdb文件
    listRewind(server.slaves,&li);
    while((ln = listNext(&li))) {
        client *slave = ln->value;

        int is_presync =
            (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START ||
            (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_END &&
             server.rdb_child_type != RDB_CHILD_TYPE_SOCKET));

        if (is_presync) {
            if (write(slave->fd, "\n", 1) == -1) {
                /* Don&#39;t worry about socket errors, it&#39;s just a ping. */
            }
        }
    }
    ...
}

3、全量複製流程-支援無磁碟複製或rdb持久化複製 

淺析Redis中的集群主從複製原理

#當slave連接到master後,使用psync(以前是sync命令,它不允許部分重新同步,所以現在改用PSYNC)命令初始化複製,將主節點replication id和處理過最大offset發送到master。

master節點擁有以下兩個屬性,一個replication id(標誌實例),一個offset(標誌寫入從節點的stream) 

Replication ID, offset

如果主節點緩衝區中沒有足夠的積壓工作,或者如果複製副本引用的是不再已知的歷史記錄(複製ID),則會發生完全重新同步 

原始碼說明:

    //没有在rdb进程,没有aof重写进程
    if (server.rdb_child_pid == -1 && server.aof_child_pid == -1) {
        time_t idle, max_idle = 0;
        int slaves_waiting = 0;
        int mincapa = -1;
        listNode *ln;
        listIter li;

        listRewind(server.slaves,&li);
        while((ln = listNext(&li))) {
            client *slave = ln->value;
            //判断slave是否是等待bgsave状态
            if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START) {
            //多久没有发送心跳或查询数据了 空闲时间间隔
                idle = server.unixtime - slave->lastinteraction;
                if (idle > max_idle) max_idle = idle;
                slaves_waiting++;
                mincapa = (mincapa == -1) ? slave->slave_capa :
                                            (mincapa & slave->slave_capa);
            }
        }

        if (slaves_waiting &&
            (!server.repl_diskless_sync ||
             max_idle > server.repl_diskless_sync_delay))
        {
            /* Start the BGSAVE. The called function may start a
             * BGSAVE with socket target or disk target depending on the
             * configuration and slaves capabilities. */
             //bgsave rdb生成
            startBgsaveForReplication(mincapa);
        }
    }

複製過程中,slave狀態轉換流程。

淺析Redis中的集群主從複製原理

4、命令傳播階段,執行完全量同步後,主從會進行指令傳播實作資料一致。

淺析Redis中的集群主從複製原理

四、複製id理解

#每次實例以主實例從頭開始重新啟動,或將複製副本提升為主實例,都會為此實例產生一個新的複製ID。如果兩個replica的複製id相同,則他們可能在不同的時間,有相同的數據,對於保存最新數據集的給定歷史記錄(複製ID),偏移量作為一個邏輯時間來理解。需要透過Replication ID, offset兩個資料來判斷。用來判斷從節點同步資料到哪了。

五、主從複製常見問題

1、slave本身有數據,會怎麼樣?

slave先刪除自身的數據,再用rdb檔案載入。

2、產生rdb檔案的過程中,客戶端寫指令怎麼處理?

儲存到記憶體快取中,rdb發送完成後傳送到slave。 

3、Redis複製如何處理key過期的? 

1、副本不會使key過期,而是等待主機使key過期。當主機使key過期(或由於LRU而將其逐出)時,它將合成一個DEL命令,該命令將傳輸到所有副本。

2、但是,由於主機驅動的expire,有時副本可能仍然具有邏輯上已過期的記憶體金鑰,因為主伺服器無法及時提供DEL命令。為了處理這個問題,副本使用它的邏輯時鐘來報告一個key不存在,只用於不違反資料集一致性的讀取操作(因為來自主伺服器的新命令將到達)

3、在Lua腳本執行期間,不執行金鑰過期。當Lua腳本運行時,從概念上講,主節點中的時間是凍結的,因此給定的鍵在腳本運行的所有時間內都將存在或不存在。這可以防止key在腳本中間過期,並且需要key才能以保證在資料集中具有相同效果的方式將相同的腳本傳送到副本。

一旦複製副本升級為主副本,它將開始獨立地使key過期,並且不需要舊主副本的任何幫助。

六、主從複製總結

1、解決了資料備份的問題,但是rdb檔案大,傳輸大文件,恢復時間也長

2、如果master異常,需要手工將replica選舉為master

3、1主多從,1主1從的情況下,還是存在單點問題 

4、Redis版本2.8.18後支援無碟複製,效能更高。

七、複製說明

1、預設用非同步複製,透過非同步確認同步的指令數

2、1個master可以有多個副本

3、副本也可以有自己的副本,從redis4.0開始,副本都會從主節點接收完全相同的複製流

4、複製既可以用於可擴展性,也可以用於只讀查詢的多個副本

更多程式相關知識,請造訪:程式設計入門! !

以上是淺析Redis中的集群主從複製原理的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除