什么叫系统不可用:

什么是99.99高可用性:

高可用计算规则,全年 系统可用的时间 / 全年

redis不可用是什么?

redis主从基于哨兵模式的高可用:

哨兵的主要功能:

(1)集群监控,负责监控redis master和slave进程是否正常正常工作

(2)消息通知,如果某个redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员

(3)故障迁移,如果master node挂掉了,会自动转移到slave node 上

(4) 配置中心,如果故障发生了,通知client客户端新的master地址

哨兵的核心知识:

(1) 哨兵至少需要 3 个实例,保证自己的健壮性

(2) 哨兵 + redis,是不会保证数据零丢失的,只能保证redis集群的高可用

(3) 哨兵 + redis ,这种复制的架构,尽量多做容灾演练

为什么哨兵最少要三台?:

2台的 majority = 2, (reids 服务器发生问题之后,需要两台都同意,才能执行迁移操作)

3 台的 majority = 2, (两台哨兵同意即可)

4 台的 majority = 2, (两台哨兵同意即可))

5 台的 majority = 3 (三台哨兵同意即可))

如果哨兵只有两天,呢么其中一台发生问题,即时检查到了redis master发生问题,也不能执行redis迁移(必须要两台都同意迁移,才可以迁移)

redis哨兵主备切换的数据丢失问题:

1)异步复制:

2) 集群脑裂

解决异步复制和脑裂导致的数据丢失(redis.conf文件中配置):

min-slaves-to-write 1

min-slaves-max-lag 10  

 要求至少有一个slave,复制和同步数据的延迟不能超过10秒

 如果说一旦所有的slave,数据复制和同步延迟都超过了10秒,这时候master将不会接收任何写请求

redis哨兵核心底层原理:

1.sdown 和 odown

sdown和odown两种失败状态

sdown是主观宕机,就一个哨兵如果自己觉得一个master宕机了,那么就是主观宕机

odown是客观宕机,如果quorum数量的哨兵都觉得一个master宕机了,那么就是客观宕机

sdown达成的条件很简单,如果一个哨兵ping一个master,超过了is-master-down-after-milliseconds指定的毫秒数之后,就主观认为master宕机

sdown到odown转换的条件很简单,如果一个哨兵在指定时间内,收到了quorum指定数量的其他哨兵也认为那个master是sdown了,那么就认为是odown了,客观认为master宕机

2.哨兵集群的自动发现机制

哨兵互相之间的发现,是通过redis的pub/sub系统实现的,每个哨兵都会往__sentinel__:hello这个channel里发送一个消息,这时候所有其他哨兵都可以消费到这个消息,并感知到其他的哨兵的存在

每隔两秒钟,每个哨兵都会往自己监控的某个master+slaves对应的__sentinel__:hello channel里发送一个消息,内容是自己的host、ip和runid还有对这个master的监控配置

每个哨兵也会去监听自己监控的每个master+slaves对应的__sentinel__:hello channel,然后去感知到同样在监听这个master+slaves的其他哨兵的存在

每个哨兵还会跟其他哨兵交换对master的监控配置,互相进行监控配置的同步

3.slave配置的自动纠正

哨兵会负责自动纠正slave的一些配置,比如slave如果要成为潜在的master候选人,哨兵会确保slave在复制现有master的数据; 如果slave连接到了一个错误的master上,比如故障转移之后,那么哨兵会确保它们连接到正确的master上

哨兵会自动更改redis的配置文件

4.slave->master选举算法

如果一个master被认为odown了,而且majority哨兵都允许了主备切换,那么某个哨兵就会执行主备切换操作,此时首先要选举一个slave来

会考虑slave的一些信息

(1)跟master断开连接的时长

(2)slave优先级

(3)复制offset

(4)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比较小的那个slave

5.quorum和majority

每次一个哨兵要做主备切换,首先需要quorum数量的哨兵认为odown,然后选举出一个哨兵来做切换,这个哨兵还得得到majority哨兵的授权,才能正式执行切换

如果quorum < majority,比如5个哨兵,majority就是3,quorum设置为2,那么就3个哨兵授权就可以执行切换

但是如果quorum >= majority,那么必须quorum数量的哨兵都授权,比如5个哨兵,quorum是5,那么必须5个哨兵都同意授权,才能执行切换

6.configuration epoch

哨兵会对一套redis master+slave进行监控,有相应的监控的配置

执行切换的那个哨兵,会从要切换到的新master(salve->master)那里得到一个configuration epoch,这就是一个version号,每次切换的version号都必须是唯一的

如果第一个选举出的哨兵切换失败了,那么其他哨兵,会等待failover-timeout时间,然后接替继续执行切换,此时会重新获取一个新的configuration epoch,作为新的version号

