Redis 实现主从复制和哨兵机制

1、Redis 主从复制

和MySQL主从复制的原因一样,Redis虽然读取写入的速度都特别快,但是也会产生读压力特别大的情况。为了分担读压力,Redis支持主从复制,Redis的主从结构可以采用一主多从或者级联结构。

1.1什么是主从复制?

将一台Redis服务器的数据,复制到其他的Redis服务器就是所谓的主从复制。前者称为主节点(master),后者称为从节点(slave);数据的复制是单向的,只能由主节点到从节点。默认情况下,一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。主从服务器之间采用的是读写分离的方式,主服务器可以进行读写操作,当发生写操作时自动将写操作同步给从服务器,而从服务器一般是只读,并接受主服务器同步过来写操作命令,然后执行这条命令。

1.2为什么需要主从复制?

主从复制是为了达成高可用。

  • 为了避免单点Redis服务器故障,准备多台服务器,互相连通。将数据复制多个副本保存在不同的服务器上,连接在一起,并保证数据是同步的。

  • 即使有其中一台服务器宕机,其他服务器依然可以继续提供服务,实现Redis的高可用,同时实现数据冗余备份。

    但是,Redis主从复制架构仅仅用来解决数据的冗余备份,从节点仅仅用来同步数据,不能用于顶替宕机的主节点,也就是无法解决master节点出现故障的自动故障转移问题。

1.3主从复制的作用?

主从复制的作用主要包括:

  • 读写分离:主节点写,从节点读,提高服务器的读写负载能力。

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

  • 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。但是,单纯的Redis主从复制架构在当主节点发生故障时,无法自动切换从节点对外提供服务,需要借助哨兵机制来完成。

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

  • 高可用基石:主从复制还是哨兵机制和集群能够实施的基础,因此说主从复制是Redis高可用的基础。

1.4全量 / 增量同步

Redis主从复制可以根据是否是全量分为全量同步和增量同步。

全量同步

Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份。具体步骤如下:

  • 从服务器连接主服务器,发送SYNC命令;

  • 主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令;

  • 主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令;

  • 从服务器收到快照文件后丢弃所有旧数据,载入收到的快照;

  • 主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令;

  • 从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;

完成上面几个步骤后就完成了从服务器数据初始化的所有操作,从服务器此时可以接收来自用户的读请求。

增量同步

Redis增量复制是指Slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程。

增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令。

Redis主从同步策略

主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,slave 在任何时候都可以发起全量同步。redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。

Redis 的主从数据是异步同步的,所以分布式的 Redis 系统并不满足「一致性」要求。当客户端在 Redis 的主节点修改了数据后,立即返回,即使在主从网络断开的情况下,主节点依旧可以正常对外提供修改服务,所以 Redis 满足「可用性」。

Redis 保证「最终一致性」,从节点会努力追赶主节点,最终从节点的状态会和主节点的状态将保持一致。如果网络断开了,主从节点的数据将会出现大量不一致,一旦网络恢复,从节点会采用多种策略努力追赶上落后的数据,继续尽力保持和主节点一致。

1.5搭建主从复制

这里以一台机器的三个端口作演示,分别是 6379、6380、6381,对应主节点、从节点一、从节点二。分别修改三个服务的配置文件的如下内容:

  • 首先创建三个目录,表示三个独立的服务,然后复制redis.conf文件到三个目录

    [root@localhost ~]# pwd
    /root
    [root@localhost ~]# mkdir master slave1 slave2
    [root@localhost ~]# cp /usr/redis7.0/redis-7.0.4/redis.conf master
    [root@localhost ~]# cp /usr/redis7.0/redis-7.0.4/redis.conf slave1
    [root@localhost ~]# cp /usr/redis7.0/redis-7.0.4/redis.conf slave2
    [root@localhost ~]# ls master/
    redis.conf
    [root@localhost ~]# ls slave1
    redis.conf
    [root@localhost ~]# ls slave2
    redis.conf
    [root@localhost ~]# 

     

  • 分别修改三个配置文件的如下内容

    - master
    	port 6379
    	bind 0.0.0.0
    	
    - slave1
    	port 6380
    	bind 0.0.0.0
    	# replicaof <masterip> <masterport>
    	replicaof 192.168.149.143 6379
    	
    - slave2
    	port 6381
    	bind 0.0.0.0
    	# replicaof <masterip> <masterport>
    	replicaof 192.168.149.143 6379

    注意:Redis 5.0 之前使用 slaveof命令,之后版本使用 replicaof命令,形成主服务器和从服务器的关系。

     
  • 然后分别启动三个服务即可
  • [root@localhost src]# ps -aux | grep redis    # 查看是否有正在运行的redis服务
    root       1511  0.0  0.5 149684  5376 pts/0    S+   09:21   0:00 vim redis.conf
    root       1579  0.0  0.0 112824   988 pts/1    S+   09:30   0:00 grep --color=auto redis
    
    
    [root@localhost src]# ./redis-server /root/master/redis.conf
    [root@localhost src]# ./redis-server /root/slave1/redis.conf
    [root@localhost src]# ./redis-server /root/slave2/redis.conf
    [root@localhost src]# ps -aux | grep redis
    root       1511  0.0  0.5 149684  5376 pts/0    S+   09:21   0:00 vim redis.conf
    root       1582  0.2  0.8 163148  8552 ?        Ssl  09:31   0:00 ./redis-server 0.0.0.0:6379
    root       1588  0.3  0.8 238928  8352 ?        Ssl  09:31   0:00 ./redis-server 0.0.0.0:6380
    root       1596  0.3  0.8 163148  8468 ?        Ssl  09:32   0:00 ./redis-server 0.0.0.0:6381
    root       1602  0.0  0.0 112824   988 pts/1    S+   09:32   0:00 grep --color=auto redis
    [root@localhost src]# 

