Redis学习(三):Redis 复制的原理与优化及 Reids Sentinel实现高可用

1. 主从复制

1. 主从复制的作用

  • 数据副本
  • 扩展读性能
  • 一个master可以有多个slave
  • 一个slave只能有一个master
  • 数据流向是单向的,master到slave

2. 主从复制配置

1. 两种实现方式

  • slaveof命令
    • 在6380上执行(slaveof 对方的ip) ,当它接收到这条命令之后,我们就实现了复制的流程。
    • 这个复制命令是异步的,执行命令后会立刻返回。实际上要很多步骤和时间。
    • 可以执行(slaveof no one)取消复制,不过并不会清除之前同步的数据,只是说以后的数据不会再同步到这台机器上。
  • 配置
    • slaveof ip port
    • slave-read-only yes 从节点只做读的操作,达到完全一致的情况。
方式  命令  配置
优点  无需重启  统一配置
缺点  不便于管理  需要重启

3. 实验

  • 我们希望master是6379,slave是6380
  • 如果是在一台机器上实验,需要两个配置文件,一个端口是6379,一个是6380。还有一些其他配置最好分开,比如RDB保存名称,其实主从复制需要依靠RDB文件
  • 在6380配置文件中添加主节点,并启动两个节点
slaveof 127.0.0.1 6379
  • 可以看到6379分片是master状态,所有节点默认都是主节点
[root@localhost redis]# redis-server config/redis-6379.conf 
[root@localhost redis]# redis-cli 
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:0
  • 6380节点可以看到是一个从节点
[root@localhost redis]# redis-cli -p 6380 info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
  • 简单看一下数据的同步
[root@localhost redis]# redis-cli
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> exit       
[root@localhost redis]# redis-cli -p 6380
127.0.0.1:6380> get hello
"world"
127.0.0.1:6380> set hello java
(error) READONLY You can't write against a read only slave.
  • 我们查看一下6379.log,发现同步过程需要RDB参与,所以前面说到两个配置不能使用同一个RDB文件
...
4123:M 22 Dec 09:13:15.162 * Slave 127.0.0.1:6380 asks for synchronization
4123:M 22 Dec 09:13:15.162 * Full resync requested by slave 127.0.0.1:6380
4123:M 22 Dec 09:13:15.167 * Starting BGSAVE for SYNC with target: disk
4123:M 22 Dec 09:13:15.169 * Background saving started by pid 4189
4189:C 22 Dec 09:13:15.180 * DB saved on disk
4189:C 22 Dec 09:13:15.180 * RDB: 6 MB of memory used by copy-on-write
4123:M 22 Dec 09:13:15.202 * Background saving terminated with success
4123:M 22 Dec 09:13:15.202 * Synchronization with slave 127.0.0.1:6380 succeeded
...
  • 查看6380.log
...
4183:S 22 Dec 09:13:15.162 * Connecting to MASTER 127.0.0.1:6379
4183:S 22 Dec 09:13:15.162 * MASTER <-> SLAVE sync started
4183:S 22 Dec 09:13:15.162 * Non blocking connect for SYNC fired the event.
4183:S 22 Dec 09:13:15.162 * Master replied to PING, replication can continue...
4183:S 22 Dec 09:13:15.162 * Partial resynchronization not possible (no cached master)
4183:S 22 Dec 09:13:15.171 * Full resync from master: e386f2149fafc462e528ef9e29177612743b14dd:0
4183:S 22 Dec 09:13:15.202 * MASTER <-> SLAVE sync: receiving 232 bytes from master
4183:S 22 Dec 09:13:15.202 * MASTER <-> SLAVE sync: Flushing old data
4183:S 22 Dec 09:13:15.202 * MASTER <-> SLAVE sync: Loading DB in memory
4183:S 22 Dec 09:13:15.203 * MASTER <-> SLAVE sync: Finished with success
...
  • 在6380执行slaveof no one
[root@localhost redis]# redis-cli -p 6380
127.0.0.1:6380> slaveof no one
OK
127.0.0.1:6380> info replication
# Replication
role:master
...
  • 主从复制从节点会把之前的数据全部清除