7.configuraiton传播

哨兵完成切换之后,会在自己本地更新生成最新的master配置,然后同步给其他的哨兵,就是通过之前说的pub/sub消息机制

这里之前的version号就很重要了,因为各种消息都是通过一个channel去发布和监听的,所以一个哨兵完成一次新的切换之后,新的master配置是跟着新的version号的

其他的哨兵都是根据版本号的大小来更新自己的master配置的

配置哨兵模式(主从状态监控)

1. Redis Sentinel搭建

1.1. Redis Sentinel的部署须知

1. 一个稳健的 Redis Sentinel 集群,应该使用至少 三个 Sentinel 实例,并且保证讲这些实例放到 不同的机器 上,甚至不同的 物理区域。

2. Sentinel 无法保证 强一致性。

3. 常见的 客户端应用库 都支持 Sentinel

4. Sentinel 需要通过不断的 测试 和 观察,才能保证高可用。

1.2. Redis Sentinel的配置文件

# 哨兵sentinel实例运行的端口,默认26379  

port 26379

# 哨兵sentinel的工作目录

dir ./

# 哨兵sentinel监控的redis主节点的

## ip:主机ip地址

## port:哨兵端口号

## master-name:可以自己命名的主节点名字(只能由字母A-z、数字0-9 、这三个字符".-_"组成。)

## quorum:当这些quorum个数sentinel哨兵认为master主节点失联 那么这时 客观上认为主节点失联了  

# sentinel monitor <master-name> <ip> <redis-port> <quorum>  

sentinel monitor mymaster 127.0.0.1 6379 2

# 当在Redis实例中开启了requirepass <foobared>,所有连接Redis实例的客户端都要提供密码。

# sentinel auth-pass <master-name> <password>  

sentinel auth-pass mymaster 123456  

# 指定主节点应答哨兵sentinel的最大时间间隔,超过这个时间,哨兵主观上认为主节点下线,默认30秒  

# sentinel down-after-milliseconds <master-name> <milliseconds>

sentinel down-after-milliseconds mymaster 30000  

# 指定了在发生failover主备切换时,最多可以有多少个slave同时对新的master进行同步。这个数字越小,完成failover所需的时间就越长;反之,但是如果这个数字越大,就意味着越多的slave因为replication而不可用。可以通过将这个值设为1,来保证每次只有一个slave,处于不能处理命令请求的状态。

# sentinel parallel-syncs <master-name> <numslaves>

sentinel parallel-syncs mymaster 1  

# 故障转移的超时时间failover-timeout,默认三分钟,可以用在以下这些方面:

## 1. 同一个sentinel对同一个master两次failover之间的间隔时间。  

## 2. 当一个slave从一个错误的master那里同步数据时开始,直到slave被纠正为从正确的master那里同步数据时结束。  

## 3. 当想要取消一个正在进行的failover时所需要的时间。

## 4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来同步数据了

# sentinel failover-timeout <master-name> <milliseconds>  

sentinel failover-timeout mymaster 180000

# 当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本。一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。

# 对于脚本的运行结果有以下规则:  

## 1. 若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10。

## 2. 若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。  

## 3. 如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。

# sentinel notification-script <master-name> <script-path>  

sentinel notification-script mymaster /var/redis/notify.sh

# 这个脚本应该是通用的,能被多次调用,不是针对性的。

# sentinel client-reconfig-script <master-name> <script-path>

sentinel client-reconfig-script mymaster /var/redis/reconfig.sh

1.3. Redis Sentinel的节点规划

角色

IP地址

端口号

Redis Master

10.206.20.231

16379

Redis Slave1

10.206.20.231

26379

Redis Slave2

10.206.20.231

36379

Redis Sentinel1

10.206.20.231

16380

Redis Sentinel2

10.206.20.231

26380

Redis Sentinel3

10.206.20.231

36380

1.4. Redis Sentinel的配置搭建

搭建reids-server 集群:

1.4.1. Redis-Server的配置管理

分别拷贝三份 redis.conf 文件到 /usr/local/redis-sentinel 目录下面。三个配置文件分别对应 masterslave1 和 slave2 三个 Redis 节点的 启动配置

$ sudo cp /usr/local/redis-4.0.11/redis.conf /usr/local/redis-sentinel/redis-16379.conf

$ sudo cp /usr/local/redis-4.0.11/redis.conf /usr/local/redis-sentinel/redis-26379.conf

$ sudo cp /usr/local/redis-4.0.11/redis.conf /usr/local/redis-sentinel/redis-36379.conf

分别修改三份配置文件如下:

主节点:redis-16379.conf

daemonize yes

pidfile /var/run/redis-16379.pid

