Redis高可用原理,搭建与验证
一、redis-ha原理
1 原理
redis高可用采用的是哨兵(sentinel),多个redis-slave配备了多个哨兵进程,哨兵监控redis-master,一旦出现故障,将一台slave提升为master。客户端通过连接哨兵来获取Redis的master地址,发生故障,哨兵会报告新的服务器地址。
2 主备切换流程
2.1 一个哨兵认为master不可用,此时被仍为主观不可用,当有指定个数的哨兵都认为master不可用,此时状态进入客观不可用,进入主备切换流程。
2.2 进入主备切换流程后,需要一定个数的哨兵都同意进行进行主备切换授权,此时才真正开始进行主备切换。
2.3 开始进行主备切换的时候,一个sentinel被授权, 获得挂掉的master的最新配置版本号,主备切换后,该版本号用于最新配置。
2.4 一个sentinel成功对master进行主备切换,会把最新配置通过广播形式高速其他sentinel,其他sentinel则更新对应master配置。
2.5 当将一个slave选举为master并发送命令后,即使其他slave还没有针对新master重新配置自己,主备切换也被认为是成功的,所有sentinels将会发布新的配置信息。
一个相互通信的sentinel集群最终会采用版本号最高且相同的配置。
3 Sentine之间和Slaves之间自动发现机制
sentinel利用master的发布/订阅机制自动发现其他的sentinel节点
每个sentinel向每个master和slave发布/订阅频道 __sentinel__:hello 每秒发送一次消息来宣布存在,每个sentinel订阅每个master和slave的频道__sentinel__:hello的内容来发现未知sentinel,检测新的sentinel,则加入自身维护的master列表。
每个sentinel发送的消息中包含其当前维护的最新master配置,如果某个sentinel发现自己配置版本低于接收到的配置版本,则用新配置更新自己的master配置。
4 Slave选举与优先级
slave选举考虑以下几方面:
1) 与master断开连接的次数
2) Slave的优先级
3)数据复制下标(评估slave当前拥有多少master数据)
4)进程id
候选人规则:
1) slaves优先级越小排名越靠前
2)优先级相同,看复制下标,哪个从master接收的复制数据多,就越靠前。
3)优先级和下标相同,选择进程ID较小的哪个。
5 获取redis master地址的原理
步骤1:连接到第一个Sentinel
客户端需要遍历Sentinel列表的地址。对于每个地址,它需要使用一个较短的超时时间来尝试连接到Sentinel。如果发生错误或者超时,下一个Sentinel地址将会被尝试。
如果所有的Sentinel地址都尝试了都不能成功的话,一个错误会被返回给客户端。
第一个应答client的sentinel应该被放在列表的头部,这样在下一次重连的时候,我们将首先尝试这个sentinel是否可达来最小化延迟。
步骤2: 请求master地址
一旦与Sentinel的连接建立后,客户端应该在sentinel中尝试去执行下面的命令:
SENTINEL get-master-addr-by-name master-name
注意: 请将上述master-name替换为真实的master名称。
返回的结果是包含两部分:
一个ip:port的二元组。
如果接收到一个 ip:port,该地址就是被用于连接到redis master的地址。否则,如果接收到的是一个空的应答,客户端需要尝试列表中的下一个sentinel。
样例结果如下:
bash-4.4$ redis-cli -p 26379
127.0.0.1:26379> sentinel get-master-addr-by-name mymaster
1) "10.233.65.74"
2) "6379"
步骤3: 在目标实例上调用ROLE命令
一旦客户端发现了master的地址,它应该尝试与master建立一次连接,并且调用ROLE命令来验证该连接的实例是否真的是一个master。
如果该连接的实例不是master,客户端应该等待一个较短的时间,然后继续从步骤1开始执行。
处理重连
一旦服务名称被解析为master的地址,一个与redis master的连接建立后,每次当需要重新连接时,客户端应该使用sentinels从步骤1再次解析地址.
总结:
redis-ha解析获取master 地址的原理:
步骤1: 获取sentinel的ip:port组成的列表,遍历该列表,
1.1 如果当前sentinel的ip:port可以连接到该sentinel,
然后就发送命令:sentinel get-master-addr-by-name <master-name>
来获取master的ip地址,并执行步骤2
1.2 否则,继续遍历下一个sentinel的ip:port
步骤2: 建立与master的连接,并执行一个ROLE命令来判定连接的是否真的是master,
2.1 如果是master,保持与master的连接。
2.2 否则,转步骤1
如果现主备切换等需要重新连接的情况,会再次从步骤1开始执行,直到解析出master的地址。
二、redis-ha环境搭建
1 下载镜像
下面是社区镜像地址
https://quay.io/repository/smile/redis?tag=latest&tab=tags
注意: 社区使用的镜像是:
quay.io/smile/redis:4.0.11-r1
2 下载redis-ha的charts
具体地址:
https://github.com/helm/charts/tree/master/stable/redis-ha
注意: 该代码必须是基于statefulset的redis-ha而不是原来基于deployment的redis-ha
3 修改redis-ha的charts
3.1 在redis-ha的values.yaml中redis.config下添加一行
protected-mode: "no"
具体修改后的样例如下所示:
redis:
port: 6379
masterGroupName: mymaster
config:
## Additional redis conf options can be added below
## For all available options see http://download.redis.io/redis-stable/redis.conf
min-slaves-to-write: 1
min-slaves-max-lag: 5 # Value in seconds
maxmemory: "0" # Max memory to use for each redis instance. Default is unlimited.
maxmemory-policy: "volatile-lru" # Max memory policy to use for each redis instance. Default is volatile-lru.
# Determines if scheduled RDB backups are created. Default is false.
# Please note that local (on-disk) RDBs will still be created when re-syncing with a new slave. The only way to prevent this is to enable diskless replication.
save: "900 1"
# When enabled, directly sends the RDB over the wire to slaves, without using the disk as intermediate storage. Default is false.
repl-diskless-sync: "yes"
rdbcompression: "yes"
rdbchecksum: "yes"
protected-mode: "no"
解释:
redis没有bind和密码的情况下,保护模式开启,拒绝其他sentinel连接,会导致主备切换不成功。所以要解除保护模式。即进行上述操作。
参考:
https://blog.csdn.net/csdn_ds/article/details/72550898
3.2 在redis-ha的redis-ha-configmap.yaml中
sentinel.conf的
dir "/data"
下添加如下内容:
{
{- $protectedMode := index .Values.redis.config "protected-mode" }}
protected-mode {
{ $protectedMode }}
具体修改后的样例如下所示:
sentinel.conf: |
{
{- if .Values.sentinel.customConfig }}
{
{ .Values.sentinel.customConfig | indent 4 }}
{
{- else }}
dir "/data"
{
{- $protectedMode := index .Values.redis.config "protected-mode" }}
protected-mode {
{ $protectedMode }}
{
{- $root := . -}}
3.3 根据需要是否修改persistentVolume的storageClass
默认是:
persistentVolume:
enabled: true
## redis-ha data Persistent Volume Storage Class
## If defined, storageClassName: <stor