Redis:数据同步之主从复制

【关于作者】

关于作者,目前在蚂蚁金服搬砖任职,在支付宝营销投放领域工作了多年,目前在专注于内存数据库相关的应用学习,如果你有任何技术交流或大厂内推及面试咨询,都可以从我的个人博客(https://0522-isniceday.top/)联系我

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rgcqmf7R-1680957055016)(https://zhangyuxiangplus.oss-cn-hangzhou.aliyuncs.com/boke/主从复制.png)]

1.Redis可靠性:

体现:

(1)数据尽量少丢失 – RDB、AOF

(2)服务尽量少中断 – 增加副本冗余,将一份数据同时保存在多个服务器上

2.主从模式

  • 读操作:主库、从库都可以接收;

  • 写操作:首先到主库执行,然后,主库将写操作同步给从库。(为了保证数据的一致性)

    img

2.1.主从库同步

第一次同步流程:

  • 第一阶段:确认连接,主库和从库建立连接,主库发送确认信号给从库后,主从就可以开始同步了

    从库发送psync命令,命令包括主库的runID和复制进度offset参数

    runID:每个redis实例启动时产生的唯一id

    offset:-1,代表第一次复制

    主库接收到psync命令后,会用FULLRESYNC响应命令带上主库的runID和主库目前复制进度返回给从库,从库会记下这些值(FULLRESYNC响应代表第一次复制采取的是全量复制)

  • 第二阶段:主库将数据同步给从库,从库将数据全量加载到内存,该过程依赖RDB快照

    详细来说,主库fork()出bgsave子进程生成RDB快照,将快照发送给从库后,从库会先清空数据(为了避免之前加载了其他数据),再本地加载数据,再主库同步的这个过程中不会产生堵塞,主库仍然接受读写请求,再同步RDB快照的这个过程中,会将生产快照的时刻之后的读写请求写入replication buffer,为了保证主从一致性

  • 第三阶段:把第二阶段执行过程中新接受到的写命令,再发送给从库。

img

主从级联

由于生成RDB快照(需要fork出bgsave进程)和传输RDB快照当从节点过多时,其会堵塞主线程影响服务,因此就有了主从级联的模式为主节点分担压力

就是说我们部署主从集群的时候,可以手动选择一个从库级联其他从库。然后我们可以再其他从库执行如如下操作去建立主从关系

replicaof 所选从库的IP 6379

如下图:

img

2.2.基于长连接的命令传播

一旦主从库完成了全量复制,它们之间就会一直维护一个网络连接,主库会通过这个连接将后续陆续收到的命令操作再同步给从库,这个过程也称为基于长连接的命令传播,可以避免频繁建立连接的开销

2.3.主从网络中断该如何处理

Redis 2.8 之前会再次进行全量复制

Redis 2.8 之后,增量复制

增量复制

主从库断连后,主库会将写请求写入replication buffer和repl_backlog_buffer,

其中repl_backlog_buffer包含两个指针,master_repl_offset用于记录主库写到的位置,slave_repl_offset用于记录读库读到写命令的位置,正常情况下两者相等。

img

当从库断连又重新连接后,从库会发送psync命令给主库,并包括从库当前的slave_repl_offset的值发送给主库,主库比较master_repl_offset和slave_repl_offset的差值,这时只需将差距中间的命令同步给从库即可

20e233bd30c3dacb0221yy0c77780b16

需要注意

由于repl buffer是环形缓冲区,如果从库执行命令比较慢,可能会导致还未执行的命名被主库的写给覆盖了,因此我们需要调节repl_backlog_size参数去避免这个问题

repl_backlog_size = 主库写入命令速度 * 操作大小 - 主从库间网络传输命令速度 * 操作大小

实际应用会将该值的大小*2,即repl_backlog_size = 缓冲空间大小 * 2,如果还是覆盖的话还是会导致主从数据不一致,这个时候可以考虑切片集群来分担单个主库的压力

举个例子,如果主库每秒写入2000个操作,每个操作的大小为2KB,网络每秒能传输1000个操作,那么,有1000个操作需要缓冲起来,这就至少需要2MB的缓冲空间。否则,新写的命令就会覆盖掉旧操作了。为了应对可能的突发压力,我们最终把repl_backlog_size设为4MB。

主从复制为什么不采用AOF?

(1)AOF文件比RDB文件更大

(2)初始化数据时,RDB文件执行的更快

3.主从同步中的坑

3.1.主从数据不一致

概念:客户端从从库读取到的值和主库中读取到的值不一致

产生原图:

  • 原因一:主从库之间的网络存在延迟,从库无法及时收到主库的命令
  • 原因二:从库当前正被自身耗时的操作阻塞,例如查询bigkey、范围查询等。同步命令阻塞在队列中,无法马上执行
  • 原因三:主从库设置的 maxmemory 不同,如果 slave 比 master 小,那么 slave 内存就会优先达到 maxmemroy,然后开始淘汰数据,此时主从库也会产生不一致

如何解决:

  • 方法一:保证主从库间的网络连接状况良好,避免将主从部署在不同的机房
  • 方法二:Redis的INFO指令能够监控主从的接收命令的进度信息(master_repl_offset)和从库复制的进度信息(slave_repl_offset),因此我们可以监控主从之间的两者进度差值,当差值达到阈值就将从库从客户端的连接中移除

3.2.读取过期数据

Redis有两种策略删除过期数据,分别是惰性删除和定期删除(注意与淘汰策略区分开来,淘汰策略指的是redis内存不足,需要清理数据,而过期数据的删除策略又是另一回事了)

  • 惰性删除:

    数据过期后,并不会立马删除该key,而是等客户端来请求,检查到过期了就把该key删除,这样的好处是减少删除操作对CPU资源的使用,对于用不到的数据不用时刻去检查然后删除

  • 定期删除:

    Redis每隔一段时间(默认100ms),就会随机选出一定数量的数据,检查它们是否过期,并把其中过期的数据删除

读取过期数据的发生场景:

  • 场景一:数据过期,请求主节点时会删除数据,但是在redis 3.2版本之前,读取从数据库哪怕数据过期也会返回过期数据

    解决措施:升级redis版本。3.2版本之后,数据过期就会返回空值给客户端,但是不会删除。

  • 场景二:过期时间设置在主从节点存在延迟,不过只存在于EXPIRE和PEXPIRE命令

    设置过期时间的操作一共有四种,如下图,

    img

    对于EXPIREPEXPIRE,其超时时间的计算是指执行命令的当前时间后推ttl秒过期,所以命令通过给从节点时,主从之间的过期时间会存在延迟,所以就从从节点读到了在主节点过期的数据

    解决措施:使用EXPIREATPEXPIREAT命令,该命令时指定具体过期的时间戳,但是得主从机器的时钟得保持一致

3.3.不合理配置项导致的服务挂掉

  • protected-mode配置项:限定哨兵实例能否被其他服务器访问,配置项设置为yes时,哨兵实例只能在部署的服务器本地进行访问。当设置为no时,其他服务器也可以访问这个哨兵实例

    所以当该配置设置为yes时,其他哨兵无法访问该哨兵,就无法监控、选主等。

    正常的设置如下:

    protected-mode no 
    //bind 其他哨兵实例的ip,这样哨兵之间才可以通信
    bind 192.168.10.3 192.168.10.4 192.168.10.5
    
  • cluster-node-timeout配置项:设置了Redis Cluster中实例响应心跳的超时时间

    如果主节点发送故障,在执行主从切换的过程中,由于网络延迟和切换操作执行的影响,切换时间往往比较长,可能会导致实例的心跳超时(超出配置项)。实例超时后又被判定为异常。

    如果执行主从切换的实例超过半数,而主从切换时间又长,可能会有半数节点超时,导致整个集群挂掉,建议配置10~20s

关于slave-read-only配置项:配置从节点是否可读

再解释一下 slave-read-only 的作用,它主要用来控制 slave 是否可写,但是否主动删除过期 key,根据 Redis 版本不同,执行逻辑也不同。

1、如果版本低于 Redis 4.0,slave-read-only 设置为 no,此时 slave 允许写入数据,但如果 key 设置了过期时间,那么这个 key 过期后,虽然在 slave 上查询不到了,但并不会在内存中删除,这些过期 key 会一直占着 Redis 内存无法释放。

2、Redis 4.0 版本解决了上述问题,在 slave 写入带过期时间的 key,slave 会记下这些 key,并且在后台定时检测这些 key 是否已过期,过期后从内存中删除。

但是请注意,这 2 种情况,slave 都不会主动删除由 master 同步过来带有过期时间的 key。也就是 master 带有过期时间的 key,什么时候删除由 master 自己维护,slave 不会介入。如果 slave 设置了 slave-read-only = no,而且是 4.0+ 版本,slave 也只维护直接向自己写入 的带有过期的 key,过期时只删除这些 key。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哈哈哈张大侠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值