1)使用異步複製。
2)一個主服務器能夠有多個從服務器。
3)從服務器也能夠有本身的從服務器。
4)複製功能不會阻塞主服務器。
5)能夠經過複製功能來讓主服務器免於執行持久化操做,由從服務器去執行持久化操做便可。redis
1)Redis 使用異步複製。從 Redis2.8開始,從服務器會以每秒一次的頻率向主服務器報告複製流(replication stream)的處理進度。數據庫
2)一個主服務器能夠有多個從服務器。安全
3)不只主服務器能夠有從服務器,從服務器也能夠有本身的從服務器,多個從服務器之間能夠構成一個圖狀結構。服務器
4)複製功能不會阻塞主服務器:即便有一個或多個從服務器正在進行初次同步, 主服務器也能夠繼續處理命令請求。網絡
5)複製功能也不會阻塞從服務器:只要在 redis.conf 文件中進行了相應的設置, 即便從服務器正在進行初次同步, 服務器也可使用舊版本的數據集來處理命令查詢。架構
6)在從服務器刪除舊版本數據集並載入新版本數據集的那段時間內,鏈接請求會被阻塞。異步
7)還能夠配置從服務器,讓它在與主服務器之間的鏈接斷開時,向客戶端發送一個錯誤。scala
8)複製功能能夠單純地用於數據冗餘(data redundancy),也能夠經過讓多個從服務器處理只讀命令請求來提高擴展性(scalability): 好比說,繁重的SORT命令能夠交給附屬節點去運行。3d
9)能夠經過複製功能來讓主服務器免於執行持久化操做:只要關閉主服務器的持久化功能,而後由從服務器去執行持久化操做便可。code

1.當配置Redis複製功能時,強烈建議打開主服務器的持久化功能。 不然的話,因爲延遲等問題,部署的服務應該要避免自動拉起。
2.爲了幫助理解主服務器關閉持久化時自動拉起的危險性,參考一下如下會致使主從服務器數據所有丟失的例子:
1)假設節點A爲主服務器,而且關閉了持久化。而且節點B和節點C從節點A複製數據
2)節點A崩潰,而後由自動拉起服務重啓了節點A. 因爲節點A的持久化被關閉了,因此重啓以後沒有任何數據
3)節點B和節點C將從節點A複製數據,可是A的數據是空的,因而就把自身保存的數據副本刪除。
結論:
1)在關閉主服務器上的持久化,並同時開啓自動拉起進程的狀況下,即使使用Sentinel來實現Redis的高可用性,也是很是危險的。由於主服務器可能拉起得很是快,以致於Sentinel在配置的心跳時間間隔內沒有檢測到主服務器已被重啓,而後仍是會執行上面的數據丟失的流程。
2)不管什麼時候,數據安全都是極其重要的,因此應該禁止主服務器關閉持久化的同時自動拉起。

圖1·由一位靈魂畫師畫出的Redis主從複製原理圖
1)從服務器向主服務器發送 SYNC 命令。
2)接到 SYNC 命令的主服務器會調用BGSAVE 命令,建立一個 RDB 文件,並使用緩衝區記錄接下來執行的全部寫命令。
3)當主服務器執行完 BGSAVE 命令時,它會向從服務器發送 RDB 文件,而從服務器則會接收並載入這個文件。
4)主服務器將緩衝區儲存的全部寫命令發送給從服務器執行。
#開啓主從複製(在從庫上執行) 127.0.0.1:6379> SLAVEOF 10.0.0.51 6379 OK #查看主從信息 127.0.0.1:6379> INFO replication # Replication role:slave //角色是從庫 master_host:10.0.0.51 //主庫IP是10.0.0.51 master_port:6379 master_link_status:down master_last_io_seconds_ago:-1 master_sync_in_progress:0 slave_repl_offset:1 master_link_down_since_seconds:1540419761 slave_priority:100 slave_read_only:1 connected_slaves:0 master_repl_offset:0 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0

