Redis哨兵简介

前言

Redis Sentinel是Redis官方建议的高可用(HA)解决方案,在我们搭建Redis集群时,Redis本身并未集成主备切换功能,sentinel本身是独立运行的,能够监控多个Redis集群,发现master宕机后能够自动切换,选举一个slave成为新的master,当原master恢复之后,sentinel会自动将其作为slave加入到集群中,整个过程不需要人工参与,完全自动化。

主要介绍

  1. sentinel主要功能

    • 定期监控Redis服务是否运行正常
    • 定期监控其他sentinel服务是否正常
    • 能够自动切换master节点
    • sentinel节点不存储数据
  2. sentinel集群

    这个不难理解,如果我们用一个非高可用的sentinel去实现Redis的高可用,明显是不科学的,当这一台sentinel宕机之后,Redis显然无法继续保持它的高可用,所以我们在部署sentinel的时候也会采用集群的方式

    优势:

    ​ 即使有sentinel服务宕机,只要还有一台sentinel运行正常,就可以使Redis继续保持高可用

  3. sentinel版本问题

    sentinel在Redis2.6版本中引入的,当时是sentinel 1,貌似有蛮多问题,毕竟初版

    在Redis2.8版本中升级到sentinel 2,之后就非常稳定了

    不过现在Redis已经发展了很久,版本也越来越高,sentinel已经非常值得信赖了

  4. sentinel中的定时任务

    • 每隔10秒向各个Redis服务器(master和slave节点)发送INFO命令,根据回应获取master和slave信息,通过master的回复可以获取到新增的slave节点
    • 每隔02秒向Redis的master服务器发送命令(hello消息),用于发现和监视其他sentinel,sentinel之间的监控不在额外创建订阅
    • 每隔01秒向Redis和sentinel所有服务发送PING消息(sentinel本身的ip、端口、id等内容),通过回复PONG判断服务是否在线
  5. 下线判断

    • 主观下线:当前sentinel断定master下线
    • 客观下线:满足sentinel配置文件中quorum数量的sentinel均断定master下线
  6. 配置文件解读

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    # sentinel运行的端口,默认为26379
    port 26377

    dir "/private/tmp"

    logfile "/var/log/redis/sentinel_26377.log"

    # 以守护进程执行
    daemonize yes

    # 守护进程运行的pid保存文件
    pidfile "/var/run/redis-sentinel.pid"

    # 格式:sentinel monitor <master-name> <ip> <redis-port> <quorum>
    # 该行的意思是:<master-name>:自定义
    # <ip>:master主机的IP
    # <redis-port>:master的端口
    # <quorum>:表示在sentinel集群中,使master由主观下线变为客观下线的sentinel数量。
    sentinel monitor cc_master 127.0.0.1 6379 2

    # 格式:sentinel down-after-milliseconds <master-name> <milliseconds>
    # sentinel会向master发送心跳PING来确认master是否存活,如果master在<milliseconds>时间内回应的不是PONG,那么这个sentinel会主观地认为这个master下线了。<milliseconds>的单位是毫秒,默认30秒。
    sentinel down-after-milliseconds cc_master 15000

    # 格式:sentinel failover-timeout <master-name> <milliseconds>
    # failover过期时间,当failover开始后,在此时间内仍然没有触发任何failover操作,当前sentinel将会认为此次failoer失败。默认180秒,即3分钟。
    sentinel failover-timeout cc_master 60000

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

    # 格式:sentinel auth-pass <master-name> <password>
    # sentinel连接的master节点的登录密码
    sentinel auth-pass cc_master redis
  7. 故障转移

    • sentinel使用Raft投票选举出一个leader去执行故障转移
      • 每一个将master标记为主观下线的sentinel节点发起投票
      • 其他sentinel节点收到投票后,若尚未参与投票(也就是尚未投票给其他sentinel),则同意,否则拒绝
      • 最终收到过半同意的sentinel节点作为leader
      • 若有两个sentinel收到了过半投票,那么就再重新选举
    • 选举新的master节点
      • 选择replica-priority配置数字最高的slave节点为master,默认为100
      • 若replica-priority相同,则选择偏移量最大的slave节点,偏移量是指slave从master同步的进度,偏移量越大说明数据越完整,可以通过Redis的info命令查看(slave_repl_offset)当前slave的偏移量
      • 若偏移量相同,则选择最先启动的slave作为master
      • 更改master后,通知其他slave节点同步为新的master节点的slave节点
      • 原master节点恢复之后自动加入到集群中,成为新master的slave节点

实战

