部署如下圖
可以分為 Master-Slave Cluster 和 Sentinel HA Solution 兩個部分
??
通常會將其中一個 Sentinel 節(jié)點(diǎn)和一個 Redis 節(jié)點(diǎn)部署在一臺機(jī)上
Master-Slave Cluster
- 配置
?master 不需要特殊配置
?slave 需要在配置文件里添加一項(xiàng)
????slaveof <Master IP> <Port>
?所有節(jié)點(diǎn)添加以下配置
????min-slaves-to-write 1??# 至少要有 1 個健康的 slave 才允許寫數(shù)據(jù),默認(rèn)是 0
?讀寫分離,默認(rèn) slave 為只讀奢讨,寫操作要通過 master - 數(shù)據(jù)同步
?(1). 配置文件有 slaveof 命令的節(jié)點(diǎn)啟動為 slave
?(2). Slave 檢查是否存有 master id
?(3). 沒有則向 master 發(fā)送 PSYNC ? -1 做全量復(fù)制庞瘸,有則發(fā)送 PSYNC id offset 做增量復(fù)制
?(4). Master 收到 PSYNC 后惜论,將全量或增量寫命令傳給 slave
?(5). Slave 更新數(shù)據(jù)
?(6). 此后 master 每次執(zhí)行寫命令许赃,都會將命令發(fā)給 slave,slave 收到后更新數(shù)據(jù) - 由于數(shù)據(jù)復(fù)制是異步的来涨,只保證最終一致性图焰,不保證強(qiáng)一致性,強(qiáng)一致性需讀寫都在 master
Sentinel HA
- Master 不會自動切換蹦掐,Sentinel 監(jiān)控 Redis 集群技羔,master 故障后從 slave 中選舉新 master
- Sentinel 同樣會有單點(diǎn)故障,所以需要 Sentinel 集群
- 配置 sentinel.conf
daemonize yes
# master-name 自定義卧抗,2 個 sentinel 認(rèn)為 master 死了才認(rèn)為該 master 不可用
sentinel monitor master-name <Master IP> <Master Port> 2
# 30 秒內(nèi)沒返回心跳就被認(rèn)為 master 死了
sentinel down-after-milliseconds master-name 30000
# 切換的 timeout 時間
sentinel failover-timeout master-name 180000
# 切換時藤滥,可以有多少個 slave 同時對新的 master 進(jìn)行同步
sentinel parallel-syncs master-name 1
監(jiān)控
?(1). Sentinel 通過配置文件發(fā)現(xiàn) master,向 master 發(fā)送 info 獲取 master 下的所有 slave
?(2). Sentinel 向 redis 發(fā)送 hello 信息(每秒一次)社裆,告知自己的 IP拙绊,端口,id 等信息
?(3). Sentinel 通過 redis 訂閱功能發(fā)現(xiàn)其他 sentinel 的 hello 信息泳秀,并建立連接
?(4). Sentinel 通過 ping 檢查 redis 狀態(tài)标沪,一定時間內(nèi)沒回復(fù)就被判為下線
?(5). 當(dāng)多數(shù) sentinel 都認(rèn)為 master 下線后開始主從切換,選舉新的 master
?(6). 向當(dāng)選的 slave 發(fā)送 slaveof no one 命令使其成為新的 master
?(7). 向其他 redis 發(fā)送 slaveof new-master-IP 命令嗜傅,與新的 master 同步客戶端通過 Sentinel 獲取 redis 集群的信息
from redis.sentinel import Sentinel
sentinel = Sentinel([('sentinel ip 1', 26379), ('sentinel ip 2', 26379), ('sentinel ip 3', 26379)], socket_timeout=0.5)
master = sentinel.master_for('master-name', socket_timeout=0.5, db=15)
master.set(key, value)
slave = sentinel.slave_for('master-name', socket_timeout=0.5, db=15)
value = slave.get(key)
masterIP, masterPort = sentinel.discover_master('master-name')
slaveList = sentinel.discover_slaves('master-name') # e.g. [(slave-1 IP, slave-1 Port), (slave-2 IP, slave-2 Port)]