在主從服務器完成同步以後,主服務器每執行一個寫命令,它都會將被執行的寫命令發送給從服務器執行,這個操做被稱爲「命令傳播」(command propagate)。
命令傳播是一個持續的過程:只要複製仍在繼續,命令傳播就會一直進行,使得主從服務器的狀態能夠一直保持一致。
1)在 Redis2.8版本以前,斷線以後重連的從服務器總要執行一次完整重同步(fullresynchronization)操做。
2)從 Redis2.8開始,Redis使用PSYNC命令代替SYNC命令。
3)PSYNC比起SYNC的最大改進在於PSYNC實現了部分重同步(partial resync)特性:
在主從服務器斷線而且從新鏈接的時候,只要條件容許,PSYNC可讓主服務器只向從服務器同步斷線期間缺失的數據,而不用從新向從服務器同步整個數據庫。
注:
PSYNC這個特性須要主服務器爲被髮送的複製流建立一個內存緩衝區(in-memory backlog), 而且主服務器和全部從服務器之間都記錄一個複製偏移量(replication offset)和一個主服務器 ID(master run id),當出現網絡鏈接斷開時,從服務器會從新鏈接,而且向主服務器請求繼續執行原來的複製進程:
1)若是從服務器記錄的主服務器ID和當前要鏈接的主服務器的ID相同,而且從服務器記錄的偏移量所指定的數據仍然保存在主服務器的複製流緩衝區裏面,那麼主服務器會向從服務器發送斷線時缺失的那部分數據,而後複製工做能夠繼續執行。
2)不然的話,從服務器就要執行完整重同步操做。

若是咱們仔細地觀察整個斷線並重連的過程,就會發現:
從服務器在斷線以前已經擁有主服務器的絕大部分數據,要讓主從服務器從新回到一致狀態,從服務器真正須要的是 k1008七、k10088和k10089這三個鍵的數據,而不是主服務器整個數據庫的數據。SYNC 命令在處理斷線並重連時的作法——將主服務器的整個數據庫從新同步給從服務器,是極度浪費的!

1)PSYNC只會將從服務器斷線期間缺失的數據發送給從服務器。兩個例子的狀況是相同的,但SYNC 須要發送包含整個數據庫的 RDB 文件,而PSYNC 只須要發送三個命令。
2)若是主從服務器所處的網絡環境並不那麼好的話(常常斷線),那麼請儘可能使用 Redis 2.8 或以上版本:經過使用 PSYNC 而不是 SYNC 來處理斷線重鏈接,能夠避免由於重複建立和傳輸 RDB文件而浪費大量的網絡資源、計算資源和內存資源。

