【Redis核心知识 六】Redis集群之主从复制模式

简介: 【Redis核心知识 六】Redis集群之主从复制模式

学习完了Redis的linux安装、Redis的事务机制、Redis的持久化策略、Redis的删除策略和Redis的高级数据模型或结构之后呢,高级部分就暂告一段落,今天正式进入Redis集群的学习中,Redis集群这里先学习下先导内容,Redis的主从复制机制。

主从复制架构

在之前的分布式文章中一再提到过分布式系统的三高架构:高并发、高性能和高可用Kafka和ElasticSearch的集群中都提供了副本或分片来提高服务器的安全性和稳定性,Redis也不例外,所以在聊主从复制之前先了解下高可用的真正定义:

理论高可用性为5个9,实际上达到4个9就很不错了。单机的Redis不靠谱

  • 机器故障:即使有RDB和AOF的持久化机制,也会有数据丢失的可能,甚至如果机器硬盘故障,持久化机制也会有丢数据的可能
  • 内存瓶颈,Redis是运行在内存中的数据库,内存不足的时候会导致容量瓶颈。

所以我们需要搭建Redis集群,就是通过添加服务器的数量,提供相同的服务,从而让服务器达到一个稳定、高效的状态,将数据复制多个副本存在不同的服务器上,并且数据需要保持同步,即使其中一台服务器宕机,其它服务器依然能够继续提供服务,实现Redis的高可用,顺便对数据进行冗余备份。本质上和其它分布式中间件的使命是一样的。

简单架构模式

在主从复制中,数据库分为两类:主数据库(master)和从数据库(slave),整体的服务架构如下图所示:

主库和从库之间的使命和注意事项如下:

  • 主库职责:主数据库可以进行写操作,当写操作导致数据变化时会自动将数据同步给从数据库,可以读,一般不用于读。
  • 从库职责:从数据库一般都是只读的,并且接收主数据库同步过来的数据
  • 宕机操作:一个master可以拥有多个slave,但是一个slave只能对应一个master。slave挂了不影响其他slave的读和master的读和写,重新启动后会将数据从master同步过来master挂了以后,不影响slave的读,会在slave节点中重新选一个master提供写的服务

以上就是主从复制的基本概念。

复合架构模式

除了简单的两层架构,还可以使用多层次的拓扑架构,也就是一个机器既可以是slave也可以是master

除了这种模式,还可以在此基础上存在多主复合架构

主从复制的作用

基于以上我们对主从复制的理解,可以总结出一些主从复制的优点,再啰嗦两句,和kafka及ES的集群架构本质相同,都是为了三高设计的:

  • 读写分离:master写,slave读,提高服务器读写负载能力
  • 负载均衡:基于主从结构,配合读写分离,由slave来分担master的负载,并根据需求的变化,弹性扩容slave的数量,通过多节点分担数据读取负载,大大提高Redis服务器并发量与数据吞吐量。
  • 故障恢复:当master出现问题时,由slave提供服务,实现快速的故障恢复
  • 数据冗余:实现数据热备份,是持久化之外的一种数据冗余方式
  • 高可用基石:基于主从复制,构建哨兵模式与集群,实现Redis的高可用方案

了解完基础的主从复制架构概念后,来学习下它的工作流程。

主从复制工作流程

整体分为三个阶段:建立连接阶段【slave发起】、数据同步阶段【master发起】、命令传播阶段【master和slave互发指令】。需要注意的是为了方便直接在一台机器上起了两个实例,真实的场景,主从不能放到一个服务器上,要不然可不就要挂一起挂了么。

接下来就一个阶段一个阶段的进行分析。

建立连接阶段

建立slave到master的连接,使master能够识别slave,并保存slave的端口号。建立连接阶段主要有以下几个步骤:

接下来我们进行以下实操:

数据连接实操

首先关掉6379和6380的守护进程和日志,改为前端启动并且设置绑定主机为127.0.0.1,这样我们就可以在前端看到日志变化了

分别启动作为主的6379服务器和作为从的6380服务器

用6380连接6379,让6380作为6379的slave

在slve客户端上进行连接:

[root@192 redis-6.0.8]# redis-cli -p 6380
127.0.0.1:6380> slaveof 127.0.0.1 6379
OK
127.0.0.1:6380>