logfile /var/log/redis/redis-16379.log

port 16379

bind 0.0.0.0

timeout 300

databases 16

dbfilename dump-16379.db

dir ./redis-workdir

masterauth 123456

requirepass 123456

从节点1:redis-26379.conf

daemonize yes

pidfile /var/run/redis-26379.pid

logfile /var/log/redis/redis-26379.log

port 26379

bind 0.0.0.0

timeout 300

databases 16

dbfilename dump-26379.db

dir ./redis-workdir

masterauth 123456

requirepass 123456

slaveof 127.0.0.1 16379

从节点2:redis-36379.conf

daemonize yes

pidfile /var/run/redis-36379.pid

logfile /var/log/redis/redis-36379.log

port 36379

bind 0.0.0.0

timeout 300

databases 16

dbfilename dump-36379.db

dir ./redis-workdir

masterauth 123456

requirepass 123456

slaveof 127.0.0.1 16379

如果要做 自动故障转移,建议所有的 redis.conf 都设置 masterauth。因为 自动故障 只会重写 主从关系,即 slaveof,不会自动写入 masterauth。如果 Redis 原本没有设置密码,则可以忽略。

1.4.2. Redis-Server启动验证

按顺序分别启动 1637926379 和 36379 三个 Redis 节点,启动命令和启动日志如下:

Redis 的启动命令:

$ sudo redis-server /usr/local/redis-sentinel/redis-16379.conf

$ sudo redis-server /usr/local/redis-sentinel/redis-26379.conf

$ sudo redis-server /usr/local/redis-sentinel/redis-36379.conf

查看 Redis 的启动进程:

$ ps -ef | grep redis-server

   0  7127     1   0  2:16下午 ??         0:01.84 redis-server 0.0.0.0:16379

   0  7133     1   0  2:16下午 ??         0:01.73 redis-server 0.0.0.0:26379

   0  7137     1   0  2:16下午 ??         0:01.70 redis-server 0.0.0.0:36379

查看 Redis 的启动日志:

节点 redis-16379

$ cat /var/log/redis/redis-16379.log

7126:C 22 Aug 14:16:38.907 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo

7126:C 22 Aug 14:16:38.908 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=7126, just started

7126:C 22 Aug 14:16:38.908 # Configuration loaded

7127:M 22 Aug 14:16:38.910 * Increased maximum number of open files to 10032 (it was originally set to 256).

7127:M 22 Aug 14:16:38.912 * Running mode=standalone, port=16379.

7127:M 22 Aug 14:16:38.913 # Server initialized

7127:M 22 Aug 14:16:38.913 * Ready to accept connections

7127:M 22 Aug 14:16:48.416 * Slave 127.0.0.1:26379 asks for synchronization

7127:M 22 Aug 14:16:48.416 * Full resync requested by slave 127.0.0.1:26379

7127:M 22 Aug 14:16:48.416 * Starting BGSAVE for SYNC with target: disk

7127:M 22 Aug 14:16:48.416 * Background saving started by pid 7134

7134:C 22 Aug 14:16:48.433 * DB saved on disk

7127:M 22 Aug 14:16:48.487 * Background saving terminated with success

7127:M 22 Aug 14:16:48.494 * Synchronization with slave 127.0.0.1:26379 succeeded

7127:M 22 Aug 14:16:51.848 * Slave 127.0.0.1:36379 asks for synchronization

7127:M 22 Aug 14:16:51.849 * Full resync requested by slave 127.0.0.1:36379

7127:M 22 Aug 14:16:51.849 * Starting BGSAVE for SYNC with target: disk

7127:M 22 Aug 14:16:51.850 * Background saving started by pid 7138

7138:C 22 Aug 14:16:51.862 * DB saved on disk

7127:M 22 Aug 14:16:51.919 * Background saving terminated with success

7127:M 22 Aug 14:16:51.923 * Synchronization with slave 127.0.0.1:36379 succeeded

以下两行日志日志表明,redis-16379 作为 Redis 的 主节点redis-26379 和 redis-36379 作为 从节点,从 主节点 同步数据。

7127:M 22 Aug 14:16:48.416 * Slave 127.0.0.1:26379 asks for synchronization

7127:M 22 Aug 14:16:51.848 * Slave 127.0.0.1:36379 asks for synchronization

节点 redis-26379

$ cat /var/log/redis/redis-26379.log

7132:C 22 Aug 14:16:48.407 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo

7132:C 22 Aug 14:16:48.408 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=7132, just started

7132:C 22 Aug 14:16:48.408 # Configuration loaded

7133:S 22 Aug 14:16:48.410 * Increased maximum number of open files to 10032 (it was originally set to 256).

