[toc]

Redis集群概述

由于单机Redis存储能力受单机限制,以及无法实现读写操作的负载均衡和读写分离,无法保证高可用。本篇就来介绍 Redis 集群搭建方案及实现原理,实现Redis对数据的冗余备份,从而保证数据和服务的高可用。主从复制是哨兵和集群的基石,因此我们循序渐进,由浅入深一层层的将Redis高可用方案抽丝剥茧展示在大家面前。

Redis主从复制介绍

主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器,主从是哨兵和集群模式能够实施的基础。前者称为主节点(master),后者称为从节点(slave),数据的复制是单向的,只能由主节点到从节点。默认情况下,每台Redis服务器都是主节点;且一个主节点可以有零个或多个从节点(0+个从节点),但一个从节点只能有一个主节点。一般主节点负责接收写请求,从节点负责接收读请求,从而实现读写分离。主从一般部署在不同机器上,复制时存在网络延时问题,使用参数repl-disable-tcp-nodelay选择是否关闭TCP_NODELAY,默认为关闭:

  • 关闭:无论数据大小都会及时同步到从节点,占带宽,适用于主从网络好的场景。
  • 开启:主节点每隔指定时间合并数据为TCP包节省带宽,默认为40毫秒同步一次,适用于网络环境复杂或带宽紧张,如跨机房。

主从复制作用

  1. 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
  2. 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
  3. 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务,分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
  4. 读写分离:主库写、从库读,读写分离不仅可以提高服务器的负载能力,同时可根据需求的变化,改变从库的数量。
  5. 高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础。

img

主从复制特点

  1. 使用异步复制。
  2. 一个主服务器可以有多个从服务器。
  3. 从服务器也可以有自己的从服务器。
  4. 复制功能不会阻塞主服务器。
  5. 可以通过复制功能来让主服务器免于执行持久化操作,由从服务器去执行持久化操作即可。

详细功能

  1. Redis 使用异步复制。从 Redis2.8开始,从服务器会以每秒一次的频率向主服务器报告复制流(replicationstream)的处理进度。
  2. 一个主服务器可以有多个从服务器。
  3. 不仅主服务器可以有从服务器,从服务器也可以有自己的从服务器,多个从服务器之间可以构成一个图状结构。
  4. 复制功能不会阻塞主服务器:即使有一个或多个从服务器正在进行初次同步, 主服务器也可以继续处理命令请求。
  5. 复制功能也不会阻塞从服务器:只要在 redis.conf 文件中进行了相应的设置, 即使从服务器正在进行初次同步, 服务器也可以使用旧版本的数据集来处理命令查询。
  6. 在从服务器删除旧版本数据集并载入新版本数据集的那段时间内,连接请求会被阻塞。
  7. 还可以配置从服务器,让它在与主服务器之间的连接断开时,向客户端发送一个错误。
  8. 复制功能可以单纯地用于数据冗余(data redundancy),也可以通过让多个从服务器处理只读命令请求来提升扩展性(scalability): 比如说,繁重的SORT命令可以交给附属节点去运行。

image-20230703171842400

关闭主服务器持久化时,复制功能的数据安全

1.当配置Redis复制功能时,强烈建议打开主服务器的持久化功能。 否则的话,由于延迟等问题,部署
的服务应该要避免自动拉起。
2.为了帮助理解主服务器关闭持久化时自动拉起的危险性,参考一下以下会导致主从服务器数据全部丢
失的例子:
1)假设节点A为主服务器,并且关闭了持久化。并且节点B和节点C从节点A复制数据
2)节点A崩溃,然后由自动拉起服务重启了节点A. 由于节点A的持久化被关闭了,所以重启之后没有任
何数据
3)节点B和节点C将从节点A复制数据,但是A的数据是空的,于是就把自身保存的数据副本删除。
结论:
1)在关闭主服务器上的持久化,并同时开启自动拉起进程的情况下,即便使用Sentinel来实现Redis的
高可用性,也是非常危险的。因为主服务器可能拉起得非常快,以至于Sentinel在配置的心跳时间间隔
内没有检测到主服务器已被重启,然后还是会执行上面的数据丢失的流程。
2)无论何时,数据安全都是极其重要的,所以应该禁止主服务器关闭持久化的同时自动拉起。

