一、什麼是Redis主從複製?
將一臺服務器作爲Redis的主庫(Master),另外服務器作爲從庫(Slave)(一臺或多臺)。主庫(Master)只負責寫數據,每次有數據更新的時候,Redis服務器會將數據從主庫同步到其他從庫中,從庫只負責讀取數據。
一個主庫可以擁有多個從庫,一個從庫只能擁有一個主庫,一個從庫也可以擁有從庫,但從庫依然還是從庫,不會擁有寫的功能。
二、爲什麼要Redis複製?
1、儘管Redis的性能很優秀,但它也會遇到沒辦法快速處理請求的情況,特別是在對集合和有序集合進行操作的時候,涉及到的元素可能會有上萬個甚至上百萬,在這種情況下執行所花費的時間可能以秒來計算,而不是毫秒或者微秒。但即使一個命令只需要花費10毫秒就能完成,單個redis實例1s也只能處理100命令。
2、要實現分佈式數據庫的更大存儲量和高併發訪問量,我們會將原來集中式數據庫的數據分別存儲到其他多個網絡節點。Redis爲了解決這個單一節點問題,也會將數據複製多個副本部署到其他節點上進行復制,實現Redis的高可用,實現數據的冗餘備份,從而保證數據和服務的高可用。
Redis複製是高可用的基石。如果沒有redis複製也就不可能實現高可用。
爲什麼不只使用一臺redis?
第一,機器故障。我們部署到一臺 Redis
服務器,當發生機器故障時,需要遷移到另外一臺服務器並且要保證數據是同步的。而數據是最重要的,如果你不在乎,基本上也就不會使用 Redis
了。
第二,容量瓶頸。當我們有需求需要擴容 Redis 內存時,從 16G 的內存升到 64G,單機肯定是滿足不了。當然,你可以重新買個 128G
的新機器。
第三,QPS 瓶頸。Redis 號稱支持10 萬 QPS,當業務需要 100 萬 QPS 時,我們該怎麼做呢?這時就用到了 Redis 複製
三、複製原理
Slave 啓動連接成功到Master後 會發送一個sync命令,Master接到命令啓動後臺的存盤進程,同時收集所有接收到的用於修改數據集命令,在後臺進程執行完畢之後,master將傳送整個數據文件到slave,以完成一次完全同步。
步驟:
步驟 | 主服務器操作 | 從服務器操作 |
---|---|---|
1 | 等待命令進入 | 連接(或者重連)主服務器,發送SYNC命令 |
2 | 開始執行BGSAVE,並使用緩衝區記錄BGSAVE之後執行的所有寫命令 | 根據配置選項來決定是繼續使用現有的數據(如果有的話)來處理客戶端的命令請求,還是向發送請求的客戶端返回錯誤 |
3 | BGSAVE執行完畢,向從服務器發送快照文件,並在發送期間繼續使用緩衝區記錄被執行的寫命令 | 丟棄所有舊數據(如果有的話),開始載入主服務器發送來的快照文件 |
4 | 快照文件發送完畢,開始向從服務器發送存儲在緩衝區裏面的寫命令 | 完成對快照文件的解釋操作,嚮往常一樣開始接受命令請求 |
5 | 緩衝區存儲的寫命令發送完畢,從現在開始,每執行一個寫命令,就像從服務器發送相同的寫命令 | 執行主服務器發送來的所有存儲在緩衝區裏面的寫命令,並從現在開始,接收並執行主服務器傳來的每個命令 |
全量複製與部分複製
1、全量複製
第一步,Redis 內部會發出一個同步命令,剛開始是 Psync 命令,Psync ? -1 表示要求 Master 主機同步數據;
第二步,Master 會向從機發送 runid 和 offset,因爲 Slave 並沒有對應的 offset,所以是全量複製;
第三步,通過指令 save masterInfo,從機 Slave 會保存 Master 的基本信息;
第四步,Master 執行 bgsave 命令(持久化命令),對於一個快照來說,怎麼快怎麼來。實際上 Master 主機裏有
repl_back_buffer(複製緩衝區);
第五步,通過指令 send RDB 發送 RDB 文件;
第六步,發送緩衝區數據;
第七步,刷新舊的數據;
第八步,加載 RDB 文件和緩衝區數據的加載。
那麼全量複製需要哪些開銷呢?
- bgsave 時間;
- RDB 文件網絡傳輸時間;
- 從節點清空數據的時間;
- 從節點加載 RDB 的時間;
- AOF 重寫的時間(這裏需要說明一下,RDB 全量複製完加載 RDB,如果 AOF 開啓的話,就會出現 AOF 重寫來保證是最新的)。
2、部分複製
部分複製是 Redis 2.8 以後出現的,之所以要加入部分複製,是因爲全量複製會產生很多問題,比如像上面的時間開銷大、無法隔離等問題,
Redis 希望能夠在 Master 出現抖動(相當於斷開連接)的時候,可以有一些機制將複製的損失降低到最低。
第一步,如果打算抖動(連接斷開 connection lost);
第二步,Master 還是會寫 repl_back_buffer(複製緩衝區);
第三步,Slave 會繼續嘗試連接主機;
第四步,Slave 會把自己當前 runid 和偏移量傳輸給主機 Master,並且執行 pysnc 命令同步;
第五步,如果 Master 發現你的偏移量在緩衝區的範圍內,就會返回 continue 命令;
第六步,同步了 offset 的部分數據,所以部分複製的基礎就是偏移量 offset。
通過部分複製,可以有效的減少全量複製的開銷。
四、實踐操作
爲了方便 這裏使用一臺服務器進行操作,拷貝了3個配置文件,分別是redis6379.conf;redis6380.conf;redis6381.conf.
配從(庫)不配主(庫)
模擬環境
1、修改配置文件:
1、修改配置文件端口號(6379,6380,6381);port:***
2、開啓守護進程 daemonize yes
3、指定不同的pid名稱如:pidfile /var/run/redis_6380.pid
4、指定不同的log名如:logfile “redis6380.log”
5、指定不同的dump.rdb如:dbfilename dump6380.rdb
6、指定不同的aof(如果開啓的話),如:appendfilename “appendonly6381.aof”
2、啓動
以配置文件的方式分別啓動三個服務6379,6380,6381
redis-server /usr/local/redis6379.conf
redis-server /usr/local/redis6380.conf
redis-server /usr/local/redis6381.conf
進入redis
redis-cli -p 6379
redis-cli -p 6380
redis-cli -p 6381
常見Redis複製
1、一主二僕
即一個主機兩個從機
這裏6379作爲我們的主機,6381,6380作爲從機。 當Slave與Master斷開後需要重新slave
of連接纔可建立之前的主從關係;Master掛掉後,Master關係依然存在,Master重啓即可恢復。
2、薪火相傳
上一個Slave可以是下一個slave的Master,Slave同樣可以接收其他slaves的連接和同步請求,那麼該slave作爲了鏈條中下一個的master,可以有效減輕master的寫壓力
中途變更轉向:會清除之前的數據,重新建立拷貝最新的
3、反客爲主
當Master掛掉後,Slave可鍵入命令 slaveof no one使當前redis停止與其他Master redis數據同步,轉成Master redis。
五、Redis哨兵機制
反客爲主的自動版,能夠後臺監控主機是否故障,如果故障了根據投票數自動將從庫轉換爲主庫
哨兵(Sentinel)機制的意義:
Redis複製過程中有一個很大的缺點,當我們的主機宕機過後,需要我們人工去解決,Redis哨兵就是爲了解決這個問題。
Redis哨兵是一個分佈式的架構,每一個Sentinel節點會對數據節點和其餘Sentinel節點進行監控,當發現某個節點無法到達的時候,會自動標識該節點。如果這個節點是主節點,那麼他會和其他Sentinel節點‘協商’,大部分節點都認爲主節點無法到達的時候,他們會選舉一個Sentinel節點來完成自動故障轉移,同時告訴Redis應用方。
哨兵(Sentinel)的作用:
監控(Monitoring): 哨兵(sentinel) 會不斷地檢查你的Master和Slave是否運作正常。
提醒(Notification):當被監控的某個 Redis出現問題時, 哨兵(sentinel) 可以通過 API 向管理員或者其他應用程序發送通知。
自動故障遷移(Automatic failover): 當一個Master不能正常工作時,哨兵(sentinel) 會開始一次自動故障遷移操作,它會將失效Master的其中一個Slave升級爲新的Master, 並讓失效Master的其他Slave改爲複製新的Master; 當客戶端試圖連接失效的Master時,集羣也會向客戶端返回新Master的地址,使得集羣可以使用Master代替失效Master。
哨兵(Sentinel)工作方式:
1、每個Sentinel(哨兵)進程以每秒鐘一次的頻率向整個集羣中的Master主服務器,Slave從服務器以及其他Sentinel(哨兵)進程發送一個PING 命令。
2、如果一個實例(instance)距離最後一次有效回覆 PING 命令的時間超過 down-after-milliseconds 選項所指定的值, 則這個實例會被 Sentinel(哨兵)進程標記爲主觀下線(SDOWN)。
3、如果一個Master主服務器被標記爲主觀下線(SDOWN),則正在監視這個Master主服務器的所有 Sentinel(哨兵)進程要以每秒一次的頻率確認Master主服務器的確進入了主觀下線狀態。
4、當有足夠數量的 Sentinel(哨兵)進程(大於等於配置文件指定的值)在指定的時間範圍內確認Master主服務器進入了主觀下線狀態(SDOWN), 則Master主服務器會被標記爲客觀下線(ODOWN)。
5、在一般情況下, 每個 Sentinel(哨兵)進程會以每 10 秒一次的頻率向集羣中的所有Master主服務器、Slave從服務器發送 INFO 命令。
6、當Master主服務器被 Sentinel(哨兵)進程標記爲客觀下線(ODOWN)時,Sentinel(哨兵)進程向下線的 Master主服務器的所有 Slave從服務器發送 INFO 命令的頻率會從 10 秒一次改爲每秒一次。
7、若沒有足夠數量的 Sentinel(哨兵)進程同意 Master主服務器下線, Master主服務器的客觀下線狀態就會被移除。若 Master主服務器重新向 Sentinel(哨兵)進程發送 PING 命令返回有效回覆,Master主服務器的主觀下線狀態就會被移除。
哨兵(Sentinel)機制實踐:
這裏我的環境分別爲:
134.98.1.72 6379 Master 作爲主服務器
134.98.1.80 6379 slave 從服務器 slaveof 134.98.1.72 6379
134.98.1.81 6381 slave 從服務器 slaveof 134.98.1.72 6381
分別啓動三臺服務器的Redis。可以通過配置文件或者手動的將80,81配置爲從服務器。
配置文件主要改動
bind 0.0.0.0
protected-mode no
sentinel.conf文件配置
#
bind 0.0.0.0
#
protected-mode no
# port <sentinel-port>
# The port that this sentinel instance will run on
port 26379 端口號
daemonize no
pidfile "/var/run/redis-sentinel.pid"
logfile "/usr/local/log/sentinel.log" 日誌地址
sentinel monitor mymaster 134.98.1.72 6379 2
表示 Sentinel(哨兵)進程去監視一個名爲 mymaster 的主服務器,這個主服務器的 IP 地址爲 134.98.1.72, 端口號爲 6379,而將這個主服務器判斷爲失效至少需要 2 個 Sentinel(哨兵)進程的同意
sentinel down-after-milliseconds mymaster 5000
Sentinel(哨兵)進程判斷服務器已經掉線所需的毫秒數。
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 60000
按照順序 分別啓動Master–>slave–>sentinel
sentinel 啓動:redis-sentinel sentinel.conf
啓動後:
此時的master是我們的72服務器,80,81爲從服務器;
13437:X 23 Apr 2019 16:04:48.053 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
13437:X 23 Apr 2019 16:04:48.053 # Redis version=5.0.4, bits=64, commit=00000000, modified=0, pid=13437, just started
13437:X 23 Apr 2019 16:04:48.053 # Configuration loaded
13437:X 23 Apr 2019 16:04:48.054 * Increased maximum number of open files to 10032 (it was originally set to 1024).
13437:X 23 Apr 2019 16:04:48.055 # Could not create server TCP listening socket 192.168.1.1:26379: bind: Cannot assign requested address
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 5.0.4 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in sentinel mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 26379
| `-._ `._ / _.-' | PID: 13437
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | http://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
13437:X 23 Apr 2019 16:04:48.056 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
13437:X 23 Apr 2019 16:04:48.060 # Sentinel ID is 1065cb78d77eabcccbee19b2c2f4585fa08e9390
13437:X 23 Apr 2019 16:04:48.060 # +monitor master mymaster 134.98.1.72 6379 quorum 2
13437:X 23 Apr 2019 16:04:48.061 * +slave slave 134.98.1.81:6379 134.98.1.81 6379 @ mymaster 134.98.1.72 6379
13437:X 23 Apr 2019 16:04:48.063 * +slave slave 134.98.1.80:6380 134.98.1.80 6380 @ mymaster 134.98.1.72 6379
13437:X 23 Apr 2019 16:05:31.929 * +sentinel sentinel 0881a984e2229c92812a37e24dfddfc338caf8d8 134.98.1.81 26379 @ mymaster 134.98.1.72 6379
13437:X 23 Apr 2019 16:06:12.481 * +sentinel sentinel 034d49988aa4f58efbe2db5f6c6c5475d3651ad4 134.98.1.80 26379 @ mymaster 134.98.1.72 6379
我們模擬master宕機,將master shutdown後,sentinel開始工作選舉新的slave作爲master
13437:X 23 Apr 2019 16:07:36.863 # +sdown master mymaster 134.98.1.72 6379
13437:X 23 Apr 2019 16:07:36.990 # +new-epoch 1
13437:X 23 Apr 2019 16:07:36.992 # +vote-for-leader 0881a984e2229c92812a37e24dfddfc338caf8d8 1
13437:X 23 Apr 2019 16:07:37.988 # +odown master mymaster 134.98.1.72 6379 #quorum 3/2
13437:X 23 Apr 2019 16:07:37.988 # Next failover delay: I will not start a failover before Tue Apr 23 16:09:37 2019
13437:X 23 Apr 2019 16:07:38.070 # +config-update-from sentinel 0881a984e2229c92812a37e24dfddfc338caf8d8 134.98.1.81 26379 @ mymaster 134.98.1.72 6379
13437:X 23 Apr 2019 16:07:38.070 # +switch-master mymaster 134.98.1.72 6379 134.98.1.81 6379
13437:X 23 Apr 2019 16:07:38.070 * +slave slave 134.98.1.80:6380 134.98.1.80 6380 @ mymaster 134.98.1.81 6379
13437:X 23 Apr 2019 16:07:38.070 * +slave slave 134.98.1.72:6379 134.98.1.72 6379 @ mymaster 134.98.1.81 6379
13437:X 23 Apr 2019 16:07:43.145 # +sdown slave 134.98.1.72:6379 134.98.1.72 6379 @ mymaster 134.98.1.81 6379
上面可以看到 81機器已經成功上位 成爲新的master,80自動跟從於81主機
通過info replication 查看信息可看到
81機器
# Replication
role:master
connected_slaves:1
slave0:ip=134.98.1.80,port=6380,state=online,offset=1059414,lag=1
master_replid:49c4d69750208186e74d53f6e7d02576cbc67fa6
master_replid2:b754d6583e4c987d8b51fe29f443479397ef8572
master_repl_offset:1059551
second_repl_offset:1050157
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1023219
repl_backlog_histlen:36333
80機器
# Replication
role:slave
master_host:134.98.1.81
master_port:6379
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_repl_offset:1062593
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:49c4d69750208186e74d53f6e7d02576cbc67fa6
master_replid2:b754d6583e4c987d8b51fe29f443479397ef8572
master_repl_offset:1062593
second_repl_offset:1050157
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1023303
repl_backlog_histlen:39291
問題:如果此時原來的master 72機器恢復好了 重新啓動會是什麼情況呢?下面我們模擬一下。
啓動成功 sentinel日誌裏看見:
info replication 查看得知
# Replication
role:slave
master_host:134.98.1.80
master_port:6380
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:1125636
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:49c4d69750208186e74d53f6e7d02576cbc67fa6
master_replid2:b754d6583e4c987d8b51fe29f443479397ef8572
master_repl_offset:1125636
second_repl_offset:1050157
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1050157
repl_backlog_histlen:75480
此時我們的主機依然還是81機器,而原來的主機會自動成爲slave跟從於81機器。
哨兵選舉算法:
會考慮slave的一些信息:
- 跟master斷開連接的時長
- slave優先級
- 複製offset
- run id
如果一個slave跟master斷開連接已經超過了down-after-milliseconds的10倍,外加master宕機的時長,那麼slave就被認爲不適合選舉爲master(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
接下來會對slave進行排序(1)按照slave優先級進行排序,slave priority越低,優先級就越高(2)如果slave priority相同,那麼看replica offset,哪個slave複製了越多的數據,offset越靠後,優先級就越高(3)如果上面兩個條件都相同,那麼選擇一個run id比較小的那個slav
如果想要slave 永遠不參與選舉 可以將slave priority設爲0
【參考資料】
《Redis實戰》
GitChat – Redis入門到分佈式實踐
https://www.jianshu.com/p/63e2d3e1d9ee
https://www.cnblogs.com/PatrickLiu/p/8444546.html
如有總結不當,有問題,錯誤的地方請大家予以指正,共同學習,共同進步