可以看到在6379master服务器上有如下日志打出,说明连接成功

同时从slave上也能看到master的信息:

59431:M 31 Oct 2020 17:39:38.060 * Ready to accept connections
59431:S 31 Oct 2020 17:55:15.652 * Before turning into a replica, using my own master parameters to synthesize a cached master: I may be able to synchronize with the new master with just a partial transfer.
59431:S 31 Oct 2020 17:55:15.652 * REPLICAOF 127.0.0.1:6379 enabled (user request from 'id=4 addr=127.0.0.1:40682 fd=8 name= age=19 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=42 qbuf-free=32726 obl=0 oll=0 omem=0 events=r cmd=slaveof user=default')
59431:S 31 Oct 2020 17:55:15.655 * Connecting to MASTER 127.0.0.1:6379
59431:S 31 Oct 2020 17:55:15.655 * MASTER <-> REPLICA sync started
59431:S 31 Oct 2020 17:55:15.655 * Non blocking connect for SYNC fired the event.
59431:S 31 Oct 2020 17:55:15.656 * Master replied to PING, replication can continue...
59431:S 31 Oct 2020 17:55:15.656 * Trying a partial resynchronization (request 67875c4eb9e6fb3f04b53a3ca91cc6b99a28b183:1).
59431:S 31 Oct 2020 17:55:15.657 * Full resync from master: c1aad1351c43a892f7bcae8ea92927e248e387a5:0
59431:S 31 Oct 2020 17:55:15.657 * Discarding previously cached master state.
59431:S 31 Oct 2020 17:55:15.668 * MASTER <-> REPLICA sync: receiving 175 bytes from master to disk
59431:S 31 Oct 2020 17:55:15.669 * MASTER <-> REPLICA sync: Flushing old data
59431:S 31 Oct 2020 17:55:15.669 * MASTER <-> REPLICA sync: Loading DB in memory
59431:S 31 Oct 2020 17:55:15.669 * Loading RDB produced by version 6.0.8
59431:S 31 Oct 2020 17:55:15.669 * RDB age 0 seconds
59431:S 31 Oct 2020 17:55:15.669 * RDB memory usage when created 1.88 Mb
59431:S 31 Oct 2020 17:55:15.669 * MASTER <-> REPLICA sync: Finished with success

我们可以简单验证下,在主master客户端set一个值:

[root@192 redis-6.0.8]# redis-cli
127.0.0.1:6379> set love tmlloveguochengyu
OK
127.0.0.1:6379>

然后从slave客户端能不能get到:

127.0.0.1:6380> get love
"tmlloveguochengyu"
127.0.0.1:6380>

可以取到,说明主库写入的数据已经同步到了从库之中。由于主从一般是内网不对外服务,所以不需要密码,简单了解下即可:

其它连接方式

当然其实还可以通过启动从库的时候直接指定主库来启动

[root@192 config]# redis-server 6380.conf  --slaveof 127.0.0.1 6379

或者最主流的做法就是通过配置文件进行连接,这样启动服务器的时候从服务器就会主动连接:

查看连接相关信息

主库6379上的状态信息如下

# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=1554,lag=0
master_replid:c1aad1351c43a892f7bcae8ea92927e248e387a5
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1554
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1554

从库6380上的状态信息如下

# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:10
master_sync_in_progress:0
slave_repl_offset:1624
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:c1aad1351c43a892f7bcae8ea92927e248e387a5
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1624
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1624

断开连接

断开链接从从库发起,命令为salveof no one.

127.0.0.1:6380> slaveof no one
OK
127.0.0.1:6380>

断开连接后,主库写入的数据不再同步到从库中去了。

数据同步阶段

数据同步阶段主要工作就是将master的数据全量的【包含历史数据快照以及同步过程中新进来的指令】同步到slave机器,让他们之间保持数据状态一致,主要流程如下:

数据同步实操

由于我们之前配置了主从的配置文件,所以启动的时候会自动执行,这里简单看下打出来的日志,master这边的日志是