主从复制的原理

  1. 从服务器向主服务器发送 SYNC 命令。
  2. 接到 SYNC 命令的主服务器会调用BGSAVE 命令,创建一个 RDB 文件,并使用缓冲区记录接下来执
    行的所有写命令。
  3. 当主服务器执行完 BGSAVE 命令时,它会向从服务器发送 RDB 文件,而从服务器则会接收并载入这
    个文件。
  4. 主服务器将缓冲区储存的所有写命令发送给从服务器执行。

image-20230703171809885

redis的主从复制部署

主机名 外网IP 内网IP 角色 应用
db01 10.0.0.51 172.16.1.51 主库 redis
db02 10.0.0.52 172.16.1.52 从库 redis

前提

  1. 开启rdb持久化 现版本自动开启、
  2. 安全模式要关闭
  3. bind监听
    • bind 10.0.0.51 172.16.1.51 127.0.0.1

操作步骤

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
# 连接主库 db01
redeis-cli

# 在从库配置(作为从库) db02
redis-cli
127.0.0.1:6379> SLAVEOF 172.16.1.51 6379
OK

# 从库db02查看主从状态
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:172.16.1.51
master_port:6379
master_link_status:up
master_last_io_seconds_ago:4
master_sync_in_progress:0
slave_read_repl_offset:28
slave_repl_offset:28
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:c3cc9b03adbdf7ade4755df02f8786a83292cbe0
master_replid2:50f2b57af8543da29b9488b850bc78265eea266d
master_repl_offset:28
second_repl_offset:29
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:28
127.0.0.1:6379>

# 主库db01查看主从状态
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=172.16.1.52,port=6379,state=online,offset=168,lag=1
master_failover_state:no-failover
master_replid:c3cc9b03adbdf7ade4755df02f8786a83292cbe0
master_replid2:50f2b57af8543da29b9488b850bc78265eea266d
master_repl_offset:168
second_repl_offset:29
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:168

# 取消从库身份
slaveof no one

image-20230528214334746

级联复制

前提

  1. 开启rdb持久化 现版本自动开启、
  2. 安全模式要关闭
  3. bind监听
    • bind 10.0.0.53 172.16.1.53 127.0.0.1

即从库下还有个从库

1
2
3
4
5
6
# 配置从库身份(db02做主库) db03
redis-cli
slaveof 172.16.1.52 6379

# 取消从库身份
slaveof no one

多实例做主从步骤

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
# 连接redis
redis-cli -p 6380

# 配置从库身份
127.0.0.1:6380> SLAVEOF 172.16.1.51 6379
OK

# 查看复制数据
127.0.0.1:6380> KEYS *
1) "name"
2) "age"

# 连接redis
redis-cli -p 6381

# 配置从库身份
127.0.0.1:6381> SLAVEOF 172.16.1.52 6379
OK

# 查看主从同步数据
127.0.0.1:6381> KEYS *
1) "name"
2) "age"

# 查看主从状态
127.0.0.1:6381> info replication
# Replication
role:slave
master_host:172.16.1.52
master_port:6379
master_link_status:up
master_last_io_seconds_ago:5
master_sync_in_progress:0
slave_read_repl_offset:1331
slave_repl_offset:1331
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:c3cc9b03adbdf7ade4755df02f8786a83292cbe0
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1331
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1262
repl_backlog_histlen:70

SYNC和PSYNC

sync命令实例

image-20230702174241317

命令传播

在主从服务器完成同步之后,主服务器每执行一个写命令,它都会将被执行的写命令发送给从服务器执
行,这个操作被称为“命令传播”(command propagate)

image-20230702174322072