其他配置

# 设置master的密码(如果master用requirepass指定了密码)
masterauth <master-password>
# 设置指定的master用户,可以用该项为slave指定一个特定用户用于同步
masteruser <username>
# 当slave失去与master的连接,或者数据同步正在进行时,slave的行为有两种可能:
# yes(默认):slave会继续响应客户端请求,可能会返回过期数据或空数据
# no:slave将对所有数据访问命令返回指定错误信息,除了以下命令:
# INFO, REPLICAOF, AUTH, SHUTDOWN, REPLCONF, ROLE, CONFIG, SUBSCRIBE,UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBLISH, PUBSUB, COMMAND, POST,HOST and LATENCY
replica-serve-stale-data yes
# 设置slave是否接受写操作,可写的slave实例可能对存储临时数据比较有用(因为写入salve的数据在同master同步后很容易被删除)
# 但如果客户端由于配置错误而向其写入数据,也可能会导致问题,所以默认不接受写操作
replica-read-only yes
# 数据同步策略:磁盘或socket,默认使用socket
# 新创建的slave和重新连接的slave只能执行“完全同步”,即将RDB文件从master传输到副本
# 可能发生的传输方式有两种:
# 磁盘备份:master创建一个用于将RDB文件写入磁盘的进程,完成之后将RDB文件传输给slave
# socket:master创建一个新的进程,直接将RDB文件写入副本套接字
# 使用磁盘方式,所有slave可以共享生成的RDB文件,而使用socket方式,所有salve必须排队等候传输
# 绝大多数情况下,磁盘速度比网络慢
repl-diskless-sync yes
# 如果开启socket方式,可以配置延迟时间,以等待master创建子进程,从而并行的通过socket传输
# 默认值为5秒,设置为0秒则为无延迟,必须等当前传输完成才能继续下一个
repl-diskless-sync-delay 5
# 如果开启socket方式,可以配置最大连接salve数,当连接的slave数量达到最大值时将开始传输,不需要等到延迟时间结束
# 设置为0则为不限制,延迟时间结束才开始传输
repl-diskless-sync-max-replicas 0
# 如果使用socket方式,当slave收到master的rdb文件时有三种选择:
# disabled:将rdb文件存储到磁盘,等全部接收以后加载到内存
# on-empty-db:接受所有rdb文件到内存中,然后直接解析该rdb文件
# swapdb:先拷贝当前数据到内存,当来自master的rdb文件解析成功以后再删除掉拷贝,不成功则恢复,这能避免数据丢失,但需要相当大的内存,有可能导致OOM
repl-diskless-load disabled
# master每隔一段时间slave发送ping,默认值为10秒
repl-ping-replica-period 10
# 超时时间,适用于三种场景:
# 1.同步期间的大容量传输I/O
# 2.master认为slave超时
# 3.slave认为master超时
# 注意超时时间必须大于repl-ping-replica-period设置的时间间隔,默认60s
repl-timeout 60
# 是否设置主从之间tcp链接为nodelay
# 如果设置为yes,Redis将使用较少的带宽将数据复制到副本,这可能会增加数据在副本的延迟
# 如果设置为no,Redis将更多带宽将用于复制,数据在副本的延迟减少
repl-disable-tcp-nodelay no
# 设置复制缓冲区的大小
# 如果slave断开连接,当slave再次连接时,通常不需要重新同步所有数据,只需要重新同步slave断开时丢失的部分数据即可
# 缓冲区越大,副本所能承受的断开时间就越长
# 只有在master至少连接了一个slave时,才会使用缓冲区
repl-backlog-size 1mb
# 如果一段时间后master没有连接的slave,那么缓存将被释放,下面选项配置释放缓冲的时间
# 注意,slave不会释缓冲,因为slave随时可能升级为master,并且与其他slave重新同步数据
# 如果设置为0表示永不释放
repl-backlog-ttl 3600
# Redis哨兵机制会根据优先级来选择要升级为master的slave,优先级较低的slave被认为更适合升级
# 如果优先级设置为0表示无法升级
# 默认情况下,优先级为100
replica-priority 100
# 从master同步数据或者从aof读取数据时可能会出错,造成数据不一致
# 默认设置ignore,表示忽略错误继续执行命令(旧版redis可能会复制持久化在未来版本中可能会出错的命令)
# 设置为panic或panic-on-replicas,出现错误则抛出异常
propagation-error-behavior ignore
# 默认为no,表示当slave无法将从master收到的写命令持久化时,将抛出异常
# 不建议修改默认值,但为了兼容老版本redis可以设置yes,这只会记录一个警告日志并执行收到的写命令
replica-ignore-disk-write-errors no
# 设置是否公布该副本,默认情况,哨兵会公布所有副本
# 副本可以从哨兵的公告中移除,一个未公布的副本会忽略"sentinel replicas <master>"命令并且不会暴露给哨兵的客户端
# 该项不影响replica-priority优先级设置
replica-announced yes
# 以下两个设置通常联合使用,用于设定slave(在线且延迟不超过指定秒数,延迟从最后一次收到slave的ping计算,slave通常每秒一次ping)数量少于某个值时master停止将写操作
# 任何一个设置为0都可关闭该功能
# 默认min-replicas-to-write为0,功能是关闭的,min-replicas-max-lag是10
min-replicas-to-write 3
min-replicas-max-lag 10
# master有多种方式展示slave的ip和port列表,例如“INFO replication”命令,master的“ROLE”命令
# 在使用端口转发或者NAT时,副本可能会使用多个不同的ip和port
# 以下两个设置可以明确的将slave的ip和prt报告给master
# 如果只需要覆盖port或IP地址,可以不需要同时设置这两个选项
replica-announce-ip 5.5.5.5
replica-announce-port 1234

 

