不想弹好吉他的撸铁狗,都不是好的程序猿
尽管说单机的 Redis 性能很好,也有齐备的长久化机制,那如果你的业务体量真的很大,超过了单机可能承载的下限了怎么办?不做任何解决的话 Redis 挂了怎么办?带着这个问题开始咱们明天的主题 -Redis 高可用,因为篇幅起因,本章就只聊聊主从复制。
为啥要先从主从复制开始聊,是因为 主从复制 能够说是整个 Redis 高可用实现的基石,你能够先有这么一个概念,至于具体为什么是基石,这个前面聊到 Sentinel 和 Redis 集群的时候会说到。
首先咱们须要晓得,对于咱们开发人员来说,为什么须要 主从架构?一个 Redis 实例难道不行吗?
其实除了开篇提到的负载超过了 Redis 单机可能解决的下限,还有一种状况 Redis 也无奈保障本身的高可用性。那就是即使 Redis 可能扛住所有流量,然而如果这个 Redis 过程所在的机器挂了呢?申请会间接调转枪口,大量的流量会霎时把你的 DB 打挂,而后你就能够背个 P0,打包回家了。
而且,假如你对 Redis 的需要真的超过了单机的容量,你怎么办?搞多台独立的 Redis 实例吗?那如果用户缓存的数据这一次存在了实例一,下一次如果用户又拜访到了实例二,难道又要去走一遍 DB 吗?除非你可能保护好用户和 Redis 实例的对应关系(然而通常这样的逻辑比较复杂),否则部署多个 Redis 实例也就失去了它的意义,没有方法做到横向扩大了。
那换成主从架构就能解决这个问题吗?
咱们能够从一个图来直观的理解一下。
在主从同步中,咱们将节点的角色划分为 master
和slave
,造成 一主多从。slave 对外提供读操作,而 master 负责写操作,造成一个读写拆散的架构,这样一来就可能承载更多的业务申请。
在少数的业务场景下,对于 Redis 的 读操作 都要多于 写操作,所以当读申请量特地大的时候,咱们能够通过减少 slave 节点来使 Redis 扛住更多的流量。
你这不行啊老弟,你往 master 写数据,那我要是连贯到 slave 下来了,不就拿不到之前的数据了?
我这个小标题的不是写了吗?主从复制 ,slave 会依照某种策略从 master 同步数据。Redis 中咱们能够通过slaveof
命令让一个 Redis 实例去复制(replicate)另外一台 Redis 的状态。被复制的 Redis 实例就是 master 节点,而执行 slaveof
命令的机器就是 slave 节点。
Redis 的主从复制分为两个步骤,别离是 同步 和命令流传。
同步操作 用于将 Master 节点内存状态复制给 Slave 节点,而 命令流传 则是在同步时,客户端又执行了一些 写操作扭转了服务器的状态,此时 master 节点的状态与同步操作执行的时候不统一了,所以须要命令流传来使 master 和 slave 状态从新统一。
同步的大抵的流程如下:
- slave 节点向 master 节点发送
sync
命令 - master 收到
sync
命令之后会执行bgsave
命令,Redis 会 fork 出一个子过程在后盾生成 RDB 文件,同时将同步过程中的写命令记录到缓冲区中 - 文件生成后,master 会把 RDB 文件发送给 slave,从服务器接管到 RDB 文件会将其载入内存
- 而后 master 将记录在缓冲区的所有写命令发送给 slave,slave 对这些命令进行 重放,将其数据库的状态更新至和 master 统一
为了让大家更加清晰的意识到这个过程,咱们通过图再来理解一下。
????????,那如果同步完了之后 slave 又挂了咋办?slave 重启之后很可能就又跟 maste 不统一了?
确实是这样,这就波及到一个名词叫 断点续传 了。下面探讨的是 slave 第一次连贯到 master,会执行 全量复制,而针对下面这种状况,Redis 新老版本解决形式不一样。
Redis2.8 之前,当主从实现了同步之后,slave 如果断线重连,向 master 发送 sync
命令,master 会将全量的数据再次同给 slave。
然而咱们会发现一个问题,就是大部分数据都是有序的,再次全量同步显得没有必要。而在 Redis2.8 之后,为了解决这个问题,便应用了 psync
命令来代替sync
。
简略来说 psync 命令就是将 slave 断线期间 master 接管到的写命令全副发送给 slave,slave 重放之后状态便与 master 统一了。
呵呵,就这?那你晓得 psync 具体怎么实现的吗?还是说就只会用用?
psync 的实现依赖于主从单方独特保护的 offset
偏移量。
每次 master 向 slave 进行 命令流传,流传了多少个字节的数据,就将本人的 offset 加上流传的字节数。而 slave 每次收到多少字节的数据,也会同样的更新本人的 offset。
基于 offset,只须要简略的比对就晓得以后主从的状态是否是统一的了,而后基于 offset,将对应偏移量所对应的指令流传给 slave 重放即可。所以即便同步的时候 slave 挂掉了,基于 offset,也能达到断点续传的成果。
不是吧不是吧,那 master 也挂了呢?你 slave 重新启动之后 master 的数据也更新了,依照你的说法,这两永远不可能达到数据统一了
这个问题 Redis 确实也有想到,实际上除了 offset 之外,slave 断线重连之后还会带上上一个 master 的实例的 runid
,每个服务实例都有本人的惟一的 runid,只有 Redis 服务重启,其runid
就会产生扭转。
master 收到这个 runid 之后会判断是否与本人以后的 runid 统一,如果统一阐明断线之前还是与本人建设的连贯,而如果不统一就阐明 slave 断线期间,master 也产生了宕机,此时就须要将数据 全量同步 给 slave 了。
就算你能解决这个问题,然而你就保护了一个偏移量,偏移量对应的命令从哪儿来?天上掉下来吗?我哪儿晓得这些命令是啥?
确实,咱们须要通过这个 offset 去拿到真正须要的数据—也就是指令,而 Redis 是通过 复制积压缓冲区 来实现的。
名字高大上,实际上就是一队列。就跟什么递归、轮询、透传一样,听着高大上,实际上简略的一匹。言归正传,复制积压缓冲区的默认大小为 1M,Redis 在进行 命令流传 时,除了将写命令发送给 slave,还会将命令写到 复制积压缓冲区 内,并和以后的 offset 关联起来。这样一来就可能通过 offset 获取到对应的指令了。
然而因为缓冲区的大小无限,如果 slave 的断线工夫太久,复制积压缓冲区内早些时候的指令就曾经被新的指令笼罩掉了,此处能够了解为一个队列,早些时候入队的元素曾经被出队了。
因为没有绝对应的 offset 了,也就无奈获取指令数据,此时 Redis 就会进行 全量同步 。当然,如果 offset 还存在于复制积压缓冲区中,则依照对应的 offset 进行 局部同步。
基于以上的全量、增量的主从复制,可能在 master 呈现故障的状况下,进行主从的切换,保障服务的失常运行。除此之外还能解决异常情况下数据失落的问题。基于读写拆散的策略还可能进步整个 Redis 服务的并发量。
可别吹了,你说的这个什么 主从复制 就没啥毛病吗?
其实是有的,例如刚刚提到的主从的切换,如果不必现成的 HA 框架,这个过程须要程序员本人手动的实现,同时告诉服务调用方 Redis 的 IP 产生了变动,这个过程能够说是非常的简单,甚至还可能波及到代码配置的改变。而且之前的 slave 复制的可都是挂掉的 master,还得去 slave 上更改其复制的主库,就更加简单了。
除此之外,尽管实现了读写拆散,然而因为是 一主多从 的架构,集群的 读申请 能够扩大,然而 写申请 的并发是有下限的,那就是 master 可能扛住的下限,这个没有方法扩大。
好了,本期的分享就到此结束了,咱们下期再见。
如果你感觉这篇文章对你有帮忙,还麻烦 点个赞 , 关个注 , 分个享 , 留个言
也能够微信搜寻公众号【SH 的全栈笔记】,关注公众号提前浏览其余的文章
往期文章
- Redis 根底—分析根底数据结构及其用法
- Redis 根底—理解 Redis 是如何做数据长久化的
- 简略理解一下 K8S,并搭建本人的集群
- WebAssembly 齐全入门——理解 wasm 的前世今生
- 浅谈 JVM 与垃圾回收