[root@localhost redis]# redis-cli
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> mset a b c d e f g h m n
OK
127.0.0.1:6379> dbsize
(integer) 5
127.0.0.1:6379> exit
[root@localhost redis]# redis-cli -p 6380
127.0.0.1:6380> flushall
OK
127.0.0.1:6380> set hello world
OK
127.0.0.1:6380> dbsize
(integer) 1
127.0.0.1:6380> slaveof 127.0.0.1 6379
OK
127.0.0.1:6380> get hello
(nil)

4. 全量复制和部分复制

1. run_id

  • redis每次启动都会有一个随机id保证这个redis的标识。
  • 假如6380复制6379,就有一些记录,当我发现6379的run_id发生了变化,他可能做了一些重启或其他重大变化,我就要把它的数据全部同步过来。
[root@localhost redis]# redis-cli -p 6379 info server | grep run
run_id:5f40738868084d52e1b097a03abbcb7b088e44ef

[root@localhost redis]# redis-cli -p 6380 info server | grep run
run_id:b853eafd17c444c6500fc89406d1ddd7fa81b1fb

2. 偏移量

  • 一个数据写入量的字节,记录了我们写了多少数据。
  • 当两个节点偏移量一致的时候,就是一种完全同步。如果不一致,可能主写了很多数据,还没有同步。
[root@localhost redis]# redis-cli -p 6379 info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=196,lag=1
master_replid:255e5f084bf01c0a7e8ebb114548e7478978ed6d
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:196
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:196

[root@localhost redis]# redis-cli -p 6379 set hello java
OK

[root@localhost redis]# redis-cli -p 6379 info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=337,lag=1
master_replid:255e5f084bf01c0a7e8ebb114548e7478978ed6d
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:337
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:337

3. 全量复制

  • redis提供了全量复制的功能,首先将本身的RDB文件,也就是当前状态去同步给slave,在此期间,它写入的命令会单独记录起来,当RDB文件加载完之后,它会通过偏移量的对比,将这个期间写入的值同步给slave。

全量复制

4. 全量复制开销

  • bgsive时间
  • RDB文件网络传输时间
  • 从节点清空数据时间
  • 从节点加载RDB的时间
  • 可能的AOF重写时间

5. 部分复制

  • 除了上面的开销以外,假如master和slave之间的网络发生了抖动,一段时间内这些数据就会进行丢失,对于slave来说这段时间master更新数据我是不知道的,最简单的方法就是我再做一次全量复制。
  • 在redis 2.8中提供了部分复制。
  • 如果发生了抖动,相当于连接断开了,但是在master写命令的时候,它会写一份复制缓冲区的命令,当slave再次连接master的时候,使用pysnc命令告诉我当前的偏移量是多少。如果你传输的偏移量是在buffer的范围内的,会返回continue(如果offset不在buffer范围内,说明你错过了太多信息,需要全量复制),会将offset开始,到buffer队列结尾的返回。有效降低了全量复制的开销。

部分复制

5. 开发与运维中的问题

1. 读写分离

  • 读写分离:读流量分摊到从节点
  • 可能遇到的问题:
    • 复制数据的延迟
    • 读到过期数据(懒惰删除,定时任务)
    • 从节点故障

2. 主从配置不一致

  • 例如maxmemory不一致:丢失数据
  • 例如数据结构优化参数(例如hash-max-ziplist-entries):内存不一致

3. 规避全量复制

  • 第一次全量复制
    • 第一次不可避免
    • 小主节点,低峰(夜间)
  • 节点运行ID不匹配
    • 主节点重启(运行ID变化)
    • 故障转移,例如哨兵或集群
  • 复制积压缓冲区不足
    • 网络中断,部分复制无法满足
    • 增大复制缓冲区配置rel_backlog_size,网络"增强"

4. 规避复制风暴

  • 单主节点复制风暴
    • 问题:主节点重启,多从节点复制
    • 解决:更换复制拓扑
  • 单机器复制风暴
    • 问题:机器宕机后,大量全量复制
    • 解决:主节点分散多机器

2. Redis Sentinel

1. 主从复制高可用?

  • 为主提供一个备份
  • 为主实现一个分流
  • 如果主节点出现了故障,故障转移基本上是需要手工来完成的

1. 主从复制问题

  • 手动故障转移
  • 写能力和存储能力受限

2. Redis Sentinel架构

  • 首先依然是一个主从结构
  • 同时还有很多Redis Sentinel节点,它的作用是完成对redis的故障判断和故障转移
  • 客户端不会直接向redis获取数据,不会记住redis的某个ip,而是直接记住Redis Sentinel的地址
  • Redis Sentinel会时刻对master和slave进行一个监控,他知道谁是master谁是slave