1.6测试

127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set name yjs
OK
127.0.0.1:6379> set age 20
OK
127.0.0.1:6379> set website www.imyjs.cn
OK
127.0.0.1:6379> keys *
1) "name"
2) "website"
3) "age"
127.0.0.1:6379> quit
[root@localhost src]# ./redis-cli -p 6380
127.0.0.1:6380> keys *
1) "age"
2) "name"
3) "website"
127.0.0.1:6380> get webite
(nil)
127.0.0.1:6380> get website
"www.imyjs.cn"
127.0.0.1:6380> set slave1 testSet
(error) READONLY You can't write against a read only replica.
127.0.0.1:6380> quit
[root@localhost src]# ./redis-cli -p 6381
127.0.0.1:6381> keys *
1) "name"
2) "age"
3) "website"
127.0.0.1:6381> get name
"yjs"
127.0.0.1:6381> set slave2 testSet
(error) READONLY You can't write against a read only replica.
127.0.0.1:6381> quit
[root@localhost src]# 

 

1.7Redis主从复制特点

  • 采用异步复制;

  • 一个主Redis可以含有多个从Redis;

  • 每个从Redis可以接收来自其他从Redis服务器的连接;

  • 主从复制对于主Redis服务器来说是非阻塞的,这意味着当从服务器在进行主从复制同步过程中,主Redis仍然可以处理外界的访问请求;

  • 主从复制对于从Redis服务器来说也是非阻塞的,这意味着,即使从Redis在进行主从复制过程中也可以接受外界的查询请求,只不过这时候从Redis返回的是以前老的数据, 如果你不想这样,那么在启动Redis时,可以在配置文件中进行设置,那么从Redis在复制同步过程中来自外界的查询请求都会返回错误给客户端;(虽然说主从复制过程中对于从Redis是非阻塞的,但是当从Redis从主Redis同步过来最新的数据后还需要将新数据加载到内存中,在加载到内存的过程中是阻塞的,在这段时间内的请求将会被阻,但是即使对于大数据集,加载到内存的时间也是比较多的);

  • 主从复制提高了Redis服务的扩展性,避免单个Redis服务器的读写访问压力过大的问题,同时也可以给为数据备份及冗余提供一种解决方案;0为了编码主Redis服务器写磁盘压力带来的开销,可以配置让主Redis不在将数据持久化到磁盘,而是通过连接让一个配置的从Redis服务器及时的将相关数据持久化到磁盘,不过这样会存在一个问题,就是主Redis服务器一旦重启,因为主Redis服务器数据为空,这时候通过主从同步可能导致从Redis服务器上的数据也被清空。

