# 復制(Replication)
Note
本文檔翻譯自: [http://redis.io/topics/replication](http://redis.io/topics/replication) 。
Redis 支持簡單且易用的主從復制(master-slave replication)功能, 該功能可以讓從服務器(slave server)成為主服務器(master server)的精確復制品。
以下是關于 Redis 復制功能的幾個重要方面:
* Redis 使用異步復制。 從 Redis 2.8 開始, 從服務器會以每秒一次的頻率向主服務器報告復制流(replication stream)的處理進度。
* 一個主服務器可以有多個從服務器。
* 不僅主服務器可以有從服務器, 從服務器也可以有自己的從服務器, 多個從服務器之間可以構成一個圖狀結構。
* 復制功能不會阻塞主服務器: 即使有一個或多個從服務器正在進行初次同步, 主服務器也可以繼續處理命令請求。
* 復制功能也不會阻塞從服務器: 只要在 `redis.conf` 文件中進行了相應的設置, 即使從服務器正在進行初次同步, 服務器也可以使用舊版本的數據集來處理命令查詢。
不過, 在從服務器刪除舊版本數據集并載入新版本數據集的那段時間內, 連接請求會被阻塞。
你還可以配置從服務器, 讓它在與主服務器之間的連接斷開時, 向客戶端發送一個錯誤。
* 復制功能可以單純地用于數據冗余(data redundancy), 也可以通過讓多個從服務器處理只讀命令請求來提升擴展性(scalability): 比如說, 繁重的 [SORT](../key/sort.html#sort) 命令可以交給附屬節點去運行。
* 可以通過復制功能來讓主服務器免于執行持久化操作: 只要關閉主服務器的持久化功能, 然后由從服務器去執行持久化操作即可。
## 關閉主服務器持久化時,復制功能的數據安全
當配置Redis復制功能時,強烈建議打開主服務器的持久化功能。 否則的話,由于延遲等問題,部署的服務應該要避免自動拉起。
為了幫助理解主服務器關閉持久化時自動拉起的危險性,參考一下以下會導致主從服務器數據全部丟失的例子:
1\. 假設節點A為主服務器,并且關閉了持久化。 并且節點B和節點C從節點A復制數據
2\. 節點A崩潰,然后由自動拉起服務重啟了節點A. 由于節點A的持久化被關閉了,所以重啟之后沒有任何數據
3\. 節點B和節點C將從節點A復制數據,但是A的數據是空的, 于是就把自身保存的數據副本刪除。
在關閉主服務器上的持久化,并同時開啟自動拉起進程的情況下,即便使用Sentinel來實現Redis的高可用性,也是非常危險的。 因為主服務器可能拉起得非常快,以至于Sentinel在配置的心跳時間間隔內沒有檢測到主服務器已被重啟,然后還是會執行上面的數據丟失的流程。
無論何時,數據安全都是極其重要的,所以應該禁止主服務器關閉持久化的同時自動拉起。
## 復制功能的運作原理
無論是初次連接還是重新連接, 當建立一個從服務器時, 從服務器都將向主服務器發送一個 [SYNC](../server/sync.html#sync) 命令。
接到 [SYNC](../server/sync.html#sync) 命令的主服務器將開始執行 [BGSAVE](../server/bgsave.html#bgsave) , 并在保存操作執行期間, 將所有新執行的寫入命令都保存到一個緩沖區里面。
當 [BGSAVE](../server/bgsave.html#bgsave) 執行完畢后, 主服務器將執行保存操作所得的 `.rdb` 文件發送給從服務器, 從服務器接收這個 `.rdb` 文件, 并將文件中的數據載入到內存中。
之后主服務器會以 Redis 命令協議的格式, 將寫命令緩沖區中積累的所有內容都發送給從服務器。
你可以通過 telnet 命令來親自驗證這個同步過程: 首先連上一個正在處理命令請求的 Redis 服務器, 然后向它發送 [SYNC](../server/sync.html#sync) 命令, 過一陣子, 你將看到 telnet 會話(session)接收到服務器發來的大段數據(`.rdb` 文件), 之后還會看到, 所有在服務器執行過的寫命令, 都會重新發送到 telnet 會話來。
即使有多個從服務器同時向主服務器發送 [SYNC](../server/sync.html#sync) , 主服務器也只需執行一次 [BGSAVE](../server/bgsave.html#bgsave) 命令, 就可以處理所有這些從服務器的同步請求。
從服務器可以在主從服務器之間的連接斷開時進行自動重連, 在 Redis 2.8 版本之前, 斷線之后重連的從服務器總要執行一次完整重同步(full resynchronization)操作, 但是從 Redis 2.8 版本開始, 從服務器可以根據主服務器的情況來選擇執行完整重同步還是部分重同步(partial resynchronization)。
## 部分重同步
從 Redis 2.8 開始, 在網絡連接短暫性失效之后, 主從服務器可以嘗試繼續執行原有的復制進程(process), 而不一定要執行完整重同步操作。
這個特性需要主服務器為被發送的復制流創建一個內存緩沖區(in-memory backlog), 并且主服務器和所有從服務器之間都記錄一個復制偏移量(replication offset)和一個主服務器 ID (master run id), 當出現網絡連接斷開時, 從服務器會重新連接, 并且向主服務器請求繼續執行原來的復制進程:
* 如果從服務器記錄的主服務器 ID 和當前要連接的主服務器的 ID 相同, 并且從服務器記錄的偏移量所指定的數據仍然保存在主服務器的復制流緩沖區里面, 那么主服務器會向從服務器發送斷線時缺失的那部分數據, 然后復制工作可以繼續執行。
* 否則的話, 從服務器就要執行完整重同步操作。
Redis 2.8 的這個部分重同步特性會用到一個新增的 [PSYNC](../server/psync.html#psync) 內部命令, 而 Redis 2.8 以前的舊版本只有 [SYNC](../server/sync.html#sync) 命令, 不過, 只要從服務器是 Redis 2.8 或以上的版本, 它就會根據主服務器的版本來決定到底是使用 [PSYNC](../server/psync.html#psync) 還是 [SYNC](../server/sync.html#sync) :
* 如果主服務器是 Redis 2.8 或以上版本,那么從服務器使用 [PSYNC](../server/psync.html#psync) 命令來進行同步。
* 如果主服務器是 Redis 2.8 之前的版本,那么從服務器使用 [SYNC](../server/sync.html#sync) 命令來進行同步。
## 配置
配置一個從服務器非常簡單, 只要在配置文件中增加以下的這一行就可以了:
```
slaveof 192.168.1.1 6379
```
當然, 你需要將代碼中的 `192.168.1.1` 和 `6379` 替換成你的主服務器的 IP 和端口號。
另外一種方法是調用 [SLAVEOF](../server/slaveof.html#slaveof) 命令, 輸入主服務器的 IP 和端口, 然后同步就會開始:
```
127.0.0.1:6379> SLAVEOF 192.168.1.1 10086
OK
```
## 只讀從服務器
從 Redis 2.6 開始, 從服務器支持只讀模式, 并且該模式為從服務器的默認模式。
只讀模式由 `redis.conf` 文件中的 `slave-read-only` 選項控制, 也可以通過 [CONFIG SET](../server/config_set.html#config-set) 命令來開啟或關閉這個模式。
只讀從服務器會拒絕執行任何寫命令, 所以不會出現因為操作失誤而將數據不小心寫入到了從服務器的情況。
即使從服務器是只讀的, `DEBUG` 和 `CONFIG` 等管理式命令仍然是可以使用的, 所以我們還是不應該將服務器暴露給互聯網或者任何不可信網絡。 不過, 使用 `redis.conf` 中的命令改名選項, 我們可以通過禁止執行某些命令來提升只讀從服務器的安全性。
你可能會感到好奇, 既然從服務器上的寫數據會被重同步數據覆蓋, 也可能在從服務器重啟時丟失, 那么為什么要讓一個從服務器變得可寫呢?
原因是, 一些不重要的臨時數據, 仍然是可以保存在從服務器上面的。 比如說, 客戶端可以在從服務器上保存主服務器的可達性(reachability)信息, 從而實現故障轉移(failover)策略。
## 從服務器相關配置
如果主服務器通過 `requirepass` 選項設置了密碼, 那么為了讓從服務器的同步操作可以順利進行, 我們也必須為從服務器進行相應的身份驗證設置。
對于一個正在運行的服務器, 可以使用客戶端輸入以下命令:
```
config set masterauth <password>
```
要永久地設置這個密碼, 那么可以將它加入到配置文件中:
```
masterauth <password>
```
另外還有幾個選項, 它們和主服務器執行部分重同步時所使用的復制流緩沖區有關, 詳細的信息可以參考 Redis 源碼中附帶的 `redis.conf` 示例文件。
## 主服務器只在有至少 N 個從服務器的情況下,才執行寫操作
從 Redis 2.8 開始, 為了保證數據的安全性, 可以通過配置, 讓主服務器只在有至少 N 個當前已連接從服務器的情況下, 才執行寫命令。
不過, 因為 Redis 使用異步復制, 所以主服務器發送的寫數據并不一定會被從服務器接收到, 因此, 數據丟失的可能性仍然是存在的。
以下是這個特性的運作原理:
* 從服務器以每秒一次的頻率 PING 主服務器一次, 并報告復制流的處理情況。
* 主服務器會記錄各個從服務器最后一次向它發送 PING 的時間。
* 用戶可以通過配置, 指定網絡延遲的最大值 `min-slaves-max-lag` , 以及執行寫操作所需的至少從服務器數量 `min-slaves-to-write` 。
如果至少有 `min-slaves-to-write` 個從服務器, 并且這些服務器的延遲值都少于 `min-slaves-max-lag` 秒, 那么主服務器就會執行客戶端請求的寫操作。
你可以將這個特性看作 CAP 理論中的 C 的條件放寬版本: 盡管不能保證寫操作的持久性, 但起碼丟失數據的窗口會被嚴格限制在指定的秒數中。
另一方面, 如果條件達不到 `min-slaves-to-write` 和 `min-slaves-max-lag` 所指定的條件, 那么寫操作就不會被執行, 主服務器會向請求執行寫操作的客戶端返回一個錯誤。
以下是這個特性的兩個選項和它們所需的參數:
* `min-slaves-to-write <number of slaves>`
* `min-slaves-max-lag <number of seconds>`
詳細的信息可以參考 Redis 源碼中附帶的 `redis.conf` 示例文件。
- Redis 文檔
- 鍵空間通知(keyspace notification)
- 事務(transaction)
- 發布與訂閱(pub/sub)
- 復制(Replication)
- 通信協議(protocol)
- 持久化(persistence)
- Sentinel
- 集群教程
- Redis 集群規范
- Redis 命令參考
- Key(鍵)
- DEL
- DUMP
- EXISTS
- EXPIRE
- EXPIREAT
- KEYS
- MIGRATE
- MOVE
- OBJECT
- PERSIST
- PEXPIRE
- PEXPIREAT
- PTTL
- RANDOMKEY
- RENAME
- RENAMENX
- RESTORE
- SORT
- TYPE
- SCAN
- String(字符串)
- APPEND
- BITCOUNT
- BITOP
- DECR
- DECRBY
- GET
- GETBIT
- GETRANGE
- GETSET
- INCR
- INCRBY
- INCRBYFLOAT
- MGET
- MSET
- MSETNX
- PSETEX
- SET
- SETBIT
- SETEX
- SETNX
- SETRANGE
- STRLEN
- Hash(哈希表)
- HDEL
- HEXISTS
- HGET
- HGETALL
- HINCRBY
- HINCRBYFLOAT
- HKEYS
- HLEN
- HMGET
- HMSET
- HSET
- HSETNX
- HVALS
- HSCAN
- List(列表)
- BLPOP
- BRPOP
- BRPOPLPUSH
- LINDEX
- LINSERT
- LLEN
- LPOP
- LPUSH
- LRANGE
- LREM
- LSET
- LTRIM
- RPOP
- RPOPLPUSH
- RPUSH
- RPUSHX
- Set(集合)
- SADD
- SCARD
- SDIFF
- SDIFFSTORE
- SINTER
- SINTER
- SINTERSTORE
- SISMEMBER
- SMEMBERS
- SMOVE
- SPOP
- SRANDMEMBER
- SREM
- SUNION
- SUNIONSTORE
- SSCAN
- SortedSet(有序集合)
- ZADD
- ZCARD
- ZCOUNT
- ZINCRBY
- ZRANGE
- ZRANGEBYSCORE
- ZRANK
- ZREM
- ZREMRANGEBYRANK
- ZREMRANGEBYSCORE
- ZREVRANGE
- ZREVRANGEBYSCORE
- ZREVRANK
- ZSCORE
- ZUNIONSTORE
- ZINTERSTORE
- ZSCAN
- Pub/Sub(發布/訂閱)
- PSUBSCRIBE
- PUBLISH
- PUBSUB
- PUNSUBSCRIBE
- SUBSCRIBE
- UNSUBSCRIBE
- Transaction(事務)
- DISCARD
- EXEC
- MULTI
- UNWATCH
- WATCH
- Script(腳本)
- EVAL
- EVALSHA
- SCRIPT EXISTS
- SCRIPT FLUSH
- SCRIPT KILL
- SCRIPT LOAD
- Connection(連接)
- AUTH
- ECHO
- PING
- QUIT
- SELECT
- Server(服務器)
- BGREWRITEAOF
- BGSAVE
- CLIENT GETNAME
- CLIENT KILL
- CLIENT LIST
- CLIENT SETNAME
- CONFIG GET
- CONFIG RESETSTAT
- CONFIG REWRITE
- CONFIG SET
- DBSIZE
- DEBUG OBJECT
- DEBUG SEGFAULT
- FLUSHALL
- FLUSHDB
- INFO
- LASTSAVE
- MONITOR
- PSYNC
- SAVE
- SHUTDOWN
- SLAVEOF
- SLOWLOG
- SYNC
- TIME
- 關于