在本机上启动3个Redis实例,采用1主2从的模式,以下只记录redis.conf和sentinel.conf中关键内容

  1. redis.conf

    • redis-master.conf配置

      1
      2
      # 默认端口
      port 6379
    • redis-slave1.conf配置

      1
      2
      3
      4
      5
      6
      # 端口
      port 63791

      # 格式:replicaof <masterip> <masterport>
      # 从节点归属的master节点
      replicaof 127.0.0.1 6379
    • redis-slave2.conf配置

      1
      2
      3
      4
      5
      6
      # 端口
      port 63792

      # 格式:replicaof <masterip> <masterport>
      # 从节点归属的master节点
      replicaof 127.0.0.1 6379
  2. sentinel.conf

    • sentinel0.conf

      1
      2
      3
      4
      5
      6
      7
      # 端口
      port 26379

      sentinel myid 842c9102c48eb0cedeb06fe55e7d2258595ac267

      # 监控master
      sentinel monitor cc_master 127.0.0.1 6379 2
    • sentinel1.conf

      1
      2
      3
      4
      5
      6
      7
      # 端口
      port 26378

      sentinel myid 842c9102c48eb0cedeb06fe55e7d2258595ac266

      # 监控master
      sentinel monitor cc_master 127.0.0.1 6379 2
    • sentinel2.conf

      1
      2
      3
      4
      5
      6
      7
      # 端口
      port 26377

      sentinel myid 842c9102c48eb0cedeb06fe55e7d2258595ac265

      # 监控master
      sentinel monitor cc_master 127.0.0.1 6379 2
  3. 启动

    1. 启动sentinel

      1
      2
      3
      4
      5
      redis-sentinel ~/Documents/develop_tools/tools/redis-5.0.5/sentinel0.conf

      redis-sentinel ~/Documents/develop_tools/tools/redis-5.0.5/sentinel1.conf

      redis-sentinel ~/Documents/develop_tools/tools/redis-5.0.5/sentinel2.conf
    2. 启动Redis

      1
      2
      3
      4
      5
      redis-server ~/Documents/develop_tools/tools/redis-5.0.5/redis-master.conf

      redis-server ~/Documents/develop_tools/tools/redis-5.0.5/redis-slave1.conf

      redis-server ~/Documents/develop_tools/tools/redis-5.0.5/redis-slave2.conf

Redis通过info查看信息

127.0.0.1:6379>info all

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# Server服务器信息
redis_version:5.0.5 # Redis 服务器版本
redis_git_sha1:00000000 # Git SHA1
redis_git_dirty:0 # Git dirty flag
redis_build_id:6c6e38af7cea0726 # Redis构建ID
redis_mode:standalone # Redis运行模式
os:Darwin 19.3.0 x86_64 # 运行环境操作系统版本
arch_bits:64 # 架构(32 或 64 位)
multiplexing_api:kqueue # Redis 所使用的事件处理机制
atomicvar_api:atomic-builtin
gcc_version:4.2.1 # 编译的GCC版本
process_id:61985 # 服务器进程的 PID
run_id:433b78ec513c8b782f3a46ba6b4ade1f12439aca # Redis 服务器的随机标识符(用于Sentinel和集群)
tcp_port:6379 # Redis端口
uptime_in_seconds:108 # Redis运行时长,秒
uptime_in_days:0 # Redis运行市场,天
hz:10
configured_hz:10
lru_clock:8363059 # 以分钟为单位进行自增的时钟,用于 LRU 管理
executable:/Users/chuan/redis-server # 运行命令
config_file: # 启动使用的配置文件

# Clients
connected_clients:7 # 已连接客户端的数量
client_recent_max_input_buffer:2 # 当前连接的客户端当中,最长的输出列表
client_recent_max_output_buffer:0 # 当前连接的客户端当中,最大输入缓存
blocked_clients:0 # 正在等待阻塞命令(BLPOP、BRPOP、BRPOPLPUSH)的客户端的数量

# Memory (太多了,不做解释了)
used_memory:2235920 # 由 Redis 分配器分配的内存总量,以字节(byte)为单位
used_memory_human:2.13M # 以可读的格式返回 Redis 分配的内存总量
used_memory_rss:3153920 # 从操作系统的角度,返回 Redis 已分配的内存总量(俗称常驻集大小)。这个值和top 、 ps 等命令的输出一致。
used_memory_rss_human:3.01M # 以可读的格式返回rss
used_memory_peak:2317840 # Redis 的内存消耗峰值(以字节为单位)
used_memory_peak_human:2.21M
used_memory_peak_perc:96.47%
used_memory_overhead:2221526
used_memory_startup:987776
used_memory_dataset:14394
used_memory_dataset_perc:1.15%
allocator_allocated:2271808
allocator_active:3116032
allocator_resident:3116032
total_system_memory:17179869184
total_system_memory_human:16.00G
used_memory_lua:37888
used_memory_lua_human:37.00K
used_memory_scripts:0
used_memory_scripts_human:0B
number_of_cached_scripts:0
maxmemory:0
maxmemory_human:0B
maxmemory_policy:noeviction
allocator_frag_ratio:1.37
allocator_frag_bytes:844224
allocator_rss_ratio:1.00
allocator_rss_bytes:0
rss_overhead_ratio:1.01
rss_overhead_bytes:37888
mem_fragmentation_ratio:1.39
mem_fragmentation_bytes:882112
mem_not_counted_for_evict:0
mem_replication_backlog:1048576
mem_clients_slaves:33844
mem_clients_normal:151226
mem_aof_buffer:0
mem_allocator:libc
active_defrag_running:0
lazyfree_pending_objects:0