7133:S 22 Aug 14:16:48.412 * Running mode=standalone, port=26379.

7133:S 22 Aug 14:16:48.413 # Server initialized

7133:S 22 Aug 14:16:48.413 * Ready to accept connections

7133:S 22 Aug 14:16:48.413 * Connecting to MASTER 127.0.0.1:16379

7133:S 22 Aug 14:16:48.413 * MASTER <-> SLAVE sync started

7133:S 22 Aug 14:16:48.414 * Non blocking connect for SYNC fired the event.

7133:S 22 Aug 14:16:48.414 * Master replied to PING, replication can continue...

7133:S 22 Aug 14:16:48.415 * Partial resynchronization not possible (no cached master)

7133:S 22 Aug 14:16:48.417 * Full resync from master: 211d3b4eceaa3af4fe5c77d22adf06e1218e0e7b:0

7133:S 22 Aug 14:16:48.494 * MASTER <-> SLAVE sync: receiving 176 bytes from master

7133:S 22 Aug 14:16:48.495 * MASTER <-> SLAVE sync: Flushing old data

7133:S 22 Aug 14:16:48.496 * MASTER <-> SLAVE sync: Loading DB in memory

7133:S 22 Aug 14:16:48.498 * MASTER <-> SLAVE sync: Finished with success

节点 redis-36379

$ cat /var/log/redis/redis-36379.log

7136:C 22 Aug 14:16:51.839 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo

7136:C 22 Aug 14:16:51.840 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=7136, just started

7136:C 22 Aug 14:16:51.841 # Configuration loaded

7137:S 22 Aug 14:16:51.843 * Increased maximum number of open files to 10032 (it was originally set to 256).

7137:S 22 Aug 14:16:51.845 * Running mode=standalone, port=36379.

7137:S 22 Aug 14:16:51.845 # Server initialized

7137:S 22 Aug 14:16:51.846 * Ready to accept connections

7137:S 22 Aug 14:16:51.846 * Connecting to MASTER 127.0.0.1:16379

7137:S 22 Aug 14:16:51.847 * MASTER <-> SLAVE sync started

7137:S 22 Aug 14:16:51.847 * Non blocking connect for SYNC fired the event.

7137:S 22 Aug 14:16:51.847 * Master replied to PING, replication can continue...

7137:S 22 Aug 14:16:51.848 * Partial resynchronization not possible (no cached master)

7137:S 22 Aug 14:16:51.850 * Full resync from master: 211d3b4eceaa3af4fe5c77d22adf06e1218e0e7b:14

7137:S 22 Aug 14:16:51.923 * MASTER <-> SLAVE sync: receiving 176 bytes from master

7137:S 22 Aug 14:16:51.923 * MASTER <-> SLAVE sync: Flushing old data

7137:S 22 Aug 14:16:51.924 * MASTER <-> SLAVE sync: Loading DB in memory

7137:S 22 Aug 14:16:51.927 * MASTER <-> SLAVE sync: Finished with success

配置Sentinel:

1.4.3. Sentinel的配置管理

分别拷贝三份 redis-sentinel.conf 文件到 /usr/local/redis-sentinel 目录下面。三个配置文件分别对应 masterslave1 和 slave2 三个 Redis 节点的 哨兵配置。

$ sudo cp /usr/local/redis-4.0.11/sentinel.conf /usr/local/redis-sentinel/sentinel-16380.conf

$ sudo cp /usr/local/redis-4.0.11/sentinel.conf /usr/local/redis-sentinel/sentinel-26380.conf

$ sudo cp /usr/local/redis-4.0.11/sentinel.conf /usr/local/redis-sentinel/sentinel-36380.conf

节点1:sentinel-16380.conf

protected-mode no

bind 0.0.0.0

port 16380

daemonize yes

sentinel monitor master 127.0.0.1 16379 2

sentinel down-after-milliseconds master 5000

sentinel failover-timeout master 180000

sentinel parallel-syncs master 1

sentinel auth-pass master 123456

logfile /var/log/redis/sentinel-16380.log

节点2:sentinel-26380.conf

protected-mode no

bind 0.0.0.0

port 26380

daemonize yes

sentinel monitor master 127.0.0.1 16379 2

sentinel down-after-milliseconds master 5000

sentinel failover-timeout master 180000

sentinel parallel-syncs master 1

sentinel auth-pass master 123456

logfile /var/log/redis/sentinel-26380.log

节点3:sentinel-36380.conf

protected-mode no

bind 0.0.0.0

port 36380

daemonize yes

sentinel monitor master 127.0.0.1 16379 2

sentinel down-after-milliseconds master 5000

sentinel failover-timeout master 180000

sentinel parallel-syncs master 1

sentinel auth-pass master 123456

