之前章节我们都是操作单机的redis,这一节开始将开始关注redis集群相关的内容,这一节主要讲述集群的基础——主从复制。
一、主从复制简介
1.1 单机可能出现的问题
单机的redis不可避免地可能会出现一些问题,比方说:
- 机器故障、服务器宕机,数据丢失,很可能对业务造成灾难性的打击;
- 容量瓶颈,内存不足,不可能无限升级内存,硬件跟不上;
解决:为了避免单点故障,可以准备多台redis互连,互相连通,将数据复制多个副本保存在不同的服务器上,并保证数据是同步的,即使有一台服务器宕机,其他服务器依然可以继续提供服务,保证高可用,同时实现数据的冗余备份。
1.2 多台服务器的连接方案
我们找多台计算机连接起来,专门用一台计算机负责负责收集数据(master服务器),其他计算机负责读数据(slave服务器);slave的数据从master上来,这样就保证了高可用;
这个过程需要解决的问题:数据同步问题,而核心工作就是master端的数据能够复制到slave中去。这就是我们说的主从复制。
主从复制就是将master中的数据即时、有效的复制到slave中去。
特征:一个master可以有多个slave,但是一个slave只能有一个master。
职责:
- master:负责写数据,执行写操作,将出现变化的数据同步到slave;
- slave:负责读数据,会配置禁止写数据,原因是如果多个slave都在写,那么很难有效保证多个slave之间的数据一致性。
那可能说,为什么不多搞几个master?是的,是可以的,相当于master本身也是一个集群,之后我们在哨兵的章节详细讨论。
1.3 主从复制的作用:
- 实现读写分离,master写,slave读,提高服务器的读写负载能力
- 实现负载均衡,基于主从结构,配合读写分离,由slave分担master的负载,并根据需求变化,改变slave的数量,大大提高redis的并发量和数据吞吐量;
- 故障恢复快,当master出现问题,可以由slave提供服务;
- 数据冗余,实现了数据的热备份
- 实现redis的高可用:基于主从复制,构建哨兵模式与集群,实现高可用
二、主从复制的原理
主从复制分为三个阶段:
- 建立连接阶段:salve向master发起连接
- 数据同步阶段:master向slave同步数据
- 命令传播阶段:后期master收到的数据,也要同步给slave,这是一个反复的过程;
2.1 建立连接阶段
2.1.1 建立slave到master的连接,使master能够识别slave,并保存slave的端口号
- slave 发送指令:salveof ip port ,这个指令就是将一台服务器变成另一台服务器的从;master接受到指令后做出回应
- slave保存master的IP和端口信息,根据保存的信息创建连接master的socket
- salve周期性的向master发送ping指令(定时),保证连接是一直存在的
- 如果master配置了身份验证,slave需要完成相应鉴权
- 最后,slave发送指令告诉master通过端口完成对slave的监听,master接收后保存salve的端口号;
总的来说:salve保存master的地址和端口,master保存slave的端口,他们之间创建socket连接;
2.1.2 具体操作简介
主从连接方案:(slave连接master)
连接方案 | 说明 |
客户端发送指令 | slaveof masterip masterport |
启动服务器参数 | redis-server -slaveof masterip masterport |
服务器配置(推荐) | slaveof masterip masterport |
2.1.3 授权访问
master:
- master配置文件设置密码:requirepass <password>
- master客户端发送命令设置密码:config set requirepass <password> 获取使用 config get requirepass
slave:
- salve客户端发送命令设置密码:auth <password>
- salve配置文件设置密码:masterauth <password>
- 启动客户端设置密码:redis-cli -a <password>
2.2 数据同步阶段
2.2.1 工作流程:
- 在slave初次连接master后,复制master中的所有数据到slave
- 将slave的数据库状态更新成master当前的数据库状态
2.2.2 详细说明:
- slave向master发送指令:psync2;slave请求同步数据
- master执行bgsave,这个时候master可能还会持续接受数据,为了防止数据无法给到slave,master创建一个复制缓冲区来接受指令;master则可以放心做RDB的过程,生成RDB文件,通过socket发送给slave
- slave接收RDB,清空文件,执行RDB文件恢复过程;这个过程是一个全量复制的过程
- slave发送指令告诉master RDB已经结束,请求master是否还有没有同步的数据;
- master把复制缓冲区信息,slave接收信息,指令重写(bgrewriteof),恢复数据,这是一个部分复制或者增量复制的过程
至此,数据同步工作完成。slave具有master端全部数据,包括RDB接收到的全部数据;master保存salve当前同步数据的位置;两者之间完成数据克隆。
2.2.3 数据同步阶段master说明
- 如果master数据量巨大,数据同步阶段应该避免高峰期,避免造成master的阻塞,影响业务正常进行;
- 复制缓冲区大小如果设置不合理,会导致数据溢出,如果进行全量复制周期太长,进行部分数据时发现数据已经丢失,部分复制不能正确进行,必须进行二次全量复制,从而陷入死循环;更改指令:repl-backing-size 1mb
- master单机内存占用主机内存不宜过大,建议使用50%到70%的内存,留下部分内存执行bgsave指令和创建复制缓冲区;
2.2.4 数据同步阶段slave说明
- 为了避免slave进行全量复制的时候、部分复制的时候服务器响应阻塞或者数据不同步,建议关闭此期间的对外服务;通过指令:slave-serve-stale-data no
- 多个slave同时对master请求数据同步,master发送的RDB文件较多,会对带宽进行冲击,如果master带宽不足,会造成master性能下降,需要适量错峰;
- slave过多的时候也可以考虑树型结构,但是注意层级不能过多,否则一致性会变差,而且分层的同时,如果要求一致性高的,读取顶层;如果相对要求不高的,可以读取下一层级,当谨慎选择;
2.3 命令传播阶段
- 当master数据库状态被修改后,导致主从服务器数据状态不一致,此时需要让主从数据同步到一致的状态,同步的动作叫命令传播
- master将接收到的数据变更指令发送给slave,slave接收到指令之后执行命令
2.3.1 命令传播阶段的部分复制
在命令传播阶段,可能会出现短时间断网等抖动,这个时候可能就会有部分复制的过程;
部分复制的三个核心要素:
- 服务器的运行ID(runid)
- 主服务器的复制积压缓冲区
- 主服务器的复制偏移量
2.3.1.1 服务器的运行ID
概念:服务器的运行ID是每台服务器每次运行的身份识别码,一台服务器多次运行可以生成多个运行ID
组成:运行ID由40位字符组成,是一个随机的十六进制字符
作用:在服务器间传输,识别身份;
实现:run id在服务器启动时自动生成,master在首次连接slave时,会将自己的运行ID发给slave,slave进行保存,可以通过info server指令进行查看;
2.3.1.2 复制缓冲区
概念:是一个先进先出的队列,用于存储服务器执行过的命令,每次传播命令,master都会把传播的命令记录下来,并存储在复制缓冲区
内部工作原理:
当master接收到一直指令后,会把这个指令放在复制积压缓冲区,当然,这个命令不是直接放进去,而且进行了相应的转换,按照字符来记录,而缓冲区亦做了相应的分割。
整个复制缓冲区由两个部分组成,一个是偏移量offset(slave通过offset来记录复制到哪里了,这个值slave和master都会记录),一个是字节值。
作用:用来保存master收到的所有指令,仅仅是影响数据变更的指令,例如set等;
产生:每个服务器启动的时候,如果开启有AOF或者被连接成为master节点,创建复制缓冲区;
数据来源:当master接收到客户端的指令时,除了将该指令执行,会将该指令保存到缓冲区中;
2.3.1.3 主从复制偏移量offset
概念:描述复制缓冲区中指令字节的位置
分类:master复制偏移量(可能有多个),slave复制偏移量(一个)
数据来源:master端为记录一次发送一次;slave端为接收一次记录一次;
作用:同步信息,比对master和slave的差异,当slave短线后,恢复数据使用;
2.4 再述数据同步加命令传播阶段的工作流程
这个部分我们再详细说一下这个过程:
- 当服务器启动的时候,首先master首选得产生自己的复制缓冲区,接下来,slave向master发送指令,第一次,slave并不知道master的runid和偏移量,所以发送psync2 ? -1 ,相当于告诉master有啥数据就全部发送过来;
- master接收到指令之后,执行bgsave生成RDB文件,记录当前的复制偏移量offset;由于发现slave并不知道自己的runid和偏移量,发送的时候(fullersync)会携带自己的runid和offset,通过socket一起发送给slave
- slave收到后,这个时候就会保存master的runid和offset信息,根据master给的RDB恢复数据,整个全量复制过程
- 而到部分复制的过程,slave发送指令psync2 runid offset ,这个时候slave是知道master的runid和offset的;master收到同步指令后,会判断runid是否匹配,判断offset是否在缓冲区,如果有一个不匹配,则执行全量复制的过程;如果OK,则部分复制,并发送continue,意为继续复制;
2.5 心跳机制
在主从复制过程中,复制数据是一个反复的过程,因为数据可能会一直在变化,master会按照一定的间隙给每一个slave同步数据,这其中就需要心跳机制来保证反复执行;
心跳机制就是进入命令传播阶段,master和slave之间需要进行信息交换,用心跳机制来进行维护,实现双方连接在线;
master的心跳:
- 指令:PING
- 周期:由repl-ping-slave-period决定,默认10秒
- 作用:判断slave是否在线
- 查询:通过info replication,获取slave最后一次连接的时间间隔,lag项维持在0或者1认为正常
slave的心跳:
- 指令:REPLCONF ACK offset
- 周期:1秒
- 作用:汇报slave自己的复制偏移量,获取最新的数据变更指令;判断master是否在线
注意事项:
- 当slave多数掉线时,或者延迟较高,master为了保证数据的稳定性,将拒绝所有的连接操作
实现:配置 min-slaves-to-write 2 min-slaves-max-lag 8
slave数量少于两个,或者所有slave的延迟大于等于8秒时,强制关闭master的写功能,停止数据同步
slave的数量和延迟就是通过slave向master发送relpcation ack指令做确认来告诉
三、常见问题
3.1. 频繁的全量复制
举例1:如果master重启,runid发生变化,导致所有的slave全量复制?
解决:内部优化方案:
- master内部创建master_replid变量,使用runid相同策略生成,长度41位,并发送给所有的slave
- 在master关闭时执行指令shutdown save,进行RDB持久化,将runid和offset保存在RDB文件中;
- master重启后,加载RDB文件,恢复数据;将保存在RDB文件中的repl-id和repl-offset加载到内存中,可以通过info信息查看
作用:本机保存上次的runid,重启后恢复该值,使所有的slave认为还是之前的master
举例2:网络环境不好,出现网络中断,slave没法正常提供服务
原因:复制缓冲区小,断网后,slave的offset越界,触发全量复制
解决:修改缓冲区大小,通过repl-backlog-size
建议设置:2 * (master到slave重连平均时长) * (master平均每秒产生写命令数据的总量)
3.2 master CPU 占用过高
现象:master CPU占用过高
原因:
- slave每一秒发送 REPLCONF ACK命令到master请求数据
- slave接收到慢查询时,如keys * , hgetall 等,占用大量的CPU性能
- master每秒轮巡调用复制定时函数replicationCron(),比对slave发现长时间没有响应(slave那边遭受慢查询之类的场景)
结果:master各种资源(输出缓冲区、带宽等)被严重占用
解决:设置合理的超时时间,确认是否释放slave,断开slave
设置:repl-timeout 默认60秒,超过该值,释放slave
3.3 slave频繁断开连接
现象:slave 与master断开连接
原因:
- master发送ping指令频率过低
- master设置超时时间较短
- ping指令在网络中存在丢包
解决:提高ping指令的发送频度,通过设置参数repl-ping-slave-period
一般来说,超时时间repl-time是ping指令频度的5到10倍比较好
3.4 数据不一致
现象:多个slave获取相同数据不同步
原因:网络信息不同步,数据发送有延迟
解决:
- 优化主从网络环境,通常可以同机房部署
- 监控主从节点延迟(通过offset判断),如果slave延迟较大,暂时屏蔽程序对该slave的数据访问,注意只能是暂时性策略,参数设置:slave-serve-stale-data yes/no,参数开启后,仅仅只会响应info、slaveof等少数指令,基本对外不能提供服务了,这种设置除非对数据一致性有特别高的要求;
这一篇就到到这里,下一篇:redis的哨兵模式。