1.主從複制高可用的問題
主從複制高可用的作用
1.為master提供備份,當master當機時,slave有完整的備份資料
2.對master實作分流,實作讀寫分離
但是主從架構有一個問題
1.如果master當機,故障轉移需要手動完成或者由别的工具來完成,從slave中選擇一個slave做為新的master
寫能力和存儲能力受限
隻能在一個節點是寫入資料
所有資料隻能儲存在一個節點上

上圖模拟了主從複制架構中一主兩從情況下,master當機,則slave從master同步資料也斷開,此時client向master寫入資料會失敗,讀寫分離時讀取資料正常,但不能更新資料
master出現故障之後,手動進行故障轉移步驟
1.選擇一個slave,執行slave no one指令使之成為一個master
2.對其餘的slave執行slaveof new master指令,将這些slave指定為新的master的slave
3.client會對新的master進行寫入資料,從slave中讀取原來的資料
上面的操作過程需要手動完成,或者編寫腳本,由腳本來執行這個過程,但是這個過程是有問題的:
怎麼判斷一個Redis節點是有問題的,怎麼通知client對新master進行寫入操作
怎麼保證一個完整的事務實作過程
上面的過程就可以使用Redis Sentinel來實作
2.Redis Sentinel架構說明
Redis Sentinel的功能:對Redis節點進行監控,故障判斷,故障轉移,故障通知
對于Redis的主從架構,Redis Sentinel會運作幾個sentinel程序
sentinel程序不操作資料,而是對Redis故障進行判斷和轉移
同時多個sentinel運作,即使一個sentinel程序運作異常,還有别的sentinel繼續運作,可以保證對故障節點判斷的準确性,同時保證Redis的高可用
對于redis-cli來說,Redis cli不會再記錄Redis的IP和端口,而是從sentinel擷取Redis資訊,然後進行連接配接Redis節點,進行資料寫入和讀取操作
多個Redis Sentinel對所有的master和slave進行監控,會實時記錄master和slave的位址資訊
Redis Sentinel故障轉移步驟:
1.當某個master發生故障,多個sentinel會監控到這個異常,這些sentinel會按照一定規則從多個slave中選中一個做為新的master,并通知别的slave從新的master中同步資料
2.當某個slave轉換為新的master,sentinel會記錄新的master的位址資訊和slave的位址資訊,通知Redis cli
3.Redis cli接收到新的master和slave的資訊,就會向新的master寫入資料,從slave中讀取資料
4.等到原來的master重新開機之後,會變成新的master的slave,并從新的master同步資料
在上面的步驟裡,sentinel實作了Redis的故障自動發現,自動轉移和自動通知
說明:一套Redis sentinel集合可以通知master-name做為辨別同時監控多套主從架構
3.Redis Sentinel安裝配置
3.1 環境說明
實驗在兩台虛拟機上完成,IP位址分别為:192.168.81.100和192.168.81.101
在兩台虛拟機上運作4個redis-server,其中
192.168.81.100的6379端口為master節點
192.168.81.100的6380端口為slave節點
192.168.81.101的6379端口和6380端口為slave節點
在192.168.81.101的26379,26380,26381端口開啟三個sentinel進行監控
3.2 在192.168.81.100虛拟機上配置主從節點
[root@localhost ~]# cd /etc/ # 進入/etc目錄
[root@localhost ~]# systemctl stop redis # 關閉系統中運作的redis
[root@localhost etc]# cp redis.conf redis_6379.conf # 複制redis配置檔案,以端口區分,友善後面進行配置
[root@localhost etc]# cp redis.conf redis_6380.conf # 複制redis配置檔案,以端口區分,友善後面進行配置
[root@localhost etc]# vi redis_6379.conf # 編輯redis-server配置檔案,修改下面幾行
bind 0.0.0.0 # 修改bing選項,才能從系統外連接配接redis
protected-mode yes # 開啟儲存模式
port 6379 # 指定redis運作的端口
daemonize yes # 以守護程序啟動redis
pidfile "/var/run/redis_6379.pid" # 指定redis運作時pid儲存路徑
logfile "/var/log/redis/redis_6379.log" # 指定redis運作時日志儲存路徑
dir /var/lib/redis_6379 # 指定redis運作時資料檔案儲存路徑
[root@localhost etc]# vi redis_6380.conf # 修改redis-server,修改下面幾行
bind 0.0.0.0
port 6380 # 指定redis運作的端口
daemonize yes
pidfile "/var/run/redis_6380.pid"
logfile "/var/log/redis/redis_6380.log"
dir /var/lib/redis_6380
slaveof 192.168.81.100 6379 # 指定redis-server為192.168.81.100:6379的slave
[root@localhost etc]# redis-server /etc/redis_6379.conf # 指定配置檔案運作redis-server
[root@localhost etc]# redis-server /etc/redis_6380.conf # 指定配置檔案運作redis-server
[root@localhost etc]# ps aux | grep redis-server # 檢視redis-server是否運作
root 2548 0.3 1.7 155192 17720 ? Ssl 23:14 0:00 redis-server 0.0.0.0:6379
root 2562 1.3 1.7 155192 17596 ? Ssl 23:15 0:00 redis-server 0.0.0.0:6380
root 2567 0.0 0.0 112648 960 pts/3 R+ 23:15 0:00 grep --color=auto redis-server
[root@localhost etc]# redis-cli -p 6380 info replication # 進入6380端口運作redis用戶端,并執行'info replication'指令
# Replication
role:slave # 角色為slave
master_host:192.168.81.100 # master為192.168.81.100
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:1919
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
3.3 在192.168.81.101虛拟機上配置從節點
[root@mysql ~]# cd /etc/ # 操作同192.168.81.100相同
[root@mysql ~]# systemctl stop redis
[root@mysql etc]# cp redis.conf redis_6379.conf
[root@mysql etc]# cp redis.conf redis_6380.conf
[root@mysql etc]# vi redis_6379.conf
bind 0.0.0.0
protected-mode yes
port 6379
daemonize yes
pidfile "/var/run/redis_6379.pid"
logfile "/var/log/redis/redis_6379.log"
dir /var/lib/redis_6379
slaveof 192.168.81.100 6379 # 指定redis-server為192.168.81.100:6379的slave
[root@mysql etc]# vi redis_6380.conf
bind 0.0.0.0
port 6380
daemonize yes
pidfile "/var/run/redis_6380.pid"
logfile "/var/log/redis/redis_6380.log"
dir /var/lib/redis_6380
slaveof 192.168.81.100 6379 # 指定redis-server為192.168.81.100:6379的slave
[root@mysql etc]# redis-server /etc/redis_6379.conf # 指定配置檔案運作redis-server
[root@mysql etc]# redis-server /etc/redis_6380.conf # 指定配置檔案運作redis-server
[root@mysql ~]# ps aux | grep redis-server # 檢視redis-server是否運作
root 2178 0.2 0.8 155204 17728 ? Ssl 15:10 0:02 redis-server 0.0.0.0:6379
root 2184 0.2 0.8 155204 17724 ? Ssl 15:10 0:02 redis-server 0.0.0.0:6380
root 2411 0.0 0.0 112664 972 pts/2 R+ 15:29 0:00 grep --color=auto redis-server
[root@mysql ~]# redis-cli -p 6379 info replication
# Replication
role:slave # 角色為slave
master_host:192.168.81.100 # master為192.168.81.100
master_port:6379
master_link_status:up
master_last_io_seconds_ago:6
master_sync_in_progress:0
slave_repl_offset:1961
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
[root@mysql ~]# redis-cli -p 6380 info replication
# Replication
role:slave # 角色為slave
master_host:192.168.81.100 # master為192.168.81.100
master_port:6379
master_link_status:up
master_last_io_seconds_ago:2
master_sync_in_progress:0
slave_repl_offset:1975
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
3.4 在192.168.81.101虛拟機上配置并運作sentinel
[root@mysql etc]# cp redis-sentinel.conf sentinel_26379.conf # 複制sentinel配置檔案,友善區分
[root@mysql etc]# cp redis-sentinel.conf sentinel_26380.conf # 複制sentinel配置檔案,友善區分
[root@mysql etc]# cp redis-sentinel.conf sentinel_26381.conf # 複制sentinel配置檔案,友善區分
[root@mysql etc]# vi sentinel_26379.conf # 修改sentinel配置檔案,修改下面幾行
daemonize yes # 以守護程序方式啟動
port 26379 # 指定端口
protected-mode no # 關閉保護模式
sentinel monitor mymaster 192.168.81.100 6379 2 # 設定sentinel監控資訊
logfile /var/log/redis/sentinel_26379.log # 設定日志檔案儲存路徑
[root@mysql etc]# vi sentinel_26380.conf
daemonize yes
port 26380
protected-mode no
sentinel monitor mymaster 192.168.81.100 6379 2
logfile /var/log/redis/sentinel_26380.log
[root@mysql etc]# vi sentinel_26381.conf
protected-mode no
port 26381
daemonize yes
sentinel monitor mymaster 192.168.81.100 6379 2
logfile /var/log/redis/sentinel_26381.log
[root@mysql etc]# redis-sentinel /etc/sentinel_26379.conf # 指定配置檔案,啟動Redis Sentinel
[root@mysql etc]# redis-sentinel /etc/sentinel_26380.conf # 指定配置檔案,啟動Redis Sentinel
[root@mysql etc]# redis-sentinel /etc/sentinel_26381.conf # 指定配置檔案,啟動Redis Sentinel
[root@mysql etc]# ps aux | grep sentinel # 檢視Redis Sentinel是否運作
root 2709 0.9 0.2 142916 5464 ? Ssl 15:49 0:00 redis-sentinel *:26379 [sentinel]
root 2713 1.1 0.2 142916 5472 ? Ssl 15:49 0:00 redis-sentinel *:26380 [sentinel]
root 2717 2.0 0.2 142916 5476 ? Rsl 15:49 0:00 redis-sentinel *:26381 [sentinel]
root 2721 0.0 0.0 112664 964 pts/2 R+ 15:49 0:00 grep --color=auto sentinel
[root@mysql ~]# redis-cli -p 26379
127.0.0.1:26379> ping # 執行ping操作
PONG
127.0.0.1:26379> info sentinel # 檢視所有sentinel的資訊
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.81.100:6379,slaves=3,sentinels=3 # 被監控的Redis主從架構命名為mymaster,被監控Redis節點的master為192.168.81.100L6379,有三個slave,同時有3個sentinel運作
127.0.0.1:26379> exit
[root@mysql ~]# grep -v '^#' /etc/sentinel_26379.conf | grep -v '^$' #檢視sentinel_26379配置檔案,去除注釋和空行,Redis Sentinel向配置檔案中添加了幾行内容
port 26379 # sentinel運作的端口
dir "/tmp"
sentinel myid 9611958fc3e8b7c2be43385e44be88f87d725a77
sentinel monitor mymaster 192.168.81.100 6379 2 # sentinel監控的Redis節點名為mymaster,master位址為192.168.81.100:6379,quorem設定為2
sentinel config-epoch mymaster 0
sentinel leader-epoch mymaster 0
logfile "/var/log/redis/sentinel_26379.log"
daemonize yes
sentinel known-slave mymaster 192.168.81.101 6379 # sentinel探測到的slave
sentinel known-slave mymaster 192.168.81.100 6380 # sentinel探測到的slave
sentinel known-slave mymaster 192.168.81.101 6380 # sentinel探測到的slave
sentinel known-sentinel mymaster 192.168.81.101 26380 17ca0cb82becb58bd24e5a87ee3b6e8e9a49caf1 # Redis Sentinel深測到的别的運作的sentinel
sentinel known-sentinel mymaster 127.0.0.1 26381 fb9342f3007e2abff165f5c33de1d48cf089f062 # Redis Sentinel深測到的别的運作的sentinel
sentinel current-epoch 0
[root@mysql ~]# grep -v '^#' /etc/sentinel_26380.conf | grep -v '^$'
port 26380
dir "/tmp"
sentinel myid 17ca0cb82becb58bd24e5a87ee3b6e8e9a49caf1
sentinel monitor mymaster 192.168.81.100 6379 2
sentinel config-epoch mymaster 0
sentinel leader-epoch mymaster 0
logfile "/var/log/redis/sentinel_26380.log"
daemonize yes
sentinel known-slave mymaster 192.168.81.101 6379
sentinel known-slave mymaster 192.168.81.101 6380
sentinel known-slave mymaster 192.168.81.100 6380
sentinel known-sentinel mymaster 127.0.0.1 26381 fb9342f3007e2abff165f5c33de1d48cf089f062
sentinel known-sentinel mymaster 192.168.81.101 26379 9611958fc3e8b7c2be43385e44be88f87d725a77
sentinel current-epoch 0
[root@mysql ~]# grep -v '^#' /etc/sentinel_26381.conf | grep -v '^$'
port 26381
daemonize yes
dir "/tmp"
sentinel myid fb9342f3007e2abff165f5c33de1d48cf089f062
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel config-epoch mymaster 0
sentinel leader-epoch mymaster 0
logfile "/var/log/redis/sentinel_26381.log"
sentinel known-sentinel mymaster 127.0.0.1 26380 17ca0cb82becb58bd24e5a87ee3b6e8e9a49caf1
sentinel known-sentinel mymaster 192.168.81.101 26379 9611958fc3e8b7c2be43385e44be88f87d725a77
sentinel current-epoch 0
至此,3個sentinel已經正常運作了
3.5 python用戶端持續通過sentinel向Redis寫入資料,讀取資料
import random
import time
from redis.sentinel import Sentinel
sentinel = Sentinel([
('192.168.81.101',26379),
('192.168.81.101',26380),
('192.168.81.101',26381),
],socket_timeout=0.1) # 傳入Redis Sentinel集合
while True:
try:
master = sentinel.discover_master('mymaster')
print('current master IP:',master) # 列印目前master的IP位址和端口
val = random.randint(0,10000) # 擷取10000以内随機整數
key = 'k%d' % val
m = sentinel.master_for('mymaster', socket_timeout=0.5)
m.set(key,'val%d' % val) # 通過sentinel向master節點寫入資料
v = m.get(key) # 通過sentinel讀取資料
print('{0} value is {1}'.format(key,v))
time.sleep(1)
except Exception as e:
print("get no val:",e)
運作上面的代碼:
current master IP: ('192.168.81.100', 6379)
k6081 value is b'val6081'
current master IP: ('192.168.81.100', 6379)
k1778 value is b'val1778'
current master IP: ('192.168.81.100', 6379)
k4927 value is b'val4927'
current master IP: ('192.168.81.100', 6379)
k4074 value is b'val4074'
current master IP: ('192.168.81.100', 6379)
k1138 value is b'val1138'
current master IP: ('192.168.81.100', 6379)
k862 value is b'val862'
current master IP: ('192.168.81.100', 6379)
k4854 value is b'val4854'
current master IP: ('192.168.81.100', 6379)
k9233 value is b'val9233'
current master IP: ('192.168.81.100', 6379)
k6844 value is b'val6844'
current master IP: ('192.168.81.100', 6379)
k8089 value is b'val8089'
3.6 在192.168.81.100虛拟機上模拟master故障
[root@localhost etc]# redis-cli -p 6379 # 連接配接6379端口連接配接redis-server
127.0.0.1:6379> info server # 執行指令檢視目前redis-server的程序ID
# Server
redis_version:3.2.10
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:c8b45a0ec7dc67c6
redis_mode:standalone
os:Linux 3.10.0-514.el7.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
gcc_version:4.8.5
process_id:2548 # 192.168.81.100:6379這個master節點的程序ID為2548
run_id:8ae71ba92660697d52bfb74b99fb15ee82a7cf84
tcp_port:6379
uptime_in_seconds:6552
uptime_in_days:0
hz:10
lru_clock:12896355
executable:/etc/redis-server
config_file:/etc/redis_6379.conf
127.0.0.1:6379> exit
[root@localhost etc]# kill -9 2548 # 在系統中kill掉2548這個程序
[root@localhost etc]# ps aux | grep redis-server # 檢視redis-server運作的程序,2548已經被終止
root 2562 0.2 1.7 155192 17756 ? Ssl Oct15 0:19 redis-server 0.0.0.0:6380
root 2651 0.0 0.0 112648 960 pts/3 S+ 01:19 0:00 grep --color=auto redis-server
3.7 檢視python代碼執行結果
current master IP: ('192.168.81.100', 6379)
1539597332.9189174 k6187 value is b'val6187'
current master IP: ('192.168.81.100', 6379)
1539597333.9236474 k1462 value is b'val1462'
current master IP: ('192.168.81.100', 6379)
get no val: Timeout connecting to server
current master IP: ('192.168.81.100', 6379)
get no val: Timeout connecting to server
current master IP: ('192.168.81.100', 6379)
get no val: Timeout connecting to server
...中間報錯資訊省略
get no val: No master found for 'mymaster'
get no val: No master found for 'mymaster'
get no val: DENIED Redis is running in protected mode because protected mode is enabled, no bind address was specified, no authentication password is requested to clients. In this mode connections are only accepted from the loopback interface. If you want to connect from external computers to Redis you may adopt one of the following solutions: 1) Just disable protected mode sending the command 'CONFIG SET protected-mode no' from the loopback interface by connecting to Redis from the same host the server is running, however MAKE SURE Redis is not publicly accessible from internet if you do so. Use CONFIG REWRITE to make this change permanent. 2) Alternatively you can just disable the protected mode by editing the Redis configuration file, and setting the protected mode option to 'no', and then restarting the server. 3) If you started the server manually just for testing, restart it with the '--protected-mode no' option. 4) Setup a bind address or an authentication password. NOTE: You only need to do one of the above things in order for the server to start accepting connections from the outside.
get no val: No master found for 'mymaster'
...中間報錯資訊省略
get no val: DENIED Redis is running in protected mode because protected mode is enabled, no bind address was specified, no authentication password is requested to clients. In this mode connections are only accepted from the loopback interface. If you want to connect from external computers to Redis you may adopt one of the following solutions: 1) Just disable protected mode sending the command 'CONFIG SET protected-mode no' from the loopback interface by connecting to Redis from the same host the server is running, however MAKE SURE Redis is not publicly accessible from internet if you do so. Use CONFIG REWRITE to make this change permanent. 2) Alternatively you can just disable the protected mode by editing the Redis configuration file, and setting the protected mode option to 'no', and then restarting the server. 3) If you started the server manually just for testing, restart it with the '--protected-mode no' option. 4) Setup a bind address or an authentication password. NOTE: You only need to do one of the above things in order for the server to start accepting connections from the outside.
current master IP: ('192.168.81.101', 6379)
1539597365.971147 k3568 value is b'val3568'
current master IP: ('192.168.81.101', 6379)
1539597366.974567 k7745 value is b'val7745'
current master IP: ('192.168.81.101', 6379)
1539597367.9783657 k6281 value is b'val6281'
current master IP: ('192.168.81.101', 6379)
可以看到
初始master是192.168.81.100:6379
,在系統指令提示符中kill掉6379端口的redis-server後,python程式從
1539597333.9236474
秒後一直抛出異常,直到
1539597365.971147
秒,python程式通過sentinel擷取到新的master為
192.168.81.101:6379
。
1539597365.971147
減去
1539597333.9236474
恰好就是sentinel配置檔案中
sentinel down-after-milliseconds mymaster
選項設定的
30000毫秒
,也就是30秒
3.8 檢視Redis Sentinel的日志
在192.168.81.101虛拟機上檢視
/var/log/redis/sentinel_26379.log
日志,下面的部分日志就是sentinel在切換master的記錄
4067:X 15 Oct 17:55:40.303 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 192.168.81.101 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:55:42.119 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 127.0.0.1 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:55:42.334 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 192.168.81.101 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:55:44.208 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 127.0.0.1 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:55:44.349 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 192.168.81.101 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:55:46.266 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 127.0.0.1 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:55:46.365 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 192.168.81.101 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:55:48.419 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 127.0.0.1 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:55:48.481 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 192.168.81.101 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:55:50.429 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 127.0.0.1 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:55:50.506 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 192.168.81.101 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:55:52.548 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 127.0.0.1 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:55:52.571 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 192.168.81.101 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:55:54.592 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 127.0.0.1 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:55:54.598 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 192.168.81.101 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:55:56.655 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 127.0.0.1 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:55:56.662 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 192.168.81.101 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:55:58.734 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 127.0.0.1 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:55:58.886 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 192.168.81.101 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:56:00.798 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 127.0.0.1 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:56:00.972 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 192.168.81.101 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:56:02.884 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 127.0.0.1 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:56:03.042 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 192.168.81.101 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:56:04.917 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 127.0.0.1 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:56:05.099 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 192.168.81.101 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:56:07.045 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 127.0.0.1 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:56:07.112 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 192.168.81.101 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:56:09.145 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 127.0.0.1 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:56:09.324 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 192.168.81.101 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:56:11.218 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 127.0.0.1 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:56:11.345 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 192.168.81.101 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:56:12.211 # +sdown master mymaster 192.168.81.100 6379
4067:X 15 Oct 17:56:12.345 # +new-epoch 8
4067:X 15 Oct 17:56:12.346 # +vote-for-leader fb9342f3007e2abff165f5c33de1d48cf089f062 8
4067:X 15 Oct 17:56:13.267 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 127.0.0.1 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:56:13.269 * +sentinel-address-switch master mymaster 192.168.81.100 6379 ip 192.168.81.101 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:56:13.289 # +odown master mymaster 192.168.81.100 6379 #quorum 2/2
4067:X 15 Oct 17:56:13.289 # Next failover delay: I will not start a failover before Mon Oct 15 18:02:13 2018
4067:X 15 Oct 17:56:13.440 # +config-update-from sentinel fb9342f3007e2abff165f5c33de1d48cf089f062 192.168.81.101 26381 @ mymaster 192.168.81.100 6379
4067:X 15 Oct 17:56:13.441 # +switch-master mymaster 192.168.81.100 6379 192.168.81.101 6379
4067:X 15 Oct 17:56:13.441 * +slave slave 192.168.81.101:6380 192.168.81.101 6380 @ mymaster 192.168.81.101 6379
4067:X 15 Oct 17:56:13.441 * +slave slave 192.168.81.100:6380 192.168.81.100 6380 @ mymaster 192.168.81.101 6379
4067:X 15 Oct 17:56:13.441 * +slave slave 192.168.81.100:6379 192.168.81.100 6379 @ mymaster 192.168.81.101 6379
4067:X 15 Oct 17:56:15.586 * +sentinel-address-switch master mymaster 192.168.81.101 6379 ip 127.0.0.1 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:56:15.604 * +sentinel-address-switch master mymaster 192.168.81.101 6379 ip 192.168.81.101 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:56:15.689 * +sentinel-address-switch master mymaster 192.168.81.101 6379 ip 127.0.0.1 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
4067:X 15 Oct 17:56:15.693 * +sentinel-address-switch master mymaster 192.168.81.101 6379 ip 192.168.81.101 port 26381 for fb9342f3007e2abff165f5c33de1d48cf089f062
從上面的日志可以看出,
在17:56:13.441時間點sentinel把master從182.168.81.100:6379切換到192.168.81.101:6379
4.Redis Sentinel用戶端連接配接(python版)
Redis Sentinel的高可用指的是服務端的高可用,對于Redis服務端的master當機,sentinel可以對故障實作自動發現,自動轉移,自動通知。這個過程用戶端是感覺不到的
Redis高可用即依賴于服務端的高可用,又依賴于用戶端的高可用
通過分析Redis Sentinel的請求響應流程,可以知道用戶端實作高可用步驟:
1.用戶端周遊sentinel節點集合,擷取一個可用的sentinel節點,同時擷取masterName
2.在可用的sentinel節點上執行sentinel的API,擷取master的位址和端口
3.在sentinel内部,sentinel會按照一定頻率在master或者slave上執行info指令,擷取對應節點的資訊
4.用戶端擷取到master的位址和端口,會執行role指令或者role replication指令,對master進行驗證
5.當master出現故障,sentinel按照算法從slave中選出一個做為新的master,同時把其餘的slave做為新的master的slave
6.sentinel維護一個頻道,sentinel在這個頻道中釋出消息,指出新master的IP和端口
7.用戶端訂閱這個頻道,接收消息後就知道新master的IP和端口,向新master連接配接進行資料處理
原理圖如下
python用戶端接入Redis Sentinel需要兩個參數:sentinel位址集合,masterName
需要注意的是Redis節點的配置檔案中的必須設定為
protected-mode
,否則連接配接會失敗
yes
from redis.sentinel import Sentinel
sentinel = Sentinel([
('192.168.81.101',26379),
('192.168.81.101',26380),
('192.168.81.101',26381),
],socket_timeout=0.1)
master = sentinel.discover_master('mymaster')
print(master) # ('192.168.81.100', 6379)
slave = sentinel.discover_slaves('mymaster')
print(slave) # [('192.168.81.100', 6380), ('192.168.81.101', 6379), ('192.168.81.101', 6380)]
5.Redis Sentinel實作原理
5.1 Redis Sentinel内部的三個定時任務
Redis Sentinel内部有三個定時任務來對redid節點進行故障判斷和轉移
- 1.每10秒每個sentinel對master和slave執行
指令,以發現slave節點和确認主從關系info
sentinel在master節點執行
info replication
指令,從指令執行結果中解析出slave節點
- 2.每2秒每個sentinel通過master節點的channel交換資訊(釋出訂閱)
master節點上有一個釋出訂閱的channel頻道:
__sentinel__:hello
,用于所有sentinel之間進行資訊交換
一個sentinel釋出消息,消息包含目前sentinel節點的資訊,對其他sentinel節點的判斷以及目前sentinel對master節點和slave節點的一些判斷
其他sentinel都可以接收到這條消息
新加入sentinel節點時,sentinel節點之間可以互相感覺,以達到資訊互動的功能
- 3.每1秒每個sentinel對其他sentinel節點和Redis節點執行ping操作
每個sentinel都可以知道其他sentinel節點,當監控的master發生故障時,友善進行判斷和新master的挑選,這個定時任務是master進行故障判定的依據
5.2 主觀下線和客觀下線
主觀下線:每個sentinel節點對Redis節點失敗的'偏見'
在redis-sentinel配置檔案中,有下面這種配置
sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel down-after-milliseconds <master-name> <timeout>
一個sentinel集合可以同時監控多個master,slave的節點
sentinel對多個master,slave節點進行區分的辨別就是master-name,ip和port是master節點的IP位址和端口,quorum是master客觀下線之後sentinel進行判斷的節點數
sentinel對master進行主觀下線判斷的時間,單們為毫秒
每個sentinel每秒對master和slave執行ping操作,當sentinel對master或slave在timeout定義的毫秒時間内沒有回複,則sentinel會認為這個節點已經被主觀下線了
在前面的例子中對sentinel的配置是
sentinel monitor mymaster 192.168.81.100 6379 2
sentinel down-after-milliseconds mymaster 30000
解釋:
sentinel集合監控名為mymaster的master,slave節點
被監控的master節點的IP位址是192.168.81.100,端口為6379,
sentinel會在`__sentinel__:hello`頻道中交流對master節點的看法,如果sentinel節點都對master節點ping失敗'達成共識',sentinel個數超過quorum的個數,sentinel集合則會認為master節點客觀下線
當兩個sentinel對master節點執行ping操作,在30000毫秒(30秒)時間内沒有得到回複,則認為節點已經被主觀下線
quorum建議設定為:(sentinel節點數 / 2) + 1,可以根據應用場景進行設定
5.3 sentinel上司者選舉
要點:
隻需要一個sentinel節點就可以完成故障轉移
通過`sentinel is-master-down-by-addr`指令來完成sentinel交換對master節點的失敗判定和新master的選舉
完成sentinel上司者選舉步驟:
1.每個做主觀下線的sentinel節點向其他sentinel節點發送指令,要求将自己設定為上司者
2.收到指令的sentinel節點如果沒有同意同意其他sentinel節點發送的指令,那麼将同意該請求,否則拒絕
3.如果該sentinel節點發現自己的票數已經超過sentinel集合半數且超過quorum,将成為上司者
4.如果此過程中有多個sentinel節點成為上司者,那麼将等待一段時間重新進行選舉
5.4 故障轉移(由sentinel上司者節點完成)
故障轉移步驟:
1.從slave節點中選出一個合适的節點作為新的master節點
2.對選出的slave節點執行`slaveof no one`指令,使成為新的master節點
3.向剩餘的slave節點發送指令,讓slave節點成為新master節點的slave節點,然後從新master節點同步資料
資料同步規則和parallel-syncs參數有關
如一個一主三從架構中,master故障,sentinel上司者從3個slave中選出一個作為新的master節點,剩餘的兩個slave節點會成為新master節點的slave,從新master節點同步同步資料
master節點隻需要生成一次RDB檔案
如果parallel-syncs參數設定為1,則剩餘兩個slave節點會按順序從新master節點拷貝資料,一個slave切點拷貝完成,另外一個slave才會從新master節點拷貝資料
如果parallel-syncs參數設定為2,則兩個slave節點會同時從master節點進行資料拷貝,這無疑會加入新master的開銷
4.sentinel上司者會把原來的master節點設定為slave節點,并保持對其'關注',當原來的master節點恢複後,sentinel會使其去複制新master節點的資料
5.5 slave節點的選擇
slave節點選擇規則
1.選擇slave-priority(slave節點優先級)最高的slave節點,如果存在則傳回,不存在則繼續
2.選擇複制偏移量(offset)最大的slave節點,offset最大說明對master的資料複制的最完整,如果存在則傳回,不存在則繼續
3.選擇run_id最小的slave節點,run_id最小說明slave節點啟動最早
6.總結:
Redis Sentinel是Redis的高可用實作方案:故障發現,故障自動轉移,配置中心,用戶端通知
Redis Sentinel是Redis 2.8版本開始才正式生産可用,之前版本不可用于生産
盡可以在不同實體機上部署Redis Sentinel所有節點,但是最好一個區域網路内
Redis Sentinel中sentinel節點個數應該大于等于3,且最好為奇數,可以保證判斷的公平
Redis Sentinel中的資料節點與普通資料節點沒有差別
用戶端初始化時連接配接的是Sentinel節點集合,不是具體的Redis節點,但是Sentinel隻是配置中心不是代理
Redis Sentinel通過三個定時任務實作了Sentinel節點對于master,slave,其餘sentinel節點的監控
Redis Sentinel在對節點做失敗判定時分為主觀下線和客觀下線
看懂Redis Sentinel故障轉移日志對于Redis Sentinel以及問題排查非常有幫助
Redis Sentinel實作讀寫分離高可用可以依賴Redis Sentinel節點的消息通知,擷取Redis資料節點的狀态變化