Redis-Sentinel架构

  • 故障转移和手动过程很类似
  • 多个sentinel发现并确认master有问题
  • 选举出一个sentinel作为领导
  • 选出一个slave作为新的master
  • 通知其余slave成为新的master的slave
  • 通知客户端主从变化
  • 等待老的master复活成为新的master的slave
  • sentinel可以监控多套master和slave

3. 安装与配置

  • 配置开启主从节点
  • 配置开启sentinel监控主节点(sentinel是特殊的的redis节点)
  • 实际应该多机器
  • 详细配置节点

1. 介绍配置

  • 主节点master-7000,从节点7001和7002

  • sentinel-26379,26380,26381

  • 关于配置我们前面博客已经了解了很多,直接给出主节点配置

port 7000
daemonize yes
pidfile /var/run/redis_7000.pid
logfile "7000.log"
dir /home/redis/data
  • 从节点配置
port 7001
daemonize yes
pidfile /var/run/redis_7001.pid
logfile "7001.log"
dir /home/redis/data
slaveof 127.0.0.1 7000

port 7002
daemonize yes
pidfile /var/run/redis_7002.pid
logfile "7002.log"
dir /home/redis/data
slaveof 127.0.0.1 7000
  • sentinel主要配置
    • sentinel monitor:监控主节点名字是什么,ip和端口是什么,最后一个参数是指几个sentinel认为master有问题了就会发动下一步的故障转移
    • sentinel down-after-milliseconds:sentinel对master判断,如果多长时间ping不通就认为有问题
    • sentinel parallel-syncs:选择新的master,其他老的slave会对新的master进行复制,复制是并发的还是串行的。我们设置为1,每次只能复制一个,可以减轻master的压力
    • sentinel failover-timeout:故障转移时间
port ${port}
dir /home/redis/data
logfile "${port}.log"
sentinel monitor mymaster 127.0.0.1 7000 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000

2. 配置演示

  • 按照上面配置文件配置并启动三个节点
[root@localhost redis]# redis-server config/redis-7000.conf 
[root@localhost redis]# redis-server config/redis-7001.conf 
[root@localhost redis]# redis-server config/redis-7002.conf 

[root@localhost redis]# ps -ef | grep redis-server | grep 700
root       5003      1  0 19:46 ?        00:00:00 redis-server 0.0.0.0:7000
root       5012      1  0 19:46 ?        00:00:00 redis-server 0.0.0.0:7001
root       5022      1  0 19:46 ?        00:00:00 redis-server 0.0.0.0:7002
  • 添加sentinel配置
[root@localhost redis]# cp sentinel.conf config/
[root@localhost redis]# cd config/
[root@localhost config]# cat sentinel.conf  | grep -v "#" | grep -v "^$" > redis-sentinel-26379.conf
  • 修改sentinel配置
[root@localhost config]# vi redis-sentinel-26379.conf 

port 26379
daemonize yes
dir /home/redis/data/sentinel
logfile "26379.log"
sentinel monitor mymaster 127.0.0.1 7000 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
  • 启动并查看进程
[root@localhost redis]# redis-sentinel config/redis-sentinel-26379.conf 
[root@localhost redis]# ps -ef | grep redis-sentinel
root       4226      1  0 22:03 ?        00:00:00 redis-sentinel *:26379 [sentinel]
  • 连接sentinel,使用info可以看到sentinel信息
[root@localhost redis]# redis-cli -p 26379
127.0.0.1:26379> ping
PONG
127.0.0.1:26379> info
...
# 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=127.0.0.1:7000,slaves=2,sentinels=1
...
  • 我们重新打开刚才的配置文件,发现自动生成了一些新的配置,说明sentinel会自动发现我们的slave
[root@localhost redis]# cat config/redis-sentinel-26379.conf 
port 26379
daemonize yes
dir "/home/redis-4.0.5/data/sentinel"
logfile "26379.log"
sentinel myid d8fe6557617f240f2441be5a879c90883a5079d6
sentinel monitor mymaster 127.0.0.1 7000 2
sentinel config-epoch mymaster 0
sentinel leader-epoch mymaster 0
# Generated by CONFIG REWRITE
sentinel known-slave mymaster 127.0.0.1 7002
sentinel known-slave mymaster 127.0.0.1 7001
sentinel current-epoch 0
  • 我们偷懒完成其他两个sentinel配置
