学习完了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。
以上就是所有主从复制机制的内容,主要学习了主从复制架构和主从复制工作流程的三个阶段,以及心跳机制和常见问题。






