logfile /var/log/redis/sentinel-36380.log

1.4.4. Sentinel启动验证

按顺序分别启动 1638026380 和 36380 三个 Sentinel 节点,启动命令和启动日志如下:

$ sudo redis-sentinel /usr/local/redis-sentinel/sentinel-16380.conf

$ sudo redis-sentinel /usr/local/redis-sentinel/sentinel-26380.conf

$ sudo redis-sentinel /usr/local/redis-sentinel/sentinel-36380.conf

查看 Sentinel 的启动进程:

$ ps -ef | grep redis-sentinel

   0  7954     1   0  3:30下午 ??         0:00.05 redis-sentinel 0.0.0.0:16380 [sentinel]

   0  7957     1   0  3:30下午 ??         0:00.05 redis-sentinel 0.0.0.0:26380 [sentinel]

   0  7960     1   0  3:30下午 ??         0:00.04 redis-sentinel 0.0.0.0:36380 [sentinel]

查看 Sentinel 的启动日志:

节点 sentinel-16380

$ cat /var/log/redis/sentinel-16380.log

7953:X 22 Aug 15:30:27.245 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo

7953:X 22 Aug 15:30:27.245 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=7953, just started

7953:X 22 Aug 15:30:27.245 # Configuration loaded

7954:X 22 Aug 15:30:27.247 * Increased maximum number of open files to 10032 (it was originally set to 256).

7954:X 22 Aug 15:30:27.249 * Running mode=sentinel, port=16380.

7954:X 22 Aug 15:30:27.250 # Sentinel ID is 69d05b86a82102a8919231fd3c2d1f21ce86e000

7954:X 22 Aug 15:30:27.250 # +monitor master master 127.0.0.1 16379 quorum 2

7954:X 22 Aug 15:30:32.286 # +sdown sentinel fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7 127.0.0.1 36380 @ master 127.0.0.1 16379

7954:X 22 Aug 15:30:34.588 # -sdown sentinel fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7 127.0.0.1 36380 @ master 127.0.0.1 16379

sentinel-16380 节点的 Sentinel ID 为 69d05b86a82102a8919231fd3c2d1f21ce86e000,并通过 Sentinel ID 把自身加入 sentinel 集群中。

节点 sentinel-26380

$ cat /var/log/redis/sentinel-26380.log

7956:X 22 Aug 15:30:30.900 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo

7956:X 22 Aug 15:30:30.901 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=7956, just started

7956:X 22 Aug 15:30:30.901 # Configuration loaded

7957:X 22 Aug 15:30:30.904 * Increased maximum number of open files to 10032 (it was originally set to 256).

7957:X 22 Aug 15:30:30.905 * Running mode=sentinel, port=26380.

7957:X 22 Aug 15:30:30.906 # Sentinel ID is 21e30244cda6a3d3f55200bcd904d0877574e506

7957:X 22 Aug 15:30:30.906 # +monitor master master 127.0.0.1 16379 quorum 2

7957:X 22 Aug 15:30:30.907 * +slave slave 127.0.0.1:26379 127.0.0.1 26379 @ master 127.0.0.1 16379

7957:X 22 Aug 15:30:30.911 * +slave slave 127.0.0.1:36379 127.0.0.1 36379 @ master 127.0.0.1 16379

7957:X 22 Aug 15:30:36.311 * +sentinel sentinel fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7 127.0.0.1 36380 @ master 127.0.0.1 16379

sentinel-26380 节点的 Sentinel ID21e30244cda6a3d3f55200bcd904d0877574e506,并通过 Sentinel ID 把自身加入 sentinel 集群中。此时 sentinel 集群中已有 sentinel-16380sentinel-26380 两个节点。

节点 sentinel-36380

$ cat /var/log/redis/sentinel-36380.log

7959:X 22 Aug 15:30:34.273 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo

7959:X 22 Aug 15:30:34.274 # Redis version=4.0.11, bits=64, commit=00000000, modified=0, pid=7959, just started

7959:X 22 Aug 15:30:34.274 # Configuration loaded

7960:X 22 Aug 15:30:34.276 * Increased maximum number of open files to 10032 (it was originally set to 256).

7960:X 22 Aug 15:30:34.277 * Running mode=sentinel, port=36380.

7960:X 22 Aug 15:30:34.278 # Sentinel ID is fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7

7960:X 22 Aug 15:30:34.278 # +monitor master master 127.0.0.1 16379 quorum 2

7960:X 22 Aug 15:30:34.279 * +slave slave 127.0.0.1:26379 127.0.0.1 26379 @ master 127.0.0.1 16379

7960:X 22 Aug 15:30:34.283 * +slave slave 127.0.0.1:36379 127.0.0.1 36379 @ master 127.0.0.1 16379