[root@localhost config]# sed "s/26379/26380/g" redis-sentinel-26379.conf > redis-sentinel-26380.conf 
[root@localhost config]# sed "s/26379/26381/g" redis-sentinel-26379.conf > redis-sentinel-26381.conf 
  • 启动23380端口
[root@localhost redis]# redis-sentinel config/redis-sentinel-26380.conf 
[root@localhost redis]# redis-cli -p 26380
127.0.0.1:26380> info
...
# 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=127.0.0.1:7000,slaves=2,sentinels=1
...
  • 这里我们看到sentinels=1。这就奇怪了,我们已经启动了2个sentinel啊,我怀疑刚才的偷懒可能有一些问题,打开26380检查一下:我们发现刚才执行后配置文件中自动生成了myid,复制过程中也复制了,把id删除,关闭26380并重新启动,同样删除26381配置文件中复制过来的26379的myid
[root@localhost redis]# redis-cli -p 26381 shutdown
[root@localhost redis]# redis-sentinel config/redis-sentinel-26381.conf 
[root@localhost redis]# redis-cli -p 26381
127.0.0.1:26381> info
...
# 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=127.0.0.1:7000,slaves=2,sentinels=3
...

3. Sentinel客户端及故障转移

1. 客户端实现基本原理

1. 遍历sentinel节点集合,获取一个可用的sentinel节点

2. 执行sentinel的API传入masterName,获得master节点的真正地址和端口

3. sentinel获得节点后还会执行role或者role replication进行一次验证

4. 如果master节点发生了变化,sentinel是可以感知的,redis数据节点变化的通知会发送给客户端

2. 故障转移演练

1. 客户端高可用观察

  • 使用jedis连接centinel
public void RedisSentinelFailoverTest() {

    String masterName = "mymaster";
    Set<String> sentinels = new HashSet<>();
    sentinels.add("127.0.0.1:26379");
    sentinels.add("127.0.0.1:26380");
    sentinels.add("127.0.0.1:26381");
    
    JedisSentinelPool jedisSentinelPool = new JedisSentinelPool(masterName, sentinels);
}
  • 发现报错
redis.clients.jedis.exceptions.JedisConnectionException: All sentinels down, cannot determine where is mymaster master is running...

at redis.clients.jedis.JedisSentinelPool.initSentinels(JedisSentinelPool.java:180)
at redis.clients.jedis.JedisSentinelPool.<init>(JedisSentinelPool.java:95)
at redis.clients.jedis.JedisSentinelPool.<init>(JedisSentinelPool.java:82)
  • 怀疑是ip地址有误,换成虚拟机ip地址192.168.242.129,发现报新的错误
警告: Cannot get master address from sentinel running @ 192.168.242.129:26380. Reason: redis.clients.jedis.exceptions.JedisDataException: 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.. Trying next one.
十二月 23, 2017 9:57:25 上午 redis.clients.jedis.JedisSentinelPool initSentinels
  • 这个错误就比较明显了,在sentinel配置中也像普通节点一样,关闭保护模式,连接成功

  • 我们现在写一个死循环,让redis不断增加节点,然后杀死主节点7000,看看会发生什么

public void RedisSentinelFailoverTest() {

    String masterName = "mymaster";
    Set<String> sentinels = new HashSet<>();
    sentinels.add("192.168.242.129:26379");
    sentinels.add("192.168.242.129:26380");
    sentinels.add("192.168.242.129:26381");

    JedisSentinelPool jedisSentinelPool = new JedisSentinelPool(masterName, sentinels);

    int counter = 0;
    while (true) {
        counter++;
        Jedis jedis = null;
        try {
            jedis = jedisSentinelPool.getResource();
            int index = new Random().nextInt(100000);
            String key = "k-" + index;
            String value = "v-" + index;
            jedis.set(key, value);
            if (counter % 100 == 0) {
                System.out.println(jedis.get(key));
            }
            TimeUnit.MILLISECONDS.sleep(10);
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }
  • 此时发现又报错找不到节点,我们原来是在虚拟机上做一些操作所以写127.0.0.1没事,现在为了在虚拟机外连接,将所有配置ip的地方都换成虚拟机ip地址192.168.242.129,发现程序不断打印节点信息
  • 杀死7000节点,使用info server查到进程id
127.0.0.1:7000> info server
# Server
...
process_id:5991
...

[root@localhost redis]# kill -9 5991
  • 发现程序产生了大量报错,因为他的故障转移是需要时间的
信息: Created JedisPool to master at 192.168.242.129:7000
v-10360
v-1527
v-96986
v-41507
v-64630

...
Caused by: java.net.ConnectException: Connection refused: connect
at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method)
at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:85)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:589)
at redis.clients.jedis.Connection.connect(Connection.java:184)
... 31 more
...