以上就是关于Redis主从复制的相关概念以及作用和特点,重点是如何搭建主从复制,相对来说使用还是很简单的,关于主从复制原理这里并没有整理,具体可以看以下参考的文章。

参考文章:

2、Redis哨兵机制

2.1为什么需要哨兵机制?

在上边主从复制模式下,主节点负责写请求,然后异步同步给从节点,从节点负责处理读请求。如果主节点宕机了,需要手动将从节点晋升为主节点,并且还要切换客户端的连接数据源。这就无法达到高可用,而通过哨兵模式就可以解决这一问题。

总结就是主从复制模式的一个缺点,就在于无法实现自动化地故障恢复 。Redis后来引入了哨兵机制,哨兵机制大大提升了系统的高可用性。

2.2什么是哨兵机制?

Sentinel(哨兵)机制是Redis 的高可用性解决方案,Redis中的哨兵节点属于是特殊的Redis实例服务,不提供读写服务,主要用来监控Redis实例节点。也就是说,哨兵在启动时,不会去加载RDB文件。

哨兵架构下客户端第一次从哨兵找出Redis的主节点,后续就直接访问Redis的主节点,不会每次都通过sentinel代理访问Redis的主节点,当Redis的主节点挂掉时,哨兵会第一时间感知到,并且在从节点中重新选出来一个新的主节点,然后将新的主节点信息通知给客户端,从而实现高可用。这里面Redis的客户端一般都实现了订阅功能,订阅sentinel发布的节点变动消息。简单的说哨兵就是带有自动故障转移功能的主从架构

但是哨兵机制仍然无法解决: 1.单节点并发压力问题 2.单节点内存和磁盘物理上限,需要使用集群架构解决这些问题。

2.3哨兵的任务

哨兵主要具有三个作用, 监控、自动故障迁移与通知

  • 监控:哨兵会利用心跳机制,周期性不断地检测主库与从库是否运作正常。

  • 自动故障迁移:当一个Master不能正常工作时,哨兵会进行自动故障迁移操作,将失效Master的其中一个Slave升级为新的Master,并让失效Master的其他Slave改为复制新的Master;当客户端试图连接失效的Master时,集群也会向客户端返回新Master的地址,使得集群可以使用新Master代替失效Master。

  • 通知:当被监控的某个Redis节点出现问题时,哨兵会将新主库的地址通知到所有从库,使得所有从库与旧主库slaveof新主库,也会将新主库的地址通知到客户端上。

2.4搭建哨兵架构

这里以一台机器的三个端口作演示,分别是 6379、6380、6381,对应主节点、从节点一、从节点二。

  1. 在主节点上创建哨兵配置

    在Master对应redis.conf同目录下新建sentinel.conf文件,名字绝对不能错;

  1. 配置sentinel.conf文件,配置需要监听的主从的master节点。

    sentinel monitor <master‐name> <ip> <redis‐port> <quorum>

    • master‐name:主节点master的名字

    • quorum:哨兵集群中多少个sentinel 认为 master 失效才判定为客观下线,一般配节点数/2+1,也就是说大于半数

    如果主从master设置了密码,还需要配置:sentinel auth-pass <master-name> <password>

  1. 启动哨兵模式进行测试

    redis-sentinel /root/sentinel/sentinel.conf

2.5通过SpringBoot操作哨兵

# redis sentinel 配置
# master书写是使用哨兵监听的那个名称
spring.redis.sentinel.master=mymaster
# 连接的不再是一个具体redis主机,书写的是多个哨兵节点
spring.redis.sentinel.nodes=192.168.149.143:26379

 

如果连接过程中出现如下错误:

RedisConnectionException: 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)

 

解决方案:在哨兵的配置文件中加入bind 0.0.0.0 开启远程连接权限

微信关注

                      编程那点事儿

本站为非盈利性站点,所有资源、文章等仅供学习参考,并不贩卖软件且不存在任何商业目的及用途,如果您访问和下载某文件,表示您同意只将此文件用于参考、学习而非其他用途。
本站所发布的一切软件资源、文章内容、页面内容可能整理来自于互联网,在此郑重声明本站仅限用于学习和研究目的;并告知用户不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。
如果本站相关内容有侵犯到您的合法权益,请仔细阅读本站公布的投诉指引页相关内容联系我,依法依规进行处理!
作者:理想
链接:https://www.imyjs.cn/archives/1102
THE END
二维码
Redis 实现主从复制和哨兵机制
<<上一篇
下一篇>>
文章目录
关闭
目 录