Featured image of post redis高可用-主从复制

redis高可用-主从复制

本文阅读量

redis高可用-主从复制

Redis高可用常见的有两种方式:

  • 主从复制(Replication-Sentinel模式)
  • Redis集群(Redis-Cluster模式)

下面将分别介绍这两种高可用方案

主从复制

背景

一般来说,要将 Redis运用于工程项目中,只使用一台Redis是万万不能的(宕机,一主二从),原因如下:

1、从结构上,单个 Redis服务器会发生单点故障,并且一台服务器需要处理所有的请求负载,压力较大

2、从容量上,单个 Redis服务器内存容量有限,就算一台 Redis服务器内存容量为256G,也不能将所有内存用作 Redis存储内存,一般来说,单台Redis最大使用内存不应该超过20G。

要实现分布式数据库的更大的存储容量和承受高并发访问量,我们会将原来集中式数据库的数据分别存储到其他多个网络节点上。

一些概念

主从复制

主从复制,是指将一台 Redis服务器的数据,复制到其他的 Redis服务器。前者称为主节点( master/ leader),后者称为从节点(slave/ follower);数据的复制是单向的,只能由主节点到从节点。 Master以写为主,Slave以读为主。

默认情况下,每台 Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。

全量复制

用于初次复制或其它无法进行部分复制的情况,将主节点中的所有数据都发送给从节点,是一个非常重型的操作,当数据量较大时,会对主从节点和网络造成很 大的开销

部分复制

用于处理在主从复制中因网络闪断等原因造成的数据丢失场景,当从节点再次连上主节点后,如果(条件允许),主节点会补发丢失数据给从节点。 因为补发的数据远远小于全量数据,可以有效避免全量复制的过高开销,需要注意的是,如果网络中断时间过长,造成主节点没有能够完整地保存中断期间执行 的写命令,则无法进行部分复制,仍使用全量复制

复制偏移量

参与复制的主从节点都会维护自身复制偏移量。

主节点(master)在处理完写入命令后,会把命令的字节长度做累加记录,统计信息在 info relication 中的 master_repl_offset 指标

主节点也会保存从节点的复制偏移量,统计信息在 info relication 中的 slave0 指标

从节点在接收到主节点发送的命令后,也会累加记录自身的偏移量。统计信息在info relication中的slave_repl_offset

复制积压缓冲区

复制积压缓冲区是保存在主节点上的一个固定长度的队列,默认大小为1MB,当主节点有连接的从节点(slave)时被创建,这时主节点(master)响应写命令 时,不但会把命令发送给从节点,还会写入复制积压缓冲区。

在命令传播阶段,主节点除了将写命令发送给从节点,还会发送一份给复制积压缓冲区,作为写命令的备份;除了存储写命令,复制积压缓冲区中还存储了其中 的每个字节对应的复制偏移量(offset) 。由于复制积压缓冲区定长且先进先出,所以它保存的是主节点最近执行的写命令;时间较早的写命令会被挤出缓冲区

缓冲区大小调整

由于缓冲区长度固定且有限,因此可以备份的写命令也有限,当主从节点offset的差距过大超过缓冲区长度时,将无法执行部分复制,只能执行全量复制。反过 来说,为了提高网络中断时部分复制执行的概率,可以根据需要增大复制积压缓冲区的大小(通过配置repl-backlog-size)来设置

例如 如果网络中断的平均时间是60s,而主节点平均每秒产生的写命令(特定协议格式)所占的字节数为100KB,则复制积压缓冲区的平均需求为6MB,保险起见, 可以设置为12MB,来保证绝大多数断线情况都可以使用部分复制。

正常情况下redis是如何决定是全量复制还是部分复制

从节点将offset发送给主节点后,主节点根据offset和缓冲区大小决定能否执行部分复制

  • 如果offset偏移量之后的数据,仍然都在复制积压缓冲区里,则执行部分复制
  • 如果offset偏移量之后的数据已不在复制积压缓冲区中(数据已被挤出),则执行全量复制。

服务器运行id(runid)

每个Redis节点(无论主从),在启动时都会自动生成一个随机ID(每次启动都不一样),由40个随机的十六进制字符组成;runid用来唯一识别一个Redis节点。 通过info server命令,查看节点的run_id

主从节点初次复制时,主节点将自己的runid发送给从节点,从节点将这个runid保存起来;当断线重连时,从节点会将这个runid发送给主节点;主节点根据runid 判断能否进行部分复制: 如果从节点保存的runid与主节点现在的runid相同,说明主从节点之前同步过,主节点会继续尝试使用部分复制(到底能不能部分复制还要看offset和复制积压缓 冲区的情况) 如果从节点保存的runid与主节点现在的runid不同,说明从节点在断线前同步的Redis节点并不是当前的主节点,只能进行全量复制

作用

1、数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式

2、故障恢复:当主节点岀现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。

3、负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写 Redis数据时应用连接主节点,读 Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。

4、读写分离:可以用于实现读写分离,主库写、从库读,读写分离不仅可以提高服务器的负载能力,同时可根据需求的变化,改变从库的数量

5、高可用(集群)基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是 Redis高可用的基础。

搭建(一主二从)

主从配置参考

配置从库的配置文件,主不需要配置

1
2
3
4
# 配置主服务器地址、端口号 
slaveof 192.168.1.11 6379
# 主服务器密码 
masterauth ""

为了避免一些问题,可以参照如下配置:

主库

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#在slave和master同步后(发送psync/sync),后续的同步是否设置成TCP_NODELAY假如设置成yes,则redis会合并小的TCP包从而节省带宽,但会增加
同步延迟(40ms),造成master与slave数据不一致假如设置成no,则redis master会立即发送同步数据,没有延迟
#前者关注性能,后者关注一致性
repl-disable-tcp-nodelay no
#从库会按照一个时间间隔向主库发送PING命令来判断主服务器是否在线,默认是10秒
repl-ping-slave-period 10
#复制积压缓冲区大小设置
repl-backlog-size 1mb
#master没有slave一段时间会释放复制缓冲区的内存,repl-backlog-ttl用来设置该时间长度。单位为秒。
repl-backlog-ttl 3600
#redis提供了可以让master停止写入的方式,如果配置了min-slaves-to-write,健康的slave的个数小于N,mater就禁止写入。master最少得有多少个健康的slave存活才能执行写命令。这个配置虽然不能保证N个slave都一定能接收到master的写操作,但是能避免没有足够健康的slave的时候,master不
能写入来避免数据丢失。设置为0是关闭该功能。
min-slaves-to-write 3
min-slaves-max-lag 10

从库

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#设置该数据库为其他数据库的从数据库
slaveof <masterip> <masterport>
#主从复制中,设置连接master服务器的密码(前提master启用了认证)
masterauth <master-password>
slave-serve-stale-data yes
# 当从库同主库失去连接或者复制正在进行,从库有两种运行方式:
# 1) 如果slave-serve-stale-data设置为yes(默认设置),从库会继续相应客户端的请求
# 2) 如果slave-serve-stale-data设置为no,除了INFO和SLAVOF命令之外的任何请求都会返回一个错误"SYNC with master in progress"
#当主库发生宕机时候,哨兵会选择优先级最高的一个称为主库,从库优先级配置默认100,数值越小优先级越高
slave-priority 100
#从节点是否只读;默认yes只读,为了保持数据一致性,应保持默认
slave-read-only yes

启动服务

1
2
# 启动主从服务
docker run --privileged=true -p 6379:6379 - -v /docker/redis/conf/redis.conf:/etc/redis/redis.conf --name redis --restart=always -d redis redis-server /etc/redis/redis.conf

查看状态

 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
# 登录主服务,查看是否配置成功
info replication
"# Replication
role:master          #角色 master
connected_slaves:2   # 2个丛机
slave0:ip=192.168.1.13,port=6379,state=online,offset=42,lag=0
slave1:ip=192.168.1.12,port=6379,state=online,offset=42,lag=0
master_failover_state:no-failover
master_replid:60e64987fa9e8b4ada339763422fa76bff989764
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:42
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:42
"

# 从服务,查看状态
info replication

"# Replication
role:slave                   #角色 master
master_host:192.168.1.11     # 可以看到主机信息
master_port:6379
master_link_status:up
master_last_io_seconds_ago:9
master_sync_in_progress:0
slave_repl_offset:364
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:60e64987fa9e8b4ada339763422fa76bff989764
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:364
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:29
repl_backlog_histlen:336
"

注:

主机可以写,从机不能写只能读!主机中的所有信息和数据,都会自动被从机所同步!

原理以及过程

主从复制过程大体可以分为3个阶段:连接建立阶段(即准备阶段)、数据同步阶段、命令传播阶段。

从节点执行slaveof命令后,会执行以下步骤(可以通过日志记录查看):

  1. 保存主节点信息。
  2. 主从建立socket连接。
  3. 发送ping命令
  4. 权限验证
  5. 同步数据集
  6. 命令持续复制

Slave启动成功连接到 master后会发送一个sync同步命令

Master接到命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后, master将传送整个数据文件到slave,并完成一次完全同步.

复制原理在详细看一下

slave与master建立socket链接后,会发送ping命令到master,然后在进行权限校验,然后Master接到命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后, master将传送整个数据文件到slave,并完成一次完全同步

全量复制:而 slave服务在接收到数据库文件数据后,将其存盘并加载到内存中

增量复制:Master继续将新的所有收集到的修改命令依次传给 slave,完成同步

但是只要是重新连接 master,一次完全同步(全量复制)将被自动执行

哨兵模式

产生背景

当主机宕掉,redis就无法提供服务了,为了解决这个问题,我们引入了哨兵来进行选择主服务。

概念

能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库

哨兵模式是一种特殊的模式,首先 Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待 Redis服务器响应,从而监控运行的多个 Redis实例

作用

这里的哨兵有两个作用:

  • 通过发送命令,让 Redis服务器返回监控其运行状态,包括主服务器和从服务器。
  • 当哨兵监测到 master宕机,会自动将slave切换成 master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机

然而一个哨兵进程对Redis服务器进行监控,可能会岀现问题,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式。

假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行 failove过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行 failover【故障转移】操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线

搭建哨兵模式

哨兵配置文件

 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
# Example sentinel. conf
# 哨兵 sentine1实例运行的端口 默认26379
port 26379
# 哨兵sentine1的工作目录
dir /tmp

#哨兵 sentinel1监控的redis主节点的 ip port 
# master-name 可以自己命名的主节点名字 只能由字母A-z、数字0-9、这三个字符".-_"组成。
# quorum 配置多少个 sentinel哨兵统一认为 master主节点失联 那么这时客观上认为主节点失联了
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor master 192.168.1.11 6379 2
# sentinel monitor slave1 192.168.1.12 6379 2
# sentinel monitor slave2 192.168.1.13 6379 2


#当在 Redis实例中开启了 requirepass foobared 授权密码 这样所有连接Redis实例的客户端都要提供密码
# 设置哨兵 sentinel连接主从的密码 注意必须为主从设置一样的验证密码
# sentinel auth-pass <master-name> <password>
sentinel auth-pass master 123456
#sentinel auth-pass slave1 123456
#sentinel auth-pass slave2 123456

# 指定多少毫秒之后 主节点没有应答哨兵sentine1 此时哨兵主观上认为主节点下线 默认30秒
# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds master 30000
#sentinel down-after-milliseconds slave1 30000
#sentinel down-after-milliseconds slave2 30000

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

# 故障转移的超时时间 fai1over- tImeout可 以用在以下这些方面:
#1.同一个 sentinel对同一个master两次fai1over之间的间隔时间
#2.当一个s1ave从一个错误的 master那里同步数据开始计算时间。直到s1ave被纠正为向正确的master那里同步数据时。
#3.当想要取消一个正在进行的fai1over所需要的时间。
#4.当进行 failover时,配置所有slaves指向新的 master所需的最大时间。不过,即使过了这个超时, slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了
#默认三分钟
#sentinel failover-timeout <master-name> <milliseconds>
sentinel failover-timeout master 180000

# SCRIPTS EXECUTION

#配置当某一事件发生时所需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时发邮件通知相关人员。
#对于脚本的运行结果有以下规则:
#若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10
#若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。
#如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
#一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。

#通知型脚本:当 sentinel有任何警告级别的事件发生时(比如说 redis实例的主观失效和客观失效等等),将会去调用这个脚本,这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信息。调用该脚本时,将传给脚本两个参数,一个是事件的类型,一个是事件的描述。如果 sentinel.conf配置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则sentinel无法正常启动成功.
#通知脚本
# sentinel notification-script <master-name> <script-path>
sentinel notification-script master /var/redis/notify.sh


#客户端重新配置主节点参数脚本
#当一个master由于failover而发生改变时,这个脚本将会被调用,通知相关的客户端关于 master地址已经发生改变的信息。
#以下参数将会在调用脚本时传给脚本:
#<master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
#目前< state>总是"fai1over",
# <ro1e>是“leader”或者“observer”中的一个。
#参数from-ip, from-port,to-ip,to-port是用来和旧的master和新的master(即旧的save)通信的
#这个脚本应该是通用的,能被多次调用,不是针对性的
# sentinel client-reconfig-script <master-name> <script-path>
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh

启动哨兵服务

1
2
3
docker run --privileged=true -p 6379:6379 -p 26379:26379 -v /docker/redis/server/conf/redis.conf:/etc/redis/redis.conf -v /docker/redis/sentinel/conf/sentinel.conf:/usr/local/etc/redis/sentinel.conf --name redis --restart=always -d redis redis-server /etc/redis/redis.conf && redis-sentinel /usr/local/etc/redis/sentinel.conf

docker run --name redis  -v /docker/sentinel/conf/sentinel.conf:/usr/local/etc/redis/sentinel.conf -d redis redis-sentinel /usr/local/etc/redis/sentinel.conf

查看状态

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 登录哨兵服务
redis-cli -p 26379
# 查看状态
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=master,status=sdown,address=192.168.1.11:6379,slaves=0,sentinels=1

如果主机此时回来了,只能归并到新的主机下,当做从机,这就是哨兵模式的规则!

优缺点

优点

1、哨兵集群,基于主从复制模式,所有的主从配置优点,它全有

2、主从可以切换,故障可以转移,系统的可用性就会更好

3、哨兵模式就是主从模式的升级,手动到自动,更加健壮

缺点

1、 Redis不好在线扩容的,集群容量一旦到达上限,在线扩容就十分麻烦!

2、实现哨兵模式的配置其实是很麻烦的,里面有很多选择!

docker_compose 安装哨兵模式

目录结构

1
2
3
4
5
6
7
8
|-- docker
	|-- redis
		|-- conf
		|-- data
		|-- docker-compse.yml
	|-- sentinel
		|-- conf
		|-- docker-compose.yml

redis/docker-compse.yml文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
version: '3'

services:
  # 主节点的容器
  redis-server-master:
    image: redis
    container_name: redis
    restart: always
    # 为了规避Docker中端口映射可能带来的问题
    # 这里选择使用host网络
    network_mode: host
    # 指定时区,保证容器内时间正确
    environment:
      TZ: "Asia/Shanghai"
    volumes:
      # 映射配置文件和数据目录
      - ./redis-master.conf:/usr/local/etc/redis/redis.conf
      - ./data/redis-master:/data
    sysctls:
      # 必要的内核参数
      net.core.somaxconn: '511'

https://www.cnblogs.com/alinainai/p/14086960.html

原理

简单的主从集群有个问题,就是主节点挂了之后,无法从新选举新的节点作为主节点进行写操作,导致服务不可用。所以接下来介绍Sentinel(哨兵)功能的使用。哨兵是一个独立的进程,哨兵会实时监控master节点的状态,当master不可用时会从slave节点中选出一个作为新的master,并修改其他节点的配置指向到新的master。

该系统执行以下三个任务:

  • 监控(Monitoring):Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
  • 提醒(Notification):当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
  • 自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。

https://www.jianshu.com/p/7d5fbf90bcd7

使用 Hugo 构建
主题 StackJimmy 设计