62045:M 01 Nov 2020 10:22:03.054 * 2 changes in 10 seconds. Saving...
62045:M 01 Nov 2020 10:22:03.055 * Background saving started by pid 62052
62052:C 01 Nov 2020 10:22:03.056 * DB saved on disk
62052:C 01 Nov 2020 10:22:03.056 * RDB: 0 MB of memory used by copy-on-write
62045:M 01 Nov 2020 10:22:03.155 * Background saving terminated with success
62045:M 01 Nov 2020 10:22:28.059 * Replica 127.0.0.1:6380 asks for synchronization
62045:M 01 Nov 2020 10:22:28.059 * Partial resynchronization not accepted: Replication ID mismatch (Replica asked for 'c1aad1351c43a892f7bcae8ea92927e248e387a5', my replication IDs are '4385d1306f5be477c73a3d00d18e3483752890cc' and '0000000000000000000000000000000000000000')
62045:M 01 Nov 2020 10:22:28.060 * Replication backlog created, my new replication IDs are '2929fc00aa50e18d63563476bac938d8f56e6cf6' and '0000000000000000000000000000000000000000'
62045:M 01 Nov 2020 10:22:28.060 * Starting BGSAVE for SYNC with target: disk
62045:M 01 Nov 2020 10:22:28.060 * Background saving started by pid 62067
62067:C 01 Nov 2020 10:22:28.060 * DB saved on disk
62067:C 01 Nov 2020 10:22:28.061 * RDB: 0 MB of memory used by copy-on-write
62045:M 01 Nov 2020 10:22:28.134 * Background saving terminated with success
62045:M 01 Nov 2020 10:22:28.134 * Synchronization with replica 127.0.0.1:6380 succeeded

slave这边的日志是

62063:S 01 Nov 2020 10:22:28.059 * Connecting to MASTER 127.0.0.1:6379
62063:S 01 Nov 2020 10:22:28.059 * MASTER <-> REPLICA sync started
62063:S 01 Nov 2020 10:22:28.059 * Non blocking connect for SYNC fired the event.
62063:S 01 Nov 2020 10:22:28.059 * Master replied to PING, replication can continue...
62063:S 01 Nov 2020 10:22:28.059 * Trying a partial resynchronization (request c1aad1351c43a892f7bcae8ea92927e248e387a5:1).
62063:S 01 Nov 2020 10:22:28.060 * Full resync from master: 2929fc00aa50e18d63563476bac938d8f56e6cf6:0
62063:S 01 Nov 2020 10:22:28.060 * Discarding previously cached master state.
62063:S 01 Nov 2020 10:22:28.134 * MASTER <-> REPLICA sync: receiving 204 bytes from master to disk
62063:S 01 Nov 2020 10:22:28.134 * MASTER <-> REPLICA sync: Flushing old data
62063:S 01 Nov 2020 10:22:28.135 * MASTER <-> REPLICA sync: Loading DB in memory
62063:S 01 Nov 2020 10:22:28.135 * Loading RDB produced by version 6.0.8
62063:S 01 Nov 2020 10:22:28.135 * RDB age 0 seconds
62063:S 01 Nov 2020 10:22:28.135 * RDB memory usage when created 1.88 Mb
62063:S 01 Nov 2020 10:22:28.135 * MASTER <-> REPLICA sync: Finished with success

可以看出是符合我们上边看到的调度流程的。

数据同步说明

数据同步和命令传播阶段的详细实现工作流程如下,第一次slave连接时发送的是psync2 ? -1拿到runid和offset后即可进行增量的数据请求

对于Master而言需要注意以下几点数据同步时的场景需求,Master需要进行如下的选择和配置以达到数据同步的最佳状态:

  • 同步时机:如果master数量巨大,数据同步阶段应避开流量高峰期,避免造成master阻塞,影响业务正常执行
  • 复制缓冲区设置:复制缓冲区大小设置应合理,否则会导致指令溢出,这种情况下数据可能会丢失,同步到slave的也不全,丢失后必须重新进行RDB,然后指令再溢出,导致陷入死循环,大小可以依据业务场景设置,我这里设置为100m:repl-backlog-size 100mb,具体应该依据业务场景判断
  • 内存使用设置:master单机内存占用主机内存比例不应过大,建议使用50%-70%的内存,留下30%-50%内存用于执行bgsave命令创建和复制缓冲区