1)在讀寫分離環境下,客戶端向主服務器發送寫命令 SET k10086 v10086,主服務器在執行這個寫命令以後,向客戶端返回回覆,並將這個寫命令傳播給從服務器。
2)接到回覆的客戶端繼續向從服務器發送讀命令 GET k10086 ,而且由於網絡狀態的緣由,客戶端的 GET命令比主服務器傳播的 SET 命令更快到達了從服務器。
3)由於從服務器鍵k10086的值還未被更新,因此客戶端在從服務器讀取到的將是一個錯誤(過時)的k10086值。
Redis是怎麼保證數據安全的呢?
1)主服務器只在有至少N個從服務器的狀況下,才執行寫操做
2)從Redis 2.8開始,爲了保證數據的安全性,能夠經過配置,讓主服務器只在有至少N個當前已鏈接從服務器的狀況下,才執行寫命令。
3)不過,由於 Redis 使用異步複製,因此主服務器發送的寫數據並不必定會被從服務器接收到,所以, 數據丟失的可能性仍然是存在的。
4)經過如下兩個參數保證數據的安全:
#執行寫操做所需的至少從服務器數量 min-slaves-to-write <number of slaves> #指定網絡延遲的最大值 min-slaves-max-lag <number of seconds>
這個特性的運做原理:
1)從服務器以每秒一次的頻率 PING 主服務器一次, 並報告複製流的處理狀況。主服務器會記錄各個從服務器最後一次向它發送 PING 的時間。用戶能夠經過配置, 指定網絡延遲的最大值 min-slaves-max-lag , 以及執行寫操做所需的至少從服務器數量 min-slaves-to-write 。
2)若是至少有 min-slaves-to-write 個從服務器, 而且這些服務器的延遲值都少於 min-slaves-max-lag 秒, 那麼主服務器就會執行客戶端請求的寫操做。你能夠將這個特性看做 CAP 理論中的 C 的條件放寬版本: 儘管不能保證寫操做的持久性, 但起碼丟失數據的窗口會被嚴格限制在指定的秒數中。
3)另外一方面, 若是條件達不到 min-slaves-to-write 和 min-slaves-max-lag 所指定的條件, 那麼寫操做就不會被執行, 主服務器會向請求執行寫操做的客戶端返回一個錯誤。
環境
角色 | 主機 IP | 端口 |
---|---|---|
主庫(master) | db01 | 10.0.0.51 6379 |
從庫(slave01) | db01 | 10.0.0.51 6380 |
主庫(slave02) | db01 | 10.0.0.51 6381 |
配置多實例
#建立多實例目錄 [root@db01 ~]# /etc/redis/{6379,6380,6381} #編輯多實例配置文件 [root@db01 ~]# cat /etc/redis/6379/redis.conf /etc/redis/6380/redis.conf /etc/redis/6381/redis.conf #redis 6379 配置文件 port 6379 daemonize yes pidfile /etc/redis/6379/redis.pid loglevel notice logfile /etc/redis/6379/redis.log dbfilename dump.rdb dir /etc/redis/6379 bind 127.0.0.1 10.0.0.51 protected-mode no #redis 6380 配置文件 port 6380 daemonize yes pidfile /etc/redis/6380/redis.pid loglevel notice logfile /etc/redis/6380/redis.log dbfilename dump.rdb dir /etc/redis/6380 bind 127.0.0.1 10.0.0.51 protected-mode no #redis 6381 配置文件 port 6381 daemonize yes pidfile /etc/redis/6381/redis.pid loglevel notice logfile /etc/redis/6381/redis.log dbfilename dump.rdb dir /etc/redis/6381 bind 127.0.0.1 10.0.0.51 protected-mode no #啓動redis多實例 [root@db01 ~]# redis-server /etc/redis/6379/redis.conf [root@db01 ~]# redis-server /etc/redis/6380/redis.conf [root@db01 ~]# redis-server /etc/redis/6381/redis.conf #查看進程 [root@db01 ~]# ps -ef|grep redis root 3570 1 0 22:44 ? 00:00:00 redis-server 127.0.0.1:6379 root 3574 1 0 22:44 ? 00:00:00 redis-server 127.0.0.1:6380 root 3578 1 0 22:44 ? 00:00:00 redis-server 127.0.0.1:6381
開啓主從
#鏈接從庫slave01(6380) [root@db01 ~]# redis-cli -p 6380 #開啓主從 127.0.0.1:6380> SLAVEOF 127.0.0.1 6379 OK #查從信息 127.0.0.1:6380> INFO replication # Replication role:slave //角色變成了從庫 master_host:127.0.0.1 //主庫的ip master_port:6379 //主庫的端口 master_link_status:up master_last_io_seconds_ago:7 master_sync_in_progress:0 slave_repl_offset:15 slave_priority:100 slave_read_only:1 connected_slaves:0 master_repl_offset:0 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0 #鏈接從庫slave02(6381) [root@db01 ~]# redis-cli -p 6381 #開啓主從 127.0.0.1:6381> SLAVEOF 127.0.0.1 6379 OK #查看主從信息 127.0.0.1:6381> INFO replication # Replication role:slave //角色變成了從庫 master_host:127.0.0.1 //主庫的ip master_port:6379 //主庫的端口 master_link_status:up master_last_io_seconds_ago:9 master_sync_in_progress:0 slave_repl_offset:225 slave_priority:100 slave_read_only:1 connected_slaves:0 master_repl_offset:0 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0 #鏈接master(6379) [root@db01 ~]# redis-cli -p 6379 #在主庫上查看主從複製信息 127.0.0.1:6379> INFO replication # Replication role:master //角色master connected_slaves:2 //兩臺slave slave0:ip=127.0.0.1,port=6380,state=online,offset=337,lag=1 slave1:ip=127.0.0.1,port=6381,state=online,offset=337,lag=1 master_repl_offset:337 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:2 repl_backlog_histlen:336
主從切換
#鏈接master(6379) [root@db01 ~]# redis-cli -p 6379 #關閉主庫 127.0.0.1:6379> shutdown #鏈接從庫slave01(6380) [root@db01 ~]# redis-cli -p 6380 #查看主從信息 127.0.0.1:6380> INFO replication # Replication role:slave master_host:127.0.0.1 master_port:6379 master_link_status:down //鏈接主庫的狀態是:down master_last_io_seconds_ago:-1 master_sync_in_progress:0 slave_repl_offset:1877 master_link_down_since_seconds:58 slave_priority:100 slave_read_only:1 connected_slaves:0 master_repl_offset:0 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0 #取消6380的主從關係 127.0.0.1:6380> SLAVEOF no one OK 127.0.0.1:6380> info replication # Replication role:master //此時6380的角色就變成了master connected_slaves:0 master_repl_offset:0 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0 #將其餘從庫從新指向新主(6380) #鏈接6381從庫 [root@db01 ~]# redis-cli -p 6381 #將6381從庫變成6380的從庫 127.0.0.1:6381> SLAVEOF 127.0.0.1 6380 OK #查看主從信息 127.0.0.1:6381> INFO replication # Replication role:slave //角色仍是slave master_host:127.0.0.1 master_port:6380 //主庫的端口已經變成了6380 master_link_status:up master_last_io_seconds_ago:4 master_sync_in_progress:0 slave_repl_offset:1 slave_priority:100 slave_read_only:1 connected_slaves:0 master_repl_offset:0 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0