# Persistence
loading:0 # 服务器是否正在载入持久化文件
rdb_changes_since_last_save:1 # 距离最后一次成功创建持久化文件之后,改变了多少个键值
rdb_bgsave_in_progress:0 # 服务器是否正在创建RDB文件
rdb_last_save_time:1585421263 # 最近一次成功创建RDB文件的UNIX时间
rdb_last_bgsave_status:ok # 最后一次创建RDB文件的结果是成功还是失败
rdb_last_bgsave_time_sec:0 # 最后一次创建RDB文件耗费的秒数
rdb_current_bgsave_time_sec:-1 # 记录当前创建RDB操作已经耗费了多长时间(单位为秒)
rdb_last_cow_size:0
aof_enabled:0 # AOF是否处于打开状态
aof_rewrite_in_progress:0 # 服务器是否正在创建AOF文件
aof_rewrite_scheduled:0 # 是否需要执行预约的AOF重写操作
aof_last_rewrite_time_sec:-1 # 最后一次重启AOF的秒数
aof_current_rewrite_time_sec:-1 # 记录当前正在重写AOF的秒数
aof_last_bgrewrite_status:ok # 最后一次重写AOF文件的结果
aof_last_write_status:ok # 最后一次写入结果
aof_last_cow_size:0

# Stats (可以不做了解)
total_connections_received:9
total_commands_processed:720
instantaneous_ops_per_sec:6
total_net_input_bytes:34197
total_net_output_bytes:229238
instantaneous_input_kbps:0.34
instantaneous_output_kbps:1.19
rejected_connections:0
sync_full:2 # 主从完全同步成功次数
sync_partial_ok:0 # 主从部分同步成功次数
sync_partial_err:0 # 主从部分同步失败次数
expired_keys:0 # 运行以来过期的key的数量
expired_stale_perc:0.00 # 过期的比率
expired_time_cap_reached_count:0 # 过期计数
evicted_keys:0
keyspace_hits:1
keyspace_misses:1
pubsub_channels:1
pubsub_patterns:0
latest_fork_usec:278
migrate_cached_sockets:0
slave_expires_tracked_keys:0
active_defrag_hits:0
active_defrag_misses:0
active_defrag_key_hits:0
active_defrag_key_misses:0

# Replication(master节点)
role:master # 角色 master和slave
connected_slaves:2 # slave节点数
slave0:ip=127.0.0.1,port=63791,state=online,offset=20163,lag=1 # 从节点1
slave1:ip=127.0.0.1,port=63792,state=online,offset=20163,lag=0 # 从节点2
master_replid:895f219aa1e7ed5ecda50dcb1f77eea9f1ef9c3d # 主实例启动随机字符串
master_replid2:0000000000000000000000000000000000000000 # 主实例启动随机字符串2
master_repl_offset:20163 # 主从同步偏移量,此值如果和上面的offset相同说明主从一致没延迟,与master_replid可被用来标识主实例复制流中的位置。
second_repl_offset:-1 # 主从同步偏移量2,此值如果和上面的offset相同说明主从一致没延迟
repl_backlog_active:1 # 复制积压缓冲区是否开启
repl_backlog_size:1048576 # 复制积压缓冲大小
repl_backlog_first_byte_offset:1 # 复制缓冲区里偏移量的大小
repl_backlog_histlen:20163 # 此值等于 master_repl_offset - repl_backlog_first_byte_offset,该值不会超过repl_backlog_size的大小

# Replication(slave节点)
role:slave # 角色 master和slave
master_host:127.0.0.1 # master节点IP
master_port:6379 # master节点端口
master_link_status:up # master通信
master_last_io_seconds_ago:1 # 主库多少秒未发送数据到从库
master_sync_in_progress:0 # 从服务器是否在与主服务器进行同步
slave_repl_offset:42262 # slave复制偏移量
slave_priority:100 # slave优先级
slave_read_only:1 # 从库是否设置只读
connected_slaves:0 # 连接的slave实例个数
master_replid:895f219aa1e7ed5ecda50dcb1f77eea9f1ef9c3d
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:42262 # master偏移量,与slave_repl_offset相同则表示同步完整
second_repl_offset:-1
repl_backlog_active:1 # 复制积压缓冲区是否开启
repl_backlog_size:1048576 # 复制积压缓冲大小
repl_backlog_first_byte_offset:1 # 复制缓冲区里偏移量的大小
repl_backlog_histlen:42262 # 此值等于 master_repl_offset - repl_backlog_first_byte_offset,该值不会超过repl_backlog_size的大小

# CPU
used_cpu_sys:0.104404 # 将所有redis主进程在核心态所占用的CPU时求和累计起来
used_cpu_user:0.079472 # 将所有redis主进程在用户态所占用的CPU时求和累计起来
used_cpu_sys_children:0.002037 # 将后台进程在核心态所占用的CPU时求和累计起来
used_cpu_user_children:0.000648 # 将后台进程在用户态所占用的CPU时求和累计起来

# Cluster
cluster_enabled:0 # 实例是否启用集群模式

# Keyspace
db0:keys=1,expires=0,avg_ttl=0 # db0的key的数量,以及带有生存期的key的数,平均存活时间