命令传播是一个持续的过程:只要复制仍在继续,命令传播就会一直进行,使得主从服务器的状态可以
一直保持一致

断线重连

突发场景

  • 网络波动
  • 服务器断电
  • …..

SYNC

image-20230702174401458

如果我们仔细地观察整个断线并重连的过程,就会发现:
从服务器在断线之前已经拥有主服务器的绝大部分数据,要让主从服务器重新回到一致状态,从服务器
真正需要的是 k10087、k10088和k10089这三个键的数据,而不是主服务器整个数据库的数据。SYNC
命令在处理断线并重连时的做法——将主服务器的整个数据库重新同步给从服务器,是极度浪费的!

PSYNC

image-20230702174449261

  1. PSYNC只会将从服务器断线期间缺失的数据发送给从服务器。两个例子的情况是相同的,但SYNC 需
    要发送包含整个数据库的 RDB 文件,而PSYNC 只需要发送三个命令。
  2. 如果主从服务器所处的网络环境并不那么好的话(经常断线),那么请尽量使用 Redis 2.8 或以上版
    本:通过使用 PSYNC 而不是 SYNC 来处理断线重连接,可以避免因为重复创建和传输 RDB文件而浪费
    大量的网络资源、计算资源和内存资源。
    1. 在读写分离环境下,客户端向主服务器发送写命令 SET k10086 v10086,主服务器在执行这个写命
      令之后,向客户端返回回复,并将这个写命令传播给从服务器。
    2. 接到回复的客户端继续向从服务器发送读命令 GET k10086 ,并且因为网络状态的原因,客户端的
      GET命令比主服务器传播的 SET 命令更快到达了从服务器。
    3. 因为从服务器键k10086的值还未被更新,所以客户端在从服务器读取到的将是一个错误(过期)的
      k10086值。

Redis是怎么保证数据安全的呢?

  1. 主服务器只在有至少N个从服务器的情况下,才执行写操作
  2. 从Redis 2.8开始,为了保证数据的安全性,可以通过配置,让主服务器只在有至少N个当前已连接从
    服务器的情况下,才执行写命令。
  3. 不过,因为 Redis 使用异步复制,所以主服务器发送的写数据并不一定会被从服务器接收到,因此,
    数据丢失的可能性仍然是存在的。
  4. 通过以下两个参数保证数据的安全:
1
2
3
4
5
#执行写操作所需的至少从服务器数量
min-slaves-to-write <number of slaves>

#指定网络延迟的最大值
min-slaves-max-lag <number of seconds>

这个特性的运作原理:

  1. 从服务器以每秒一次的频率 PING 主服务器一次, 并报告复制流的处理情况。主服务器会记录各个
    从服务器最后一次向它发送 PING 的时间。用户可以通过配置, 指定网络延迟的最大值 min-slavesmax-
    lag , 以及执行写操作所需的至少从服务器数量 min-slaves-to-write 。
  2. 如果至少有 min-slaves-to-write 个从服务器, 并且这些服务器的延迟值都少于 min-slaves-max-lag
    秒, 那么主服务器就会执行客户端请求的写操作。你可以将这个特性看作 CAP 理论中的 C 的条件放宽
    版本: 尽管不能保证写操作的持久性, 但起码丢失数据的窗口会被严格限制在指定的秒数中。
  3. 另一方面, 如果条件达不到 min-slaves-to-write 和 min-slaves-max-lag 所指定的条件, 那么写操
    作就不会被执行, 主服务器会向请求执行写操作的客户端返回一个错误。

如果主库挂了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 宕掉主机
redis-cli

# 关闭主库模拟宕机
127.0.0.1:6379> shutdown
not connected>

# 找到数据新的从库提升和主库
127.0.0.1:6381> SLAVEOF no one
OK

# 将其他从库重新指向主库
127.0.0.1:6379> SLAVEOF 172.16.1.51 6381
OK
127.0.0.1:6380> SLAVEOF 172.16.1.51 6381
OK