7960:X 22 Aug 15:30:34.993 * +sentinel sentinel 21e30244cda6a3d3f55200bcd904d0877574e506 127.0.0.1 26380 @ master 127.0.0.1 16379

sentinel-36380 节点的 Sentinel IDfd166dc66425dc1d9e2670e1f17cb94fe05f5fc7,并通过 Sentinel ID 把自身加入 sentinel 集群中。此时 sentinel 集群中已有 sentinel-16380sentinel-26380sentinel-36380 三个节点。

1.4.5. Sentinel配置刷新(这里注意一下,sentinel的配置文件是自动刷新的)

sentinel-16380.conf 文件新生成如下的配置项:

# Generated by CONFIG REWRITE

dir "/usr/local/redis-sentinel"

sentinel config-epoch master 0

sentinel leader-epoch master 0

sentinel known-slave master 127.0.0.1 36379

sentinel known-slave master 127.0.0.1 26379

sentinel known-sentinel master 127.0.0.1 26380 21e30244cda6a3d3f55200bcd904d0877574e506

sentinel known-sentinel master 127.0.0.1 36380 fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7

sentinel current-epoch 0

可以注意到,sentinel-16380.conf 刷新写入了 Redis 主节点关联的所有 从节点 redis-26379redis-36379,同时写入了其余两个 Sentinel 节点 sentinel-26380sentinel-36380IP 地址,端口号Sentinel ID

# Generated by CONFIG REWRITE

dir "/usr/local/redis-sentinel"

sentinel config-epoch master 0

sentinel leader-epoch master 0

sentinel known-slave master 127.0.0.1 26379

sentinel known-slave master 127.0.0.1 36379

sentinel known-sentinel master 127.0.0.1 36380 fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7

sentinel known-sentinel master 127.0.0.1 16380 69d05b86a82102a8919231fd3c2d1f21ce86e000

sentinel current-epoch 0

可以注意到,sentinel-26380.conf 刷新写入了 Redis 主节点关联的所有 从节点 redis-26379redis-36379,同时写入了其余两个 Sentinel 节点 sentinel-36380sentinel-16380IP 地址,端口号Sentinel ID

# Generated by CONFIG REWRITE

dir "/usr/local/redis-sentinel"

sentinel config-epoch master 0

sentinel leader-epoch master 0

sentinel known-slave master 127.0.0.1 36379

sentinel known-slave master 127.0.0.1 26379

sentinel known-sentinel master 127.0.0.1 16380 69d05b86a82102a8919231fd3c2d1f21ce86e000

sentinel known-sentinel master 127.0.0.1 26380 21e30244cda6a3d3f55200bcd904d0877574e506

sentinel current-epoch 0

可以注意到,sentinel-36380.conf 刷新写入了 Redis 主节点关联的所有 从节点 redis-26379redis-36379,同时写入了其余两个 Sentinel 节点 sentinel-16380sentinel-26380IP 地址,端口号Sentinel ID

1.5. Sentinel时客户端命令

检查其他 Sentinel 节点的状态,返回 PONG 为正常。

> PING sentinel

显示被监控的所有 主节点 以及它们的状态。

> SENTINEL masters

显示指定 主节点 的信息和状态。

> SENTINEL master <master_name>

显示指定 主节点 的所有 从节点 以及它们的状态。

> SENTINEL slaves <master_name>

返回指定 主节点IP 地址和 端口。如果正在进行 failover 或者 failover 已经完成,将会显示被提升为 主节点从节点IP 地址和 端口

> SENTINEL get-master-addr-by-name <master_name>

重置名字匹配该 正则表达式 的所有的 主节点 的状态信息,清除它之前的 状态信息,以及 从节点 的信息。

> SENTINEL reset <pattern>

强制当前 Sentinel 节点执行 failover,并且不需要得到其他 Sentinel 节点的同意。但是 failover 后会将 最新的配置 发送给其他 Sentinel 节点。

>SENTINEL failover <master_name>

2. Redis Sentinel故障切换与恢复

2.1. Redis CLI客户端跟踪

上面的日志显示,redis-16379 节点为 主节点,它的进程 ID 为 7127。为了模拟 Redis 主节点故障,强制杀掉这个进程。

$ kill -9 7127

使用 redis-cli 客户端命令进入 sentinel-16380 节点,查看 Redis 节点 的状态信息。

$ redis-cli -p 16380

查看 Redis 主从集群的 主节点 信息。可以发现 redis-26379 晋升为 新的主节点

127.0.0.1:16380> SENTINEL master master

1) "name"

2) "master"

3) "ip"

4) "127.0.0.1"

5) "port"

