Redis主從複製的配置和原理

複製

爲了不單點故障,能夠將數據庫複製多個副本並部署到不一樣服務器,這樣即便有一臺服務器出現故障,其餘服務器依然能夠提供服務,Redis也提供了複製功能,會自動將更新的數據同步到其餘數據庫上。redis

配置

在複製模式中,數據庫分爲兩種:主數據庫(master)和從數據庫(slave)。數據庫

主庫能夠進行讀寫操做,當寫操做致使數據發生變化時,會自動將數據同步給從庫。從庫模式是隻讀的,並接受主庫同步過來的數據。一個主庫能夠有多個從庫,而一個從庫只能擁有一個主庫。緩存

Redis中使用複製很是容易,只須要在從庫啓動時輸入參數:bash

--slaveof host port

也能夠在配置文件中加入該配置,那麼就不須要在啓動時輸入了。服務器

下面是我修改好的從庫節點的Redis自動啓動腳本:網絡

#!/bin/sh
#chkconfig: 2345 80 90
# Simple Redis init.d script conceived to work on Linux systems
# as it does use of the /proc filesystem.
REDISPORT=6379                          #端口號,這是默認的,若是你安裝的時候不是默認端口號,則須要修改
REDISPATH=/usr/local/bin/                #redis-server啓動腳本的所在目錄,你若是忘了能夠用find / -name redis-server 或whereis redis-server找到
EXEC=${REDISPATH}/redis-server
CLIEXEC=${REDISPATH}/redis-cli
PIDFILE=/var/run/redis_${REDISPORT}.pid  #在redis.conf中可找到該路徑
CONF="/opt/redis-5.0.7/redis.conf"           #redis.conf的位置, 若是不和redis-server在同一目錄要修改爲你的redis.conf所在目錄
case "$1" in
  start)
    if [ -f $PIDFILE ]
    then
        echo "$PIDFILE exists, process is already running or crashed"
    else
        echo "Starting Redis server..."
        $EXEC $CONF --slaveof 192.168.2.101 6379
    fi
    ;;
  stop)
    if [ ! -f $PIDFILE ]
    then
        echo "$PIDFILE does not exist, process is not running"
    else
        PID=$(cat $PIDFILE)
        echo "Stopping ..."
        $CLIEXEC -p $REDISPORT shutdown
        while [ -x /proc/${PID} ]
        do
          echo "Waiting for Redis to shutdown ..."
          sleep 1
        done
        echo "Redis stopped"
    fi
    ;;
  *)
    echo "Please use start or stop as first argument"
    ;;
esac

在啓動時加入了slaveof參數。less

啓動3個Redis實例後,複製信息以下:異步

從數據庫經過在配置文件中配置slave-read-only=no來關閉從數據庫的只讀,向從數據庫中寫入數據不會同步給任何其餘的數據庫,而且當主數據庫對應數據發生變化就會覆蓋從數據庫。工具

若是當前數據庫已是其餘住數據庫的從數據庫了, 在執行新的slaveof命令後,當前數據庫會中止和原來的主數據庫的同步,轉而和新數據庫同步。性能

另外,能夠對從數據庫使用 SLAVEOF NO ONE命令,來使當前數據庫中止接收其餘數據庫的同步並轉換成主數據庫。

原理

複製過程

複製要處理的動做有:初始化、同步,斷開重連。

初始化

  • 當一個從數據庫啓動後,會向主數據庫發送SYNC命令;
  • 主數據庫接收到SYNC命令後,會開始在後臺保存快照,並將保存快照期間接收到的命令緩存起來;
  • 當完成快照後,Redis會將快照文件和全部緩存的命令發送給從數據庫;
  • 從數據庫收到後,會載入快照文件並直接收到的緩存命令;

同步

當完成初始化後,每當主數據庫收到寫命令,會就將命令同步給從數據庫,從而保證主從數據庫的數據一致。

斷開重連

當斷開重連後,Redis2.6以及以前的版本會從新進行初始化,缺點是即便從數據庫可能僅有幾條命令沒有收到,主數據庫也必需要將數據庫裏的全部數據從新傳輸給從數據庫。效率很低,在網絡環境很差時尤爲明顯。

