• NoSQL数据库之Redis主从同步与级联复制


    消息队列

    消息队列: 把要传输的数据放在队列中

    功能: 可以实现多个系统之间的解耦,异步,削峰/限流等
    常用的消息队列应用: kafka,rabbitMQ,redis

    消息队列主要分为两种,这两种模式Redis都支持

    生产者/消费者模式
    发布者/订阅者模式
    

    生产者消费者模式

    在生产者/消费者(Producer/Consumer)模式下,上层应用接收到的外部请求后开始处理其当前步骤的操
    作,在执行完成后将已经完成的操作发送至指定的频道(channel,逻辑队列)当中,并由其下层的应用监听
    该频道并继续下一步的操作,如果其处理完成后没有下一步的操作就直接返回数据给外部请求,如果还
    有下一步的操作就再将任务发布到另外一个频道,由另外一个消费者继续监听和处理。此模式应用广泛
    

    模式介绍

    生产者消费者模式下,多个消费者同时监听一个队列,但是一个消息只能被最先抢到消息的消费者消
    费,即消息任务是一次性读取和处理,此模式在分布式业务架构中很常用,比较常用的消息队列软件还有RabbitMQ、Kafka、RocketMQ、ActiveMQ等。
    

    队列介绍

    队列当中的消息由不同的生产者写入,也会有不同的消费者取出进行消费处理,但是一个消息一定是只能被取出一次也就是被消费一次。
    

    生产者发布消息

    [root@redis ~]# redis-cli
    127.0.0.1:6379> AUTH 123456
    OK
    127.0.0.1:6379> LPUSH channel1 msg1 #从管道的左侧写入
    (integer) 1
    127.0.0.1:6379> LPUSH channel1 msg2
    (integer) 2
    127.0.0.1:6379> LPUSH channel1 msg3
    (integer) 3
    127.0.0.1:6379> LPUSH channel1 msg4
    (integer) 4
    127.0.0.1:6379> LPUSH channel1 msg5
    (integer) 5
    

    查看队列所有消息

    127.0.0.1:6379> LRANGE channel1 0 -1
    1) "msg5"
    2) "msg4"
    3) "msg3"
    4) "msg2"
    5) "msg1"
    

    消费者消费消息

    127.0.0.1:6379> RPOP channel1 #从管道的右侧消费,用于消息的先进先出
    "msg1"
    127.0.0.1:6379> RPOP channel1
    "msg2"
    127.0.0.1:6379> RPOP channel1
    "msg3"
    127.0.0.1:6379> RPOP channel1
    "msg4"
    127.0.0.1:6379> RPOP channel1
    "msg5"
    127.0.0.1:6379> RPOP channel1
    (nil)
    

    再次验证队列消息

    127.0.0.1:6379> LRANGE channel1 0 -1
    (empty list or set) #队列中的消息已经被已全部消费完毕
    

    发布者订阅模式

    模式简介

    在发布者订阅者模式下,发布者将消息发布到指定的channel里面,凡是监听该channel的消费者都会收
    到同样的一份消息,这种模式类似于是收音机的广播模式,即凡是收听某个频道的听众都会收到主持人发布的相同的消息内容。此模式常用语群聊天、群通知、群公告等场景
    

    Publisher:发布者

    Subscriber:订阅者

    Channel:频道

    订阅者监听频道
    [root@redis ~]# redis-cli
    127.0.0.1:6379> AUTH 123456
    OK
    127.0.0.1:6379> SUBSCRIBE channel1 #订阅者事先订阅指定的频道,之后发布的消息才
    能收到
    Reading messages... (press Ctrl-C to quit)
    1) "subscribe"
    2) "channel1"
    3) (integer) 1
    

    发布者发布消息

    127.0.0.1:6379> PUBLISH channel1 test1 #发布者发布消息
    (integer) 2 #订阅者个数
    127.0.0.1:6379> PUBLISH channel1 test2
    (integer) 2
    

    各个订阅者都能收到消息

    订阅多个频道

    #订阅指定的多个频道
    127.0.0.1:6379> SUBSCRIBE channel1 channel2
    

    订阅所有频道

    127.0.0.1:6379> PSUBSCRIBE * #支持通配符*
    

    订阅匹配的频道

    127.0.0.1:6379> PSUBSCRIBE chann* #匹配订阅多个频道
    

    取消订阅

    127.0.0.1:6379> unsubscribe channel1
    1) "unsubscribe"
    2) "channel1"
    3) (integer) 0
    

    redis 集群与高可用

    虽然Redis可以实现单机的数据持久化,但无论是RDB也好或者AOF也好,都解决不了单点宕机问题,即
    一旦单台 redis服务器本身出现系统故障、硬件故障等问题后,就会直接造成数据的丢失

    此外,单机的性能也是有极限的,因此需要使用另外的技术来解决单点故障和性能扩展的问题。

    redis 主从复制

    redis 主从复制架构

    主从模式(master/slave),可以实现Redis数据的跨主机备份。
    程序端连接到高可用负载的VIP,然后连接到负载服务器设置的Redis后端real server,此模式不需要在
    程序里面配 置Redis服务器的真实IP地址,当后期Redis服务器IP地址发生变更只需要更改redis 相应的
    后端real server即可, 可避免更改程序中的IP地址设置。
    

    主从复制特点

    一个master可以有多个slave
    一个slave只能有一个master
    数据流向是从master到slave单向的
    

    主从复制实现

    Redis Slave 也要开启持久化并设置和master同样的连接密码,因为后期slave会有提升为master的可
    能,Slave 端切换master同步后会丢失之前的所有数据,而通过持久化可以恢复数据
    一旦某个Slave成为一个master的slave,Redis Slave服务会清空当前redis服务器上的所有数据并将
    master的数据导入到自己的内存,但是如果只是断开同步关系后,则不会删除当前已经同步过的数据。
    
    当配置Redis复制功能时,强烈建议打开主服务器的持久化功能。否则的话,由于延迟等问题,部署的主
    节点Redis服务应该要避免自动启动。
    

    参考案例: 导致主从服务器数据全部丢失

    1.假设节点A为主服务器,并且关闭了持久化。并且节点B和节点c从节点A复制数据
    2.节点A崩溃,然后由自动拉起服务重启了节点A.由于节点A的持久化被关闭了,所以重启之后没有任何数据
    3.节点B和节点c将从节点A复制数据,但是A的数据是空的,于是就把自身保存的数据副本删除。
    

    在关闭主服务器上的持久化,并同时开启自动拉起进程的情况下,即便使用Sentinel来实现Redis的高可
    用性,也是非常危险的。因为主服务器可能拉起得非常快,以至于Sentinel在配置的心跳时间间隔内没
    有检测到主服务器已被重启,然后还是会执行上面的数据丢失的流程。无论何时,数据安全都是极其重要的,所以应该禁止主服务器关闭持久化的同时自动启动。

    命令行配置

    启用主从同步

    默认redis 状态为master,需要转换为slave角色并指向master服务器的IP+PORT+Password
    在从节点执行REPLICAOF MASTER_IP PORT 指令可以启用主从同步复制功能,早期版本使用 SLAVEOF指令(注意防火墙问题Firewalld)

    127.0.0.1:6379> REPLICAOF MASTER_IP PORT
    127.0.0.1:6379> CONFIG SET masterauth <masterpass>
    

    范例:

    #在mater上设置key1
    [root@centos8 ~]# redis-cli
    127.0.0.1:6379> AUTH 123456
    OK
    127.0.0.1:6379> config get requirepass
    1) "requirepass"
    2) ""
    127.0.0.1:6379> config set requirepass 123456
    OK
    127.0.0.1:6379> config get requirepass
    (error) NOAUTH Authentication required.
    127.0.0.1:6379> auth 123456
    OK
    127.0.0.1:6379> config get requirepass
    1) "requirepass"
    2) "123456"
    
    127.0.0.1:6379> INFO replication
    # Replication
    role:master
    connected_slaves:0
    ...
    127.0.0.1:6379> SET key1 v1-master
    OK
    127.0.0.1:6379> KEYS *
    1) "key1"
    127.0.0.1:6379> GET key1
    "v1-master"
    127.0.0.1:6379>
    

    范例:

    #以下都在slave上执行,登录
    [root@centos8 ~]# redis-cli
    127.0.0.1:6379> info
    NOAUTH Authentication required.
    127.0.0.1:6379> AUTH 123456
    OK
    127.0.0.1:6379> INFO replication #查看当前角色默认为master
    # Replication
    role:master
    connected_slaves:0
    ...
    127.0.0.1:6379> SET key1 v1-slave-18
    OK
    127.0.0.1:6379> KEYS *
    1) "key1"
    127.0.0.1:6379> GET key1
    "v1-slave-18"
    127.0.0.1:6379>
    
    #在第二个slave,也设置相同的key1,但值不同
    127.0.0.1:6379> KEYS *
    1) "key1"
    127.0.0.1:6379> GET key1
    "v1-slave-28"
    127.0.0.1:6379>
    127.0.0.1:6379> INFO replication
    # Replication
    role:master
    ...
    #在slave上设置master的IP和端口,4.0版之前的指令为slaveof
    127.0.0.1:6379> REPLICAOF 172.31.0.8 6379 #仍可使用SLAVEOF MasterIP Port
    OK
    #在slave上设置master的密码,才可以同步
    127.0.0.1:6379> CONFIG SET masterauth 123456
    OK
    127.0.0.1:6379> INFO replication
    # Replication #角色变为slave
    role:slave
    master_host:172.31.0.8 #指向master
    master_port:6379
    master_link_status:up
    
    #查看已经同步成功
    127.0.0.1:6379> GET key1
    "v1-master"
    #在master上可以看到所有slave信息
    127.0.0.1:6379> INFO replication
    # Replication
    role:master
    connected_slaves:2
    slave0:ip=172.31.0.18,port=6379,state=online,offset=112,lag=1 #slave信息
    slave1:ip=172.31.0.28,port=6379,state=online,offset=112,lag=1
    

    删除主从同步

    在从节点执行 REPLIATOF NO ONE 指令可以取消主从复制

    #取消复制,在slave上执行REPLIATOF NO ONE,会断开和master的连接不再主从复制, 但不会清除slave
    上已有的数据
    127.0.0.1:6379> REPLICAOF no one
    

    同步日志

    在 master 上观察日志

    [root@centos8 ~]# tail /var/log/redis/redis.log
    24402:M 06 Oct 2021 09:09:16.448 * Replica 172.31.0.18:6379 asks for
    synchronization
    

    在 slave 节点观察日志

    [root@centos8 ~]# tail -f /var/log/redis/redis.log
    24395:S 06 Oct 2021 09:09:16.411 * Connecting to MASTER 172.31.0.8:6379
    24395:S 06 Oct 2021 09:09:16.412 * MASTER <-> REPLICA sync started
    

    修改slave节点配置文件

    范例: (CentOS8 注意防火墙问题)

    [root@centos8 ~]# vim /etc/redis.conf
    .......
    # replicaof <masterip> <masterport>
    replicaof 172.31.0.8 6379 #指定master的IP和端口号
    # If the master is password protected (using the "requirepass" configuration
    # directive below) it is possible to tell the replica to authenticate before
    # starting the replication synchronization process, otherwise the master will
    # refuse the replica request.
    # masterauth <master-password>
    masterauth 123456 #如果密码需要设置
    .......
    [root@centos8 ~]# systemctl restart redis
    

    master和slave查看状态

    #在master上查看状态
    127.0.0.1:6379> info replication
    # Replication
    role:master
    connected_slaves:1
    slave0:ip=172.31.0.18,port=6379,state=online,offset=1104403,lag=0
    
    #在slave上查看状态
    127.0.0.1:6379> get key1 #同步成功后,slave上的key信息丢失,从master复制过来新的值
    "v1-master"
    127.0.0.1:6379> INFO replication
    # Replication
    role:slave
    master_host:172.31.0.8
    master_port:6379
    master_link_status:up
    ...
    
    #停止master的redis服务:systemctl stop redis,在slave上可以观察到以下现象
    127.0.0.1:6379> INFO replication
    # Replication
    role:slave
    master_host:172.31.0.8
    master_port:6379
    master_link_status:down #显示down,表示无法连接master
    

    slave 观察日志

    [root@centos8 ~]# tail -f /var/log/redis/redis.log
    24592:S 20 Feb 2021 12:03:58.792 * Connecting to MASTER 172.31.0.8:6379
    24592:S 20 Feb 2021 12:03:58.792 * MASTER <-> REPLICA sync started
    

    Master日志

    [root@centos8 ~]# tail /var/log/redis/redis.log
    11846:M 20 Feb 2021 12:11:35.171 * DB loaded from disk: 0.000 seconds
    11846:M 20 Feb 2021 12:11:35.171 * Ready to accept connections
    

    slave 状态只读无法写入数据

    127.0.0.1:6379> set key1 v1-slave
    (error) READONLY You can't write against a read only replica.
    

    主从复制故障恢复

    主从复制故障恢复过程介绍

    slave 节点故障和恢复

    当 slave 节点故障时,将Redis Client指向另一个 slave 节点即可,并及时修复故障从节点

    master 节点故障和恢复

    需要提升slave为新的master

    master故障后,只能手动提升一个slave为新master,不支持自动切换。
    之后将其它的slave节点重新指定新的master为master节点
    Master的切换会导致master_replid发生变化,slave之前的master_replid就和当前master不一致从而会引发所有 slave的全量同步。

    主从复制故障恢复实现

    假设当前主节点172.31.0.8故障,提升172.31.0.18为新的master

    127.0.0.1:6379> info replication
    # Replication
    role:slave
    master_host:172.31.0.18
    master_port:6379
    master_link_status:up
    

    停止slave同步并提升为新的master

    #将当前 slave 节点提升为 master 角色
    127.0.0.1:6379> REPLICAOF NO ONE #旧版使用SLAVEOF no one
    OK
    (5.04s)
    127.0.0.1:6379> info replication
    # Replication
    role:master
    connected_slaves:0
    

    测试能否写入数据:

    127.0.0.1:6379> set keytest1 vtest1
    OK
    

    修改所有slave 指向新的master节点

    #修改172.31.0.28节点指向新的master节点172.31.0.18
    127.0.0.1:6379> SLAVEOF 172.31.0.8 6379
    OK
    127.0.0.1:6379> set key100 v100
    (error) READONLY You can't write against a read only replica.
    #查看日志
    [root@centos8 ~]# tail -f /var/log/redis/redis.log
    1762:S 20 Feb 2021 13:28:21.943 # Connection with master lost.
    1762:S 20 Feb 2021 13:28:21.943 * Caching the disconnected master state.
    

    在新master可看到slave

    #在新master节点172.31.0.18上查看状态
    127.0.0.1:6379> INFO replication
    # Replication
    role:master
    connected_slaves:1
    slave0:ip=172.31.0.28,port=6379,state=online,offset=4606,lag=0
    ...
    

    实现 redis 的级联复制

    即实现基于Slave节点的Slave
    master和slave1节点无需修改,只需要修改slave2及slave3指向slave1做为mater即可

    #在slave2和slave3上执行下面指令
    127.0.0.1:6379> REPLICAOF 172.31.0.18 6379
    OK
    127.0.0.1:6379> CONFIG SET masterauth 123456
    

    在 master 设置key,观察是否同步

    #在master新建key
    127.0.0.1:6379> set key2 v2
    OK
    127.0.0.1:6379> get key2
    "v2"
    #在slave1和slave2验证key
    127.0.0.1:6379> get key2
    "v2"
    #在slave1和slave2都无法新建key
    127.0.0.1:6379> set key3 v3
    (error) READONLY You can't write against a read only replica.
    

    在中间那个slave查看状态

    127.0.0.1:6379> INFO replication
    # Replication
    role:slave
    master_host:172.31.0.8
    master_port:6379
    master_link_status:up
    master_last_io_seconds_ago:8 #最近一次与master通信已经过去多少秒。
    master_sync_in_progress:0 #是否正在与master通信。
    slave_repl_offset:4312 #当前同步的偏移量
    slave_priority:100 #slave优先级,master故障后值越小越优先同步。
    slave_read_only:1
    connected_slaves:1
    slave0:ip=172.31.0.28,port=6379,state=online,offset=4312,lag=0 #slave的slave节点
    

    主从复制优化

    主从复制过程

    Redis主从复制分为全量同步和增量同步

    首次主从同步是全量同步,主从同步可以让从服务器从主服务器同步数据,而且从服务器还可再有其它
    的从服务器,即另外一台redis服务器可以从一台从服务器进行数据同步,redis 的主从同步是非阻塞
    的,master收到从服务器的psync(2.8版本之前是SYNC)命令,会fork一个子进程在后台执行bgsave命
    令,并将新写入的数据写入到一个缓冲区中,bgsave执行完成之后,将生成的RDB文件发送给slave,然
    后master再将缓冲区的内容以redis协议格式再全部发送给slave,slave 先删除旧数据,slave将收到后的
    RDB文件载入自己的内存,再加载所有收到缓冲区的内容 从而这样一次完整的数据同步
    
    Redis全量复制一般发生在Slave首次初始化阶段,这时Slave需要将Master上的所有数据都复制一份。
    
    全量同步之后再次需要同步时,从服务器只要发送当前的offset位置(等同于MySQL的binlog的位置)给主
    服务器,然后主服务器根据相应的位置将之后的数据(包括写在缓冲区的积压数据)发送给从服务器,再次
    将其保存到从节点内存即可。
    
    主从同步完整过程

    具体主从同步过程如下:

    1)从服务器连接主服务器,发送PSYNC命令
    2)主服务器接收到PSYNC命令后,开始执行BGSAVE命令生成RDB快照文件并使用缓冲区记录此后执行的所有
    写命令
    3)主服务器BGSAVE执行完后,向所有从服务器发送RDB快照文件,并在发送期间继续记录被执行的写命令
    4)从服务器收到快照文件后丢弃所有旧数据,载入收到的快照至内存
    5)主服务器快照发送完毕后,开始向从服务器发送缓冲区中的写命令
    6)从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令
    7)后期同步会先发送自己slave_repl_offset位置,只同步新增加的数据,不再全量同步
    

    复制缓冲区(环形队列)配置参数:

    #复制缓冲区大小,建议要设置足够大
    repl-backlog-size 1mb
    #Redis同时也提供了当没有slave需要同步的时候,多久可以释放环形队列:
    repl-backlog-ttl 3600
    

    避免全量复制

    第一次全量复制不可避免,后续的全量复制可以利用小主节点(内存小),业务低峰时进行全量
    节点运行ID不匹配:主节点重启会导致RUNID变化,可能会触发全量复制,可以利用故障转移,例如哨
    兵或集群,而从节点重启动,不会导致全量复制
    复制积压缓冲区不足: 当主节点生成的新数据大于缓冲区大小,从节点恢复和主节点连接后,会导致全量复制.解决方法将repl-backlog-size 调大
    

    避免复制风暴

    单主节点复制风暴
    当主节点重启,多从节点复制
    解决方法:更换复制拓扑
    
    单机器多实例复制风暴
    机器宕机后,大量全量复制
    解决方法:主节点分散多机器
    

    主从同步优化配置

    Redis在2.8版本之前没有提供增量部分复制的功能,当网络闪断或者slave Redis重启之后会导致主从之间的全量同步,即从2.8版本开始增加了部分复制的功能。

    性能相关配置
    repl-diskless-sync no # 是否使用无盘同步RDB文件,默认为no,no为不使用无盘,需要将RDB文件保
    存到磁盘后再发送给slave,yes为支持无盘,支持无盘就是RDB文件不需要保存至本地磁盘,而且直接通过
    socket文件发送给slave
    repl-diskless-sync-delay 5 #diskless时复制的服务器等待的延迟时间
    repl-ping-slave-period 10 #slave端向server端发送ping的时间间隔,默认为10秒
    repl-timeout 60 #设置主从ping连接超时时间,超过此值无法连接,master_link_status显示为down,
    并记录错误日志
    repl-disable-tcp-nodelay no #是否启用TCP_NODELAY,如设置成yes,则redis会合并小的TCP包从
    而节省带宽, 但会增加同步延迟(40ms),造成master与slave数据不一致,假如设置成no,则redis
    master会立即发送同步数据,没有延迟,yes关注网络性能,no关注redis服务中的数据一致性
    repl-backlog-size 1mb #master的写入数据缓冲区,用于记录自上一次同步后到下一次同步过程中间的
    写入命令,计算公式:repl-backlog-size = 允许从节点最大中断时长 * 主实例offset每秒写入量,比
    如master每秒最大写入64mb,最大允许60秒,那么就要设置为64mb*60秒=3840MB(3.8G),建议此值是设
    置的足够大
    repl-backlog-ttl 3600 #如果一段时间后没有slave连接到master,则backlog size的内存将会被释
    放。如果值为0则 表示永远不释放这部份内存。
    slave-priority 100 #slave端的优先级设置,值是一个整数,数字越小表示优先级越高。当master故障
    时将会按照优先级来选择slave端进行恢复,如果值设置为0,则表示该slave永远不会被选择。
    min-replicas-to-write 1 #设置一个master的可用slave不能少于多少个,否则master无法执行写
    min-slaves-max-lag 20 #设置至少有上面数量的slave延迟时间都大于多少秒时,master不接收写操作(拒绝写入)
    

    常见主从复制故障汇总

    master密码不对

    即配置的master密码不对,导致验证不通过而无法建立主从同步关系。

    [root@centos8 ~]# tail -f /var/log/redis/redis.log
    24930:S 20 Feb 2021 13:53:57.029 * Connecting to MASTER 172.31.0.8:6379
    24930:S 20 Feb 2021 13:53:57.030 * MASTER <-> REPLICA sync started
    24930:S 20 Feb 2021 13:53:57.030 * Non blocking connect for SYNC fired the
    event.
    24930:S 20 Feb 2021 13:53:57.030 * Master replied to PING, replication can
    continue...
    24930:S 20 Feb 2021 13:53:57.031 # Unable to AUTH to MASTER: -ERR invalid password
    
    Redis版本不一致

    不同的redis 大版本之间存在兼容性问题,比如:3和4,4和5之间,因此各master和slave之间必须保持版本一致

    无法远程连接

    在开启了安全模式情况下,没有设置bind地址或者密码

    [root@centos8 ~]# vim /etc/redis.conf
    #bind 127.0.0.1 #将此行注释
    
    [root@centos8 ~]# systemctl restart redis
    [root@centos8 ~]# ss -ntl
    
    #可以本机登录
    [root@centos8 ~]# redis-cli
    127.0.0.1:6379> KEYS *
    (empty list or set)
    
    配置不一致

    主从节点的maxmemory不一致,主节点内存大于从节点内存,主从复制可能丢失数据
    rename-command 命令不一致,如在主节点定义了fushall,flushdb,从节点没定义,结果执行flushdb,不同步

    #master有一个rename-command flushdb "king",而slave没有这个配置,则同步时从节点可以看到
    以下同步错误
    3181:S 21 Oct 2021 17:34:50.581 # == CRITICAL == This replica is sending an
    error to its master: 'unknown command `king`, with args beginning with: ' after processing the command '<unknown>'
    
  • 相关阅读:
    父级display:none获取子元素宽的问题
    获取url参数
    借一例固定菜单栏!!!
    笨蛋!!!
    调用腾讯地图api,在手机端获取用户地理位置。
    遇到的小tip
    复制事件
    跑马灯!!!!的汉子你威武雄壮!!!
    web页面有哪三层构成?分别是什么?
    CentOS 8 手动部署LNMP环境
  • 原文地址:https://www.cnblogs.com/xuanlv-0413/p/15085209.html
Copyright © 2020-2023  润新知