6) "26379"

7) "runid"

8) "b8ca3b468a95d1be5efe1f50c50636cafe48c59f"

9) "flags"

10) "master"

11) "link-pending-commands"

12) "0"

13) "link-refcount"

14) "1"

15) "last-ping-sent"

16) "0"

17) "last-ok-ping-reply"

18) "588"

19) "last-ping-reply"

20) "588"

21) "down-after-milliseconds"

22) "5000"

23) "info-refresh"

24) "9913"

25) "role-reported"

26) "master"

27) "role-reported-time"

28) "663171"

29) "config-epoch"

30) "1"

31) "num-slaves"

32) "2"

33) "num-other-sentinels"

34) "2"

35) "quorum"

36) "2"

37) "failover-timeout"

38) "180000"

39) "parallel-syncs"

40) "1"

2.2. Redis Sentinel日志跟踪

查看任意 Sentinel 节点的日志如下:

7954:X 22 Aug 18:40:22.504 # +tilt #tilt mode entered

7954:X 22 Aug 18:40:32.197 # +tilt #tilt mode entered

7954:X 22 Aug 18:41:02.241 # -tilt #tilt mode exited

7954:X 22 Aug 18:48:24.550 # +sdown master master 127.0.0.1 16379

7954:X 22 Aug 18:48:24.647 # +new-epoch 1

7954:X 22 Aug 18:48:24.651 # +vote-for-leader fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7 1

7954:X 22 Aug 18:48:25.678 # +odown master master 127.0.0.1 16379 #quorum 3/2

7954:X 22 Aug 18:48:25.678 # Next failover delay: I will not start a failover before Wed Aug 22 18:54:24 2018

7954:X 22 Aug 18:48:25.709 # +config-update-from sentinel fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7 127.0.0.1 36380 @ master 127.0.0.1 16379

7954:X 22 Aug 18:48:25.710 # +switch-master master 127.0.0.1 16379 127.0.0.1 26379

7954:X 22 Aug 18:48:25.710 * +slave slave 127.0.0.1:36379 127.0.0.1 36379 @ master 127.0.0.1 26379

7954:X 22 Aug 18:48:25.711 * +slave slave 127.0.0.1:16379 127.0.0.1 16379 @ master 127.0.0.1 26379

7954:X 22 Aug 18:48:30.738 # +sdown slave 127.0.0.1:16379 127.0.0.1 16379 @ master 127.0.0.1 26379

7954:X 22 Aug 19:38:23.479 # -sdown slave 127.0.0.1:16379 127.0.0.1 16379 @ master 127.0.0.1 26379

分析日志,可以发现 redis-16329 节点先进入 sdown 主观下线 状态。

+sdown master master 127.0.0.1 16379

哨兵检测到 redis-16329 出现故障,Sentinel 进入一个 新纪元,从 0 变为 1

+new-epoch 1

三个 Sentinel 节点开始协商 主节点 的状态,判断其是否需要 客观下线

+vote-for-leader fd166dc66425dc1d9e2670e1f17cb94fe05f5fc7 1

超过 quorum 个数的 Sentinel 节点认为 主节点 出现故障,redis-16329 节点进入 客观下线状态。

+odown master master 127.0.0.1 16379 #quorum 3/2

Sentinal 进行 自动故障切换,协商选定 redis-26329 节点作为新的 主节点

+switch-master master 127.0.0.1 16379 127.0.0.1 26379

redis-36329 节点和已经 客观下线 的 redis-16329 节点成为 redis-26479 的 从节点

7954:X 22 Aug 18:48:25.710 * +slave slave 127.0.0.1:36379 127.0.0.1 36379 @ master 127.0.0.1 26379

7954:X 22 Aug 18:48:25.711 * +slave slave 127.0.0.1:16379 127.0.0.1 16379 @ master 127.0.0.1 26379

2.3. Redis的配置文件(自动刷新的)

分别查看三个 redis 节点的配置文件,发生 主从切换 时 redis.conf 的配置会自动发生刷新

节点 redis-16379

daemonize yes

pidfile "/var/run/redis-16379.pid"

logfile "/var/log/redis/redis-16379.log"

port 16379

bind 0.0.0.0

timeout 300

databases 16

dbfilename "dump-16379.db"

dir "/usr/local/redis-sentinel/redis-workdir"

masterauth "123456"

requirepass "123456"

节点 redis-26379

daemonize yes

pidfile "/var/run/redis-26379.pid"

logfile "/var/log/redis/redis-26379.log"

port 26379

bind 0.0.0.0

timeout 300

databases 16

dbfilename "dump-26379.db"

dir "/usr/local/redis-sentinel/redis-workdir"

masterauth "123456"

requirepass "123456"

