https://futures.yunsonbai.top/?hmsr=yunsonbai.top
贵金属行情

redis主从同步细节和坑

https://futures.yunsonbai.top/?hmsr=yunsonbai.top

引言

为了保障线上高可用,Redis免不了使用主从的方式部署,结合自身使用和阅读所得只是整理了一下Redis主从复制的知识点。

主从配置

Redis服务器起来之后,都会认为自己是主服务器,比如我们启动了两台Redis服务器,ip分别为xx.xx.xx.1:6379(后边简称1号机)和xx.xx.xx.2:6379(后边简称2号机),然后在在2号机上执行命令:slaveof xx.xx.xx.1 6379
那么2号机将成为1号记得从服务器,1号机成为2号机的主服务器。
当然也可给1号机多配置几个从服务器,比如加一个3号机,也就是一主多从。
另外,还可以给2号机配置一个从服务器,比如4号机(输入命令 slaveof xx.xx.xx.2 6379),那此时1号机既是1号机的从服务器,也变成了4号机的主服务器。结构如可以看下图:

知识点

  • 全部同步: 直接让主服务器创建并发送RDB文件,以及把保存在缓冲区里面的写命令发送给从服务器来进行同步
  • 部分同步:在一定条件(后边重点记录)下,主服务器把从服务器断开期间的写命令发送给从服务器。

从服务器请求同步是携带的数据

  • 主服务器的运行ID,第一次同步则是个空值
  • 从服务器的offset(从服务器写命令累计偏移量),第一次的时候为-1

部分依赖的组成部分

  • 主和从服务器各自记录的偏移量offset

    • 主服务器每次接收写操作时,本地的offset就会增加
    • 从服务器每次同步完会增加offset,也就是保持和主的offset一致
  • 主服务的复制积压缓冲区

  • 从服务器运行的ID

关于主从如何建立好连接大致分为从服务器设置主服务器的地址和端口,然后建立套接字链接,发送ping,验证身份,从向主发送端口信息,同步(当然第一次同步时,肯定是全部同步)

所谓的一定条件

当从服务器断开连接,再次与主服务器链接上后,从服务器先将本地记录的主服务器运行ID(后边记为记录主ID)发给建立上连接的主服务器

  • 主服务器运行ID(主ID)和从服务器发送过来的记录主ID一致,则尝试部分同步

    如果从服务器发过来的offset和主服务器自己记录的offset不一致,尝试寻找复制积压缓冲区是否完全包含差量数据,包含进行部分同步,不包含则进行全量同步。

    部分同步时,主服务器将复制积压缓冲区从服务器传来的offset之后的所有数据都发给从服务器。

  • 主ID和记录主ID不一致,则直接进行全量同步,并把主ID和主服务器的偏移量给从服务器,从服务器更新本地记录主ID

几个注意点

复制缓冲区和积压缓冲区

复制缓冲区将新的更新保存到主服务器,直到主服务器的 RDB 快照传输到从服务器并加载到其内存中。 在此之后,复制缓冲区中的数据被传输到从站。 Client-output-buffer-limit 是我们允许复制缓冲区增长的最大大小。

主服务器,会把每条写命令,都按顺序存入积压缓冲区,积压缓冲区在从站短暂断开连接后的数据。 所以当slave重新连接的时候,我们就不需要再做全同步了。需要注意的是它是一个环形区域,所谓环形区域就是说,当他写满后,会覆盖前边的内容接着写新的内容。

全同步时大致过程

主进程fork出一个子进程开始进行RDB快照,同时为当前从服务器开辟一块复制缓冲区,用来存储从开始RDB快照,到从服务器完全加载完RDB文件并请求后续数据同步时间段内的所有写入命令。

当从服务器加载完住服务器传过来的RDB文件并告知主服务器后,嘱咐其会把为该从服务器开辟的辅助缓冲区内的数据,发送给从服务器。这样就完成了一次全同步。

  • 如果发送复制缓冲区命令的时候又有新的写命令,怎么办?

    写入命令都在复制积压缓存区保存,完成全同步后,即可尝试进行部分同步,如果不能进行部分同步,则再一次全同步

  • 如果收到多个从服务器的同步,怎么办
    主服务器会收集起来所有需要同步的从服务器,只会启动一个RDB快照去给多个从服务提供服务。

部分同步的大致过程

当从服务器和主服务短暂断开,再次建立连接后。从服务器会将主服务器runID和本机的offset传给主服务器,主服务对比runID一致,开始比对offset

  • 断开期间,没有新的写入命令: 此时从服务器传过来的offset和主服务的offset是一致的,主从达到数据一致

  • 断开期间有新的写入命令,参考下边的表格,表格的时间列,从上往下逐渐变大

时间
t0 主从全同步完毕 主从全同步完毕
t1 set name yunson 执行部分同步得到的 set name yunson
t10 set name xiaoming 执行部分同步得到的 set name xiaoming
t11 主从断开 主从断开
t12 set name xiaofang 主从断开
t13 set name xiaopeng 主从断开
t14 主从重新连接 主从重新连接
t15 向主发送psync并携带funID和本机offset信息
t16 set name xiaohong
t17 处理从的部分同步请求,发现offset与从的offset不一致,且在积压缓冲区找到了差集命令,向从返回continue,表示可以部分同步
t18 收到continue,开始部分同步
t19 向从服务器发送,set name xiaofang,set name xiaopeng,set name xiaopeng三个命令 收到指令,并执行
t20 主从部分同步完毕 主从部分同步完毕

可能的坑点

复制缓冲区

这个之前遇到过,为了节省内存把复制缓冲区设置小了,改成了64M,直接导致悲催的结果。

初出茅庐,把redis当成了数据库使用,存储了很多数据,大概有几个G,然后还设置了从服务,以为这将很稳定,但是一个bug,直接导致了系统几乎不能用了。 bug是导致服务超高并发的写数据,而且还用了大value,导致在主从在完成RDB文件同步前,就把复制缓冲区写满了,直接就导致全同步失败,使得主从不停的进行全同步,前边的文章也聊了一直RDB持久话,也是相当耗redis性能的,最终导致我们的服务崩溃。

总结一下:

  • redis始终是缓存服务器,如果你的redis存储的数据动不动就上G,需要你好好研究研究是不是设计的架构有问题。内容越少,全同步越快。
  • 复制缓冲区还是别轻易设置小了,尽量保持在256M或者512M,不过最终要考量,主从同步时间*每秒写入指令的大小
  • 复制缓冲区也不要设置的太大,如果先后来多个从服务器过来同步,可能占完内存。

复制积压缓冲区

这个也不能太小,如果太小的话,主从断开时间段内,该缓冲区已经被覆盖写(环形内存导致),也就是丢了主从差集命令,那只能接着进行全同步。

所以这个的大小要考虑为 主从断开时间 * 每秒写命令的大小

总之尽量不要把redis当做存储用,它就是缓存,redis存储的数据越少,同步就越快。

yunsonbai wechat
公众号:技术and生活