Redis主从库实现数据一致

·  阅读 502

即使有AOF和RDB,也依然有服务不可用的问题。为啥嘞?假设你只有一台服务器,它宕机了,它在恢复期间,新来的数据请求是不是就无法被提供服务了。

Redis具有高可靠性是指:数据尽量少丢失,和服务尽量少中断。AOF和RDB保证了前者,那么后者Redis的做法是增加副本冗余量,将一份数据保存在多个实例上,这样一台机器挂了,其他的还能继续服务。

多个服务如何保持数据一致呢?

Redis肯定有解决方法,那就是她提供了主从库模式,然后主从库之间读写分离。保证数据副本一致。

读操作:主从库都可以读 写操作:主库先写,然后同步给从库

为啥要读写分离呢? 假设不读写分离,客户端对一个数据操作了三次,每次都落在不同的实例上,这个时候读取是不是就有问题了,这样就可能取到旧的值。 当然如果非要保证这三个实例数据一致也不是不可能,加锁,实例间协商等等一大堆操作,但是这没有必要呀,开销多大。

如果我们读写分离,那我们就不用担心这个问题了,写操作只在主库,直接同步就行了。

那么我们同步是怎么同步的嘞、是一次性还是分批次?主从库连接断了还能保持一致吗? 听我慢慢道来。

1、主从库如何进行第一次同步

多个Redis实例之间相互是通过replicaof形成主从库的关系,我们在实例2上执行 replicaof 实例一的IP 端口号
这条命令时,实例2就成了实例1的从库,并且从实例一上面复制了数据。

先看张图

主要分为三个阶段:

1、建立主从库连接,协商同步。 这一步成功建立连接了,从库会告诉主库即将同步(发送psync命令,包含了主库的runID和复制进度offset),主库确认回复后就开始同步了。 runID,是每个Redis实例启动时都会自动生成的一个随机ID,用来唯一标记这个实例。当从库和主库第一次复制时,因为不知道主库的runID,所以将runID设为“?”。 offset,此时设为-1,表示第一次复制。

主库接受到psync命令后,会用fullresync响应命令带上两个参数,返回给从库,从库也会记录下这两个参数,fullresync第一次复制是全量复制。

2、主库将数据给从库,从库收到数据,在本地完成数据加载。 这个过程需要RDB文件,主库先生存RDB文件,接着将文件发给从库,从库收到文件后,先清空当前数据库,然后加载RDB文件。这是为了避免之前数据的影响。

主库将数据同步给从库的过程中,主库不会被阻塞,仍然可以继续接受正常请求。这些新请求如何保持一致性呢?主库会在内存中用专门的replication buffer,记录这些新请求。

3、主库会把上面的新请求再发送给从库,就是当RDB发生完后,就会把此时的replication buffer的修改操作发给从库。

2、主从级联模式分担全量复制时的主库压力

全量复制时主库会有两个操作,生成RDB和传输RDB文件。

如果从库太多,主库就忙于fork子进程生成RDB文件,进行全量同步,这个fork操作会阻塞主线程,从而导致响应变慢。传输RDB也会占用网络带宽,主库的压力较大。

肯定是有解决方式的,主-从-从。

通过“主-从-从”模式将主库生成RDB和传输RDB的压力,以级联的方式分散到从库上。

简单说就是部署主从集群,手动选择一个从库,用于联级其他的从库,然后再选择一些从库,让他们和刚刚的从库建立主从关系,命令还是replicaof

这样一来,这些从库就不会和主库进行交互,只要与联级的从库交互就行。

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

听起来好像挺简单,但是有风险点,最常见的就是网络断链或者阻塞,这样主从之间就无法进行命令传播了,也没法保持数据一致性了。

有问题那就肯定有解决方法。

3、主从库之间的网络断了咋办

Redis2.8之前,断了就再进行一次全量复制,这玩意开销大,之后的版本才有增量复制的方式继续同步。

增量就是只把主从库网络连接断了期间主库收到的命令进行同步。

主要的地方就是repl_backlog_buffer这个缓冲区。 它是一个环形的缓存区,主库会记录自己写到的位置,从库会记录自己读到的位置。听到这里差不多知道怎么办了吧。

我再说说,刚开始的时候,主从位置是一样的,主库不断接受新命令,位置一直在偏移,有个偏移量,也就是master_repl_offset,新操作越大,这个值越大。 从库在复制完写操作命令之后,它的位置也开始偏移,也有个偏移量slave_repl_offset,正常情况下,主从的这两个偏移量相等。

主从库恢复连接后,从库给主库发生给连接命令就是psync,同时还有自己的偏移量也会发生给主库,主库呢就来判断两个之间的差距。

如果有新数据,主库就将这两个偏移量之间的数据命令操作同步给从库。

再看个图看下增量复制

注意个点,repl_backlog_buffer这个缓冲区,它是环形的那就可能发生覆盖,如果缓冲区满了,主库还在写这就覆盖了,这个时候从库读取的慢,那就不能保持数据一致性了。

怎么避免呢? 我们调整repl_backlog_size这个参数,和缓冲区空间大小有关。有个计算公式 缓冲空间大小 = 主库写入命令速度 * 操作大小 - 主从库间网络传输命令速度 * 操作大小 实际中我们考虑突发的请求我们会把这个扩大一倍。

举个例子: 主库每秒写2000个操作,每个操作大小2kb,网络每秒传输1000个,就需要把剩下的1000个操作存起来,至少需要2MB的空间,为了应对突发情况,我们会设置成4MB。

这个是动态的,根据实际情况可以设置4倍,8倍等。

总结:因为全量复制比较耗时也无法避免,所以我们一般一个Redis实例的数据库不要太大,

分类:
后端
标签: