为什么需要主从复制

主要是安全性和可用性的考虑,如果只有一个redis服务,一旦服务宕机,那么所有的客户端无法访问,会对业务造成很大影响,另一个是一旦硬件损坏,单机无法恢复,会对数据带来灾难性影响。
另一个考虑是性能的提升,主从复制模式,写请求只能由master节点来处理,但是读请求可能用从节点来分担请求,提高效率。
可用性、数据安全、性能都可以通过搭建多个 Reids 服务实现。其中有一个是主节点(master),可以有多个从节点(slave)。
主从之间通过数据同步,存储完全相同的数据。如果主节点发生故障,则把某个从节点改成主节点,访问新的主节点
使用哨兵模式来实现主从复制架构是为了主从节点的切换故障转移能够自动进行。

Redis主从复制
实现主从复制架构的手段:
一主多从

1
2
3
4
5
6
7
8
#从节点的配置文件添加,主节点的ip端口,表示以该节点为主节点实现主从复制。
slaveof 192.168.18.140 6379
#从节点启动服务的时候指定master节点
./redis-server --slaveof 192.168.18.140 6379
#从节点启动后,在客户端使用命令,指定master节点
slaveof 192.168.18.140 6379
# 查看主从架构的状态。
info replication

从节点不能写入数据(只读),只能从 master 节点同步数据。get成功,set 失败。
从节点取消指定主节点,自己成为主节点,也就是从节点断开slave模式:
slave no one 命令。

主从复制原理

连接阶段

  • slave 节点启动时,会在自己本地保存master 节点的信息,包括ip port等。
  • slave 节点内部有个定时任务 replicationCron,每隔1秒钟检查是否有新的master节点要连接和复制,如果有,就跟master节点建立一个Socket连接,如果连接成功,从节点为该Socket连接创建一个专门处理复制工作的文件事件处理器,负责后续的复制工作,如接收RDB命令,接收命令传播等。
  • 当从节点变成了主节点的一个客户端之后,会给主节点发送 ping 请求。

数据同步阶段

slave节点第一次会执行全量复制,master节点通过bgsave命令在本地生成一个RDB数据快照文件,然后将RDB文件通过Socket连接传送给从节点,然后从节点先清空自身数据,然后使用接收到的RDB文件加载数据。 传送文件会有个超时时间,如果超时了会进行重连,可以设置大一点,防止超时循环重连,配置文件修改repl-timeout配置项设置。

在开始生成RDB文件时,master节点还会接收新的写命令,会把新的写命令缓存在内存中,在slave 节点通过RDB文件加载数据成功后,再把新的写命令发送给slave节点。

命令传播阶段:从节点数据同步完成后,可以开始对外提供读服务,主节点还是会继续执行写命令,然后异步将命令发送给slave 节点用于同步数据。是异步的,所以数据一致性必然会存在延时,与Zookeeper采用CP模式保证数据分布式一致性不一样,redis使用的是AP保证服务可用性。

如果从节点由于某些原因,比如宕机啥的,与主节点断开连接了一段时间,在重新连接后会使用增量复制来同步数据,主节点和从节点都有master_repl_offset命令来记录数据的偏移量,偏移量越大,数据越新,一般主节点都要大于等于从节点的偏移量,可以使用该偏移量来进行增量复制,增量的数据就是主节点与从节点直接的数据偏移量之差。

数据一致性延迟是不可避免的,只能通过优化网络来尽可能减少数据延时时间。

1
repl-disable-tcp-nodelay no

上面配置的作用是:当设置为yes时,TCP会对多个写命令TCP包进行合并一次性发送从而减少带宽,但是发送的频率会降低,从节点的数据延时会增加,一致性变差,具体发送频率与linux内核参数有关,默认为40ms,当设置为no时,TCP会立马把主节点的数据发送给从节点,带宽增加但延时变小。

一般来说,只有当应用对 Redis 数据不一致的容忍度较高,且主从节点之间网络状况不好时,才会设置为 yes;多数情况使用默认值 no。

主从复制的不足

  1. RDB文件过大时,同步耗时。
  2. 在一主一从或者一主多从情况下,如果主节点挂了,对外提供的写服务就是不可用了(从节点仍然可以对外提供读服务),单点问题没有解决,需要手动进行主从的切换才行,但是这样比较费事费力,还会导致服务一段时间不可用。
    所以可以使用哨兵模式来实现自动的主从切换和故障转移。

Docker下redis的主从配置

拉取redis镜像

1
docker pull redis

启动3个redis容器

启动3个redis容器服务,分别使用到6379、6380、6381端口

1
2
3
docker run  redis-6379 -p 6379:6379 -d
docker run redis-6379 -p 6380:6379 -d
docker run redis-6379 -p 6381:6379 -d

查看容器

1
docker ps

进入容器

1
docker exec -it 容器ID/name redis-cli

redis集群配置

看容器内网的ip地址

1
docker inspect 容器ID/name

查看当前redis角色(主还是从)

进入docker容器内部,查看当前redis角色(主还是从)

1
2
3
4
5
[root@tcy1 tcy]# docker exec -it 容器ID /bin/bash
root@ab54741166e1:/data# redis-cli
127.0.0.1:6379> info replication
role:master // 主
role:slave // 从

使用redis-cli命令修改redis-6380、redis-6381的主机为172.17.0.3:6379

1
SLAVEOF 172.17.0.3 6379

配置Sentinel哨兵

进入3台redis容器内部进行配置,在容器根目录里面创建sentinel.conf文件文件
内容为:sentinel monitor mymaster 172.17.0.1 6379 1

1
2
3
[root@tcy1 tcy]# docker exec -it a9fa77adc598 /bin/bash
root@a9fa77adc598:/data# cd / && touch sentinel.conf r
oot@a9fa77adc598:/# vim /sentinel.conf

如果出现:bash: vim: command not found

解决:

  1. apt-get update
  2. apt-get install vim
启动哨兵
1
redis-sentinel /sentinel.conf

哨兵模式

哨兵(Sentinel)模式的大致思路是创建一台服务器来监控所有的Redis服务节点状态,比如master节点超过一定时间没有给监控服务器发送心跳报文,就把master标记为下线,然后把某个slave节点变成master节点,重写进行数据的同步。应用程序(客户端)每一次都是从这个监控服务器拿到master的地址。

如果监控服务器本身出问题了怎么办?那我们就拿不到 master 的地址了,应用也没有办法访问。可以对sentinel服务器进行集群。

从 Redis2.8 版本起,提供了一个稳定版本的 Sentinel(哨兵),用来解决高可用的问题。它是一个特殊状态的 redis 实例。

我们会启动一个或者多个 Sentinel 的服务(通过 src/redis-sentinel),它本质上只是一个运行在特殊模式之下的 Redis,Sentinel 通过 info 命令得到被监听 Redis 机器的master,slave 等信息。

为了保证监控服务器的可用性,我们会对 Sentinel 做集群的部署。
Sentinel 既监控所有的 Redis 服务,Sentinel 之间也相互监控。
注意:Sentinel 本身没有主从之分,只有 Redis 服务节点有主从之分。

此时就要以下角色

  1. sentinel集合。
  2. redis服务节点group(一主多从为一个group)。

服务下线

Sentinel默认以每秒一次的频率向各Redis服务节点发送PING命令,如果在down-after-milliseconds 内都没有收到有效回复,Sentinel 会将该服务器标记为主观下线。

1
sentinel down-after-milliseconds <master-name> <milliseconds>  #sentinel.conf

这个时候 Sentinel 节点会继续询问其他的 Sentinel 节点,确认这个节点是否下线,如果多数(超过半数) Sentinel 节点都认为 master 下线,master 才真正确认被下线(客观下线),这个时候就需要重新选举 master。

由于Sentinel实现了集群,所以首先需要通过Raft算法在Sentinel集群中选取出一个临时Leader来对下线的master进行故障转移。

故障转移

选举出的Leader Sentinel会向某个从节点发送 slaveof no one 使它成为一个独立主节点,然后向其余从节点发送slaveof xxx xxx ,使得这些从节点成为新主节点的从节点,并进行数据的同步,然后故障转移完成。

如何从多个slave节点中选择一个成为新的master节点:

关于从节点选举,一共有四个因素影响选举的结果,分别是断开连接时长、优先级排序、复制数量、进程 id。
如果与哨兵连接断开的比较久,超过了某个阈值,就直接失去了选举权。如果拥有选举权,那就看谁的优先级高,这个在配置文件redis.conf里可以设置(replica-priority 100),数值越小优先级越高。
如果优先级相同,就看谁从 master 中复制的数据最多(复制偏移量最大),选最多的那个,如果复制数量也相同,就选择进程 id 最小的那个。

Sentinel搭建实战

环境:
需要三个服务节点一主二从和三个sentinel集群节点,由于虚拟机不够,这里就只用三台虚拟机,每台虚拟机部署一个服务节点和一个sentinel节点。

ip
节点角色/端口
192.168.18.140 master 6379 / sentinel 26379
192.168.18.141 salve 6379 / sentinel 26379
192.168.18.142 slave 6379 / sentinel 26379

修改从服务节点141、142的配置文件redis.conf,
添加 slaveof 192.168.18.140 6379。
或在redis-cli 内执行命令行 SLAVEOF 192.168.18.140 6379
来建立主从(主无需执行或修改 当前命令)

修改三个sentinel节点的配置文件sentinel.conf:【哨兵配置】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
daemonize yes    #以服务进程模式启动
port 26379 #端口
protected-mode no # 是否允许外部网络访问
dir "/usr/local/redis-5.0.5/sentinel-tmp" #sentinel的工作目录
sentinel monitor redis-master 192.168.18.140 6379 2 #要监控的服务节点 master节点的ip port 从节点个数
sentinel down-after-milliseconds redis-master 30000 #master宕机多久,才会被 Sentinel 主观认为下线。
sentinel failover-timeout redis-master 180000 #故障转移的超时时间
#指定了在进行主从切换故障转移时,最多可以有多少个salve同时对新的master进行数据同步,
#这个设置的越少,故障转移需要的时间越长,因为能够同时进行同步的slave越少,这个设置的越大,
#代表在故障转移期间能够对外提供读服务的slave节点就越少,因为在同步期间服务不可用。
#比如故障转移时有4个slave节点向新的master节点同步数据,如果设置为1,那么同步将串行执行,
#同一时间只有一台salve进行同步,另外三台还能对外提供服务,
#如果设置为4,那么会4台slave同时进行同步,那么此时就没有从服务器对外提供服务了。
sentinel parallel-syncs redis-master 1

先启动三台服务节点再启动三台sentinel节点:

1
2
./redis-server ../redis.conf
./redis-sentinel ../sentinel.conf

验证

  1. 使用info replication命令查看服务节点的状态。
  2. 故意关闭shutdown主节点。
  3. 经过一定时间后,某一台从节点会被升为主节点。自动故障转移成功。

哨兵机制的不足

  • 主从切换时会丢失数据,主从复制是异步执行的,master节点写入数据后没来得及同步到slave节点就发送了故障,进行主从切换,该没有同步的数据就会丢失。

  • 只能单点写,没有解决水平扩容的问题。

  • 这种主从模式所以节点保存的数据都是一样的,无法对数据进行水平扩容,如果数据量非常大,这个时候我们需要多个 master-slave 的 group,把数据分布到不同的 group 中。


返回 redis 系列
avatar
懒觉猫先生
欢迎你们的到来!
关注我吧
最新文章
最新评论
正在加载中...
网站资讯
文章数目 :
172
已运行时间 :
本站总字数 :
111.4k
本站访客数 :
本站总访问量 :
最后更新时间 :