...
v-74169
v-53527
v-58618
v-80320
v-23413
v-51509
  • 一段时间后故障恢复,不过第一次尝试的时候故障一致不能恢复,停下来去看配置文件,发现7001的配置文件的RDB文件忘记改了,还是redis-6379.rdb,数据记录混在一起造成无法恢复故障。

2. 服务端日志分析:数据节点和sentinel节点

  • 我们首先看一下主节点7000的日志,因为我们使用kill -9杀死进程,所有日志并没有什么变化,最后一条记录还是同步成功
[root@localhost data]# tail -f 7000.log 

4218:C 23 Dec 10:53:26.497 * RDB: 8 MB of memory used by copy-on-write
4176:M 23 Dec 10:53:26.597 * Background saving terminated with success
4176:M 23 Dec 10:53:26.598 * Synchronization with slave 192.168.242.129:7002 succeeded
  • 我们再看一下7001日志,首先在杀死7000后,他已经和主节点失联了,一直在尝试连接
4184:S 23 Dec 10:54:38.856 * Connecting to MASTER 192.168.242.129:7000
4184:S 23 Dec 10:54:38.857 * MASTER <-> SLAVE sync started
4184:S 23 Dec 10:54:38.857 # Error condition on socket for SYNC: Connection refused
4184:S 23 Dec 10:54:39.864 * Connecting to MASTER 192.168.242.129:7000
4184:S 23 Dec 10:54:39.865 * MASTER <-> SLAVE sync started
4184:S 23 Dec 10:54:39.865 # Error condition on socket for SYNC: Connection refused
4184:S 23 Dec 10:54:40.872 * Connecting to MASTER 192.168.242.129:7000
4184:S 23 Dec 10:54:40.872 * MASTER <-> SLAVE sync started
4184:S 23 Dec 10:54:40.873 # Error condition on socket for SYNC: Connection refused
  • 然后它形成了自己的一个master状态,而且接收到了一条用户请求,希望让他成为新的master。并进行了一个配置的重写
4184:M 23 Dec 10:54:42.227 * Discarding previously cached master state.
4184:M 23 Dec 10:54:42.227 * MASTER MODE enabled (user request from 'id=5 addr=192.168.242.129:49264 fd=10 name=sentinel-35e89218-cmd age=64 idle=0 flags=x db=0 sub=0 psub=0 multi=3 qbuf=0 qbuf-free=32768 obl=36 oll=0 omem=0 events=r cmd=exec')
4184:M 23 Dec 10:54:42.229 # CONFIG REWRITE executed with success.
  • 接着我们看到7002去复制新的master7001
4184:M 23 Dec 10:54:43.156 * Slave 192.168.242.129:7002 asks for synchronization
4184:M 23 Dec 10:54:43.157 * Partial resynchronization request from 192.168.242.129:7002 accepted. Sending 464 bytes of backlog starting from offset 93371.
  • 我们来看一下sentinel的日志26379,这里日志比较多,我们看一些简单的

  • 我们看到了sdown,就是说他认为7000下线了

4221:X 23 Dec 10:54:41.923 # +sdown master mymaster 192.168.242.129 7000
  • 然后他希望成为一个领导者,其他sentinel给他投票
4221:X 23 Dec 10:54:42.039 # +vote-for-leader 35e89218cd8bbc8f30cda79f75ab6af88ad3dc2e 14
4221:X 23 Dec 10:54:42.717 # +config-update-from sentinel 35e89218cd8bbc8f30cda79f75ab6af88ad3dc2e 192.168.242.129 26380 @ mymaster 192.168.242.129 7000
  • 然后他选出新的master,并让其他slave成为新master的slave
4221:X 23 Dec 10:54:42.717 # +switch-master mymaster 192.168.242.129 7000 192.168.242.129 7001
4221:X 23 Dec 10:54:42.717 * +slave slave 192.168.242.129:7002 192.168.242.129 7002 @ mymaster 192.168.242.129 7001