以上就是master部分需要设置的,当然slave也需要做一些设置和配置,Slave需要进行如下的选择和配置以达到数据同步的最佳状态:

  • 关闭对外写服务:为避免slave进行全量、部分复制时服务器阻塞,建议slave保持关闭服务状态:slave-server-stale-data no,设置后同步期间外部客户端均不能对slave进行任何操作
  • 同步时机:多个slave同时对master请求数据同步,master发送的RDB文件增加,造成带宽冲击,数据同步应错峰执行
  • 架构思考:slave过多时,调整拓扑结构为复合架构,多层级架构,这么做可以减少master压力,但需要注意顶层master与底层slave之间的数据同步可能不及时。

以上就是slave部分需要设置和注意的内容。

命令传播阶段

数据同步完成后就开始周期性的进行指令的传播,进行反复同步,实际上就是AOF不停的从master发到slave,slave重写指令后执行指令,达到数据复制的目的。需要注意,其实在数据同步阶段的时候命令也在不停的传播,这个过程中是通过心跳检测机制来进行的。

master的info信息可以查看:

# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=3122,lag=0
master_replid:2929fc00aa50e18d63563476bac938d8f56e6cf6
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:3136
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:3136

需要注意的是当slave时延过高或者在线服务较少时,停止master的写,没有意义。

主从复制流程详细版

整体的流程如下图所示,三个阶段:建立连接阶段,数据同步阶段,命令传播阶段

主从复制常见问题

主要有三个问题需要注意,频繁的全量复制问题

频繁的全量复制

第一种情况就是当master意外宕机的时候,runid和offset丢失,重启后,所有slave需要重新执行全量复制的工作:

解决方案:意外关闭时执行命令shutdown save,进行RDB持久化,记录runid和offset,重启时读取。

[root@192 data]# redis-check-rdb dump-6379.rdb  
[offset 0] Checking RDB file dump-6379.rdb
[offset 26] AUX FIELD redis-ver = '6.0.8'
[offset 40] AUX FIELD redis-bits = '64'
[offset 52] AUX FIELD ctime = '1604197348'
[offset 67] AUX FIELD used-mem = '1975344'
[offset 85] AUX FIELD repl-stream-db = '0'
[offset 135] AUX FIELD repl-id = '2929fc00aa50e18d63563476bac938d8f56e6cf6'
[offset 150] AUX FIELD repl-offset = '0'
[offset 166] AUX FIELD aof-preamble = '0'
[offset 168] Selecting DB ID 0
[offset 204] Checksum OK
[offset 204] \o/ RDB looks OK! \o/
[info] 1 keys read
[info] 0 expires
[info] 0 already expired
[root@192 data]#

第二种情况就是上文提到的缓冲区大小不够,导致反复的循环RDB和AOF

解决方案:可以依据延迟时间内数据总量再double一下得到缓冲区的大小设置。

频繁的网络中断

slave服务阻塞的时候,没有响应master的ping链接,master还在不停的轮询slave,此时会造成master的性能下降,为了解除master的执念,可以设置对slave的放手时间

这个值设置小了会造成slave丢失,设置大了会占用CPU性能。同时还有一种可能就是ping的设置,ping设置小了和设置过大都不合适,设置过小会造成slave丢失,设置过大又会占用CPU性能。

数据不同步

一定时间内的数据不同步是被允许的,但是过大也是有问题的。

首先要优化主从环境,尽量在一个机房,其次暂时不使用同步较差的slave。

以上就是所有主从复制机制的内容,主要学习了主从复制架构和主从复制工作流程的三个阶段,以及心跳机制和常见问题