节点 redis-36379

daemonize yes

pidfile "/var/run/redis-36379.pid"

logfile "/var/log/redis/redis-36379.log"

port 36379

bind 0.0.0.0

timeout 300

databases 16

dbfilename "dump-36379.db"

dir "/usr/local/redis-sentinel/redis-workdir"

masterauth "123456"

requirepass "123456"

slaveof 127.0.0.1 26379

分析redis-26379 节点 slaveof 配置被移除,晋升为 主节点redis-16379 节点处于 宕机状态redis-36379slaveof 配置更新为 127.0.0.1 redis-26379,成为 redis-26379从节点

重启节点 redis-16379。待正常启动后,再次查看它的 redis.conf 文件,配置如下:

daemonize yes

pidfile "/var/run/redis-16379.pid"

logfile "/var/log/redis/redis-16379.log"

port 16379

bind 0.0.0.0

timeout 300

databases 16

dbfilename "dump-16379.db"

dir "/usr/local/redis-sentinel/redis-workdir"

masterauth "123456"

requirepass "123456"

# Generated by CONFIG REWRITE

slaveof 127.0.0.1 26379

节点 redis-16379 的配置文件新增一行 slaveof 配置属性,指向 redis-26379,即成为 新的主节点 的 从节点

小结

本文首先对 Redis 实现高可用的几种模式做出了阐述,指出了 Redis 主从复制 的不足之处,进一步引入了 Redis Sentinel 哨兵模式 的相关概念,深入说明了 Redis Sentinel具体功能基本原理高可用搭建自动故障切换 验证等。

当然,Redis Sentinel 仅仅解决了 高可用 的问题,对于 主节点 单点写入和单节点无法扩容等问题,还需要引入 Redis Cluster 集群模式 予以解决。

3.哨兵节点管理

3.1 哨兵节点的增加和删除

增加sentinal,会自动发现

删除sentinal的步骤

(1)停止sentinal进程

(2)SENTINEL RESET *,在所有sentinal上执行,清理所有的master状态

(3)SENTINEL MASTER mastername,在所有sentinal上执行,查看所有sentinal对数量是否达成了一致

3.2 slave的永久下线

让master摘除某个已经下线的slave:SENTINEL RESET mastername,在所有的哨兵上面执行

3.3 slave切换为Master的优先级

slave->master选举优先级:slave-priority,值越小优先级越高

3.4 基于哨兵集群架构下的安全认证

每个slave都有可能切换成master,所以每个实例都要配置两个指令

master上启用安全认证,requirepass

master连接口令,masterauth

sentinal,sentinel auth-pass <master-group-name> <pass>

3.5 容灾演练

通过哨兵看一下当前的master:SENTINEL get-master-addr-by-name mymaster

把master节点kill -9掉,pid文件也删除掉

查看sentinal的日志,是否出现+sdown字样,识别出了master的宕机问题; 然后出现+odown字样,就是指定的quorum哨兵数量,都认为master宕机了

(1)三个哨兵进程都认为master是sdown了

(2)超过quorum指定的哨兵进程都认为sdown之后,就变为odown

(3)哨兵1是被选举为要执行后续的主备切换的那个哨兵

(4)哨兵1去新的master(slave)获取了一个新的config version

(5)尝试执行failover

(6)投票选举出一个slave区切换成master,每个哨兵都会执行一次投票

(7)让salve,slaveof noone,不让它去做任何节点的slave了; 把slave提拔成master; 旧的master认为不再是master了

(8)哨兵就自动认为之前的187:6379变成了slave了,19:6379变成了master了

(9)哨兵去探查了一下187:6379这个salve的状态,认为它sdown了

所有哨兵选举出了一个,来执行主备切换操作

如果哨兵的majority都存活着,那么就会执行主备切换操作

再通过哨兵看一下master:SENTINEL get-master-addr-by-name mymaster

尝试连接一下新的master

故障恢复,再将旧的master重新启动,查看是否被哨兵自动切换成slave节点

(1)手动杀掉master

(2)哨兵能否执行主备切换,将slave切换为master

(3)哨兵完成主备切换后,新的master能否使用

(4)故障恢复,将旧的master重新启动

(5)哨兵能否自动将旧的master变为slave,挂接到新的master上面去,而且也是可以使用的

主从架构,单master的瓶颈:

能够存储多少数据量,受限于master

怎么横向扩展,master:

我们可以做多个redis 主从集群,做业务的分流,不同的请求,访问到不同的master

redis cluster帮我们做好了这样的功能:

可以支撑N个redis master node,每个master node都可以挂载多个slave node

redis cluster (多master + 读写分离 + 高可用)

redis cluster 是用来支撑海量数据的