3. 三个定时任务

1. 每10秒每个sentinel对master和slave执行info

  • 发现slave节点
  • 确认主从关系

2. 每2秒每个sentinel通过master节点的channel交换信息(pub/sub)

  • 通过__sentinel__:hell频道交互
  • 交互对节点的"看法"和自身信息

3. 每1秒每个sentinel对其他sentinel和redis执行ping

  • 心跳检测,失败判定依据

4. 主观下线和客观下线

  • 我们重新看一下当时的配置
sentinel monitor <masterName> <ip> <port> <quorum>
sentinel down-after-millseconds <masterName> <timeout>
  • 主观下线:每个sentinel节点对Redis节点失败的"偏见"
  • 客观下线:所有sentinel节点对Redis节点失败"达成共识"(超过quorum个统一)
  • sentinel is-master-down-by-addr

5. 领导者选举

  • 原因:只有一个sentinel节点完成故障转移。
  • 选举:通过sentinel is-master-down-by-addr命令希望成为领导者。
    • 每个做主观下线的sentinel节点向其他sentinel节点发送命令,要求将他设置为领导者。
    • 收到命令的sentinel节点如果没有同意通过其他sentinel节点发送的命令,那么将同意该请求,否则拒绝。
    • 如果该sentinel节点发现自己的票数已经超过sentinel集合半数且超过quorum,那么它将成为领导者。
    • 如果此过程有过个sentinel节点成为了领导者,那么将等待一段时间重新进行选举。

6. 故障转移

1. 从slave节点中选出一个"合适的"节点作为新的master节点

  • 选择slave-priority(slave节点优先级)最高的slave节点,如果存在则返回,不存在则继续。
  • 选择复制偏移量最大的slave节点(复制的最完整),如果存在则返回,不存在则继续。
  • 选择runId最小的slave节点。

2. 对上面的slave节点执行slaveof no one命令让其成为master节点

3. 向剩余的slave节点发送命令,让他们成为新master节点的slave节点,复制规则和parallel-syncs参数有关

4. 更新对原来的master节点配置为slave,并保持着对其"关注",当其恢复后命令他去复制新的master节点

7. 常见的开发运维问题

1. 节点运维

  • 原因:
    • 机器下线:例如过保等情况。
    • 机器性能不足:例如CPU,内存,硬盘,网络等。
    • 节点自身故障:例如服务不稳定等。
  • 节点下线
    • 主节点:sentinel failover 。
    • 从节点:临时下线还是永久下线,例如是否做一些清理工作。但是要考虑读写分离的情况。
    • seninel节点:同上。
  • 节点上线:
    • 主节点:sentinel failover 进行替换。
    • 从节点:slaveof即可,sentinel节点可以感知。
    • seninel节点:参考其他sentinel节点启动即可。

2. 高可用读写分离

  • 可以参考JedisSentinelPool的实现。
  • 从节点的作用
    • 副本:高可用的基础。
    • 扩展:读能力。
  • 三个"消息"
    • +switch-master:切换主节点(从节点晋升主节点)。
    • +convert-to-slave:切换从节点(原主节点降为从节点)。
    • +sdown:主观下线。

8. 回顾总结

  • Redis Sentinel是Redis的高可用配置实现方案:故障发现,故障自动转移,配置中心,客户端通知。
  • Redis Sentinel从Redis2.8版本开始才正式生产可用,之前版本生产不可用。
  • 尽可能在不同物理机上部署Redis Sentinel所有节点。
  • Redis Sentinel中的Sentinel节点个数应改为大于等于3,且最好为奇数。
  • Redis Sentinel中的数据节点与普通节点没有区别。
  • 客户端初始化连接的是Sentinel节点集合,不再是具体的Redis节点,但Sentinel只是配置中心不是代理。
  • Redis Sentinel通过三个定时任务实现了Sentinel节点对于主节点,从节点,其余Sentinel节点的监控。
  • Redis Sentinel在对节点做失败判定时分为主观下线和客观下线。
  • 看懂Redis Sentinel故障转移日志对于Redis Sentinel以及问题排查非常有帮助。
  • Redis Sentinel实现读写分离高可用可以依赖Sentinel节点的消息通知,获取Redis数据节点的状态变化。

最后

大家可以关注我的微信公众号一起学习进步。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值