相关文章
|
10月前
|
存储 负载均衡 NoSQL
【赵渝强老师】Redis Cluster分布式集群
Redis Cluster是Redis的分布式存储解决方案,通过哈希槽(slot)实现数据分片,支持水平扩展,具备高可用性和负载均衡能力,适用于大规模数据场景。
731 2
|
存储 NoSQL 数据库
Redis 逻辑数据库与集群模式详解
Redis 是高性能内存键值数据库,广泛用于缓存与实时数据处理。本文深入解析 Redis 逻辑数据库与集群模式:逻辑数据库提供16个独立存储空间,适合小规模隔离;集群模式通过分布式架构支持高并发和大数据量,但仅支持 database 0。文章对比两者特性,讲解配置与实践注意事项,并探讨持久化及性能优化策略,助你根据需求选择最佳方案。
1141 5
|
8月前
|
NoSQL 算法 Redis
【Docker】(3)学习Docker中 镜像与容器数据卷、映射关系!手把手带你安装 MySql主从同步 和 Redis三主三从集群!并且进行主从切换与扩容操作,还有分析 哈希分区 等知识点!
Union文件系统(UnionFS)是一种**分层、轻量级并且高性能的文件系统**,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem) Union 文件系统是 Docker 镜像的基础。 镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
894 6
|
9月前
|
存储 监控 NoSQL
Redis高可用架构全解析:从主从复制到集群方案
Redis高可用确保服务持续稳定,避免单点故障导致数据丢失或业务中断。通过主从复制实现数据冗余,哨兵模式支持自动故障转移,Cluster集群则提供分布式数据分片与水平扩展,三者层层递进,保障读写分离、容灾切换与大规模数据存储,构建高性能、高可靠的Redis架构体系。
|
9月前
|
存储 运维 NoSQL
Redis集群模式
Redis集群是一种分布式存储方案,旨在解决数据存储容量不足的问题。它通过将数据分片存储在多个节点上,实现数据的横向扩展。常见的分片算法包括哈希求余、一致性哈希和哈希槽分区。其中,Redis采用哈希槽分区算法,将数据均匀分配到16384个槽位中,每个分片负责一部分槽位。当节点故障时,集群通过故障检测和主从切换机制,确保服务的高可用性。集群还支持自动的数据迁移和负载均衡,保障系统稳定运行。
|
9月前
|
存储 负载均衡 NoSQL
Redis主从复制
在分布式系统中,为解决单点故障和提升性能,常采用Redis主从复制架构。通过将数据复制到多个从节点,实现读写分离、负载均衡及高可用性,同时支持多种拓扑结构以适应不同场景需求。
|
11月前
|
负载均衡 NoSQL Redis
【赵渝强老师】Redis的主从复制集群
Redis主从复制是指将一台Redis服务器的数据复制到其他Redis服务器,实现数据热备份、故障恢复、负载均衡及高可用架构的基础。主节点负责写操作,从节点同步数据并可提供读服务,提升并发处理能力。
361 5
|
10月前
|
存储 NoSQL 算法
Redis的集群架构与使用经验
本文介绍了Redis的集群架构与使用经验,包括主从复制、哨兵集群及Cluster分片集群的应用场景与实现原理。内容涵盖Redis主从同步机制、数据分片存储方式、事务支持及与Memcached的区别,并讨论了Redis内存用尽时的处理策略。适用于了解Redis高可用与性能优化方案。
|
存储 NoSQL Redis
阿里面试:Redis 为啥那么快?怎么实现的100W并发?说出了6大架构,面试官跪地: 纯内存 + 尖端结构 + 无锁架构 + EDA架构 + 异步日志 + 集群架构
阿里面试:Redis 为啥那么快?怎么实现的100W并发?说出了6大架构,面试官跪地: 纯内存 + 尖端结构 + 无锁架构 + EDA架构 + 异步日志 + 集群架构
阿里面试:Redis 为啥那么快?怎么实现的100W并发?说出了6大架构,面试官跪地: 纯内存 + 尖端结构 +  无锁架构 +  EDA架构  + 异步日志 + 集群架构
|
存储 NoSQL 算法
Redis分片集群中数据是怎么存储和读取的 ?
Redis集群采用的算法是哈希槽分区算法。Redis集群中有16384个哈希槽(槽的范围是 0 -16383,哈希槽),将不同的哈希槽分布在不同的Redis节点上面进行管理,也就是说每个Redis节点只负责一部分的哈希槽。在对数据进行操作的时候,集群会对使用CRC16算法对key进行计算并对16384取模(slot = CRC16(key)%16383),得到的结果就是 Key-Value 所放入的槽,通过这个值,去找到对应的槽所对应的Redis节点,然后直接到这个对应的节点上进行存取操作