Redis2.8版本對這種方式進行了一大改進,斷線重連可以支持增量數據傳輸,當從數據庫從新鏈接後,主數據庫只須要將斷線期間執行的命令傳輸給從數據庫便可,從而大大提升了Redis複製的實用性。

Redis採用了樂觀複製策略,容忍在必定時間內主從數據庫不一致,但最終會同步。

具體說就是Redis在的主從複製過程自己是異步的,主數據庫執行完客戶端請求的命令後會當即將執行結果返回給客戶端,並異步將命令同步給從數據庫,這裏要注意,Redis不會等待從數據庫結果後,在向客戶端返回結果(爲了性能),這裏就會產生一個主從不一致的窗口期。若是在主數據庫傳送給從數據庫命令以前,兩個數據庫網絡鏈接斷開了,此時主從數據是不一致的。

不過Redis提供了兩個配置來限制這種狀況:

min-slaves-to-write 3
min-slaves-max-lag 10

min-slaves-to-write 3 表示只有當3個或3個以上從數據庫鏈接到主數據庫時,主數據庫纔可寫。

min-slaves-max-lag 10表示容許從數據庫最長失去鏈接的時間(秒),這個特性默認是關閉的。

從數據庫持久化

爲了保證數據持久化,一種作法是開始從數據庫的持久化,同時禁用主數據庫的持久化。當從數據庫崩潰重啓後,主數據庫會自動將數據同步過來,因此無需擔憂數據丟失。

而後當主數據庫崩潰時,狀況就稍顯複雜,須要手動經過從數據庫數據恢復主數據庫數據時,要嚴格按照如下兩步進行:

  • 在從數據庫使用SLAVEOF NO ONE命令將從數據庫升級成主數據庫;
  • 啓動以前崩潰的主數據庫,而後使用SLAVEOF命令將其設置成新的主數據庫的從數據庫;

當開啓複製切關閉主數據庫的持久化功能時,必定不要使用Supervisior以及相似的進行管理工具令主數據庫崩潰後自動啓動。這會致使從數據庫中的數據被清空,形成全部數據的丟失。

由於主數據庫從新啓動後,由於沒有開啓持久化,因此數據庫中的數據是空的,這時從數據庫依然會從主數據庫中接收數據,致使從數據庫也被清空。

無硬盤複製

在複製模式下,即便已經關閉了RDB方式的持久化,只要執行復制仍是會進行快照。

在Redis2.8.18版本開始,引入了無硬盤複製選項:

repl-diskless-sync yes

開始該選項,Redis在與從數據庫進行復制初始化時將不會將快照內容存儲到磁盤,而是直接經過網絡發送給從數據庫,避免了磁盤性能瓶頸。

增量複製

從Redis2.8開始,斷開重連時,將採用增量複製,其原理是:

  1. 從數據庫存儲主數據庫的RunID,每當實例重啓後,就會自動生成一個新的RunID;
  2. 在複製同步階段,主數據庫將每個命令傳送給從數據庫時,都會同時把該命令存放到一個積壓隊列(backlog)中,並記錄下當前隊列中存放的命令的偏移量範圍;
  3. 同時,從數據庫接收到主數據庫傳來的命令時,會記錄下該命令的偏移量;

基於上面3點,Redis2.8後,當主從鏈接準備就緒後,從數據庫會發送PSYNC命令:

PSYNC 主數據庫RunID 斷開前的最新的命令偏移量

主數據庫接收到PSYNC命令後,會執行如下判斷來決定這次重連是否能夠執行增量複製:

  • 判斷RunID是否相同;
  • 命令偏移量是否在積壓隊列中,若是在則能夠執行增量複製,並將積壓隊列中響應的命令發送給從數據庫;

積壓隊列

本質上積壓隊列是一個固定長度的循環隊列,默認狀況下積壓隊列的大小爲1MB,能夠經過配置文件中的repl-backlog-size選項來調整。

能夠理解爲積壓隊列越大,其容許主從斷線的時間就越長。根據網絡狀態設置一個合理的積壓隊列很重要。積壓隊列保存的是命令,因此估算時只須要估計主從斷線的時間中,主數據庫可能執行的命令的大小便可。

與積壓隊列相關的另外一個配置選項是repl-backlog-ttl,即當全部從數據庫與主數據庫斷開鏈接後,通過多久時間能夠釋放積壓隊列的內存空間(默認1小時)。

相關文章
相關標籤/搜索