Redis复制机制
作为一个优秀的分布式基础服务,Reids的设计很值得我们学习。本文就介绍一下Redis的主从数据复制机制。
Redis 2.8 版本之前复制实现
SYNC命令
当客户端向服务端发送数据同步请求的时候,整个处理过程主要有以下四个步骤:
- 从服务器向主服务器发送SYNC命令
- 收到SYNC命令的主服务器开始执行BGSAVE命令,在后台生成RDB文件,并同时开启缓冲区,将这段时间新进来的命令记录下来。
- 当主服务器RDB文件生成结束以后,主服务器将会把文件发给从服务器去执行,让从服务器可以同步到主服务器开始执行BGSAVE命令时的状态。
- 主服务器将生成RDB文件这段时间内,存在缓冲区的新命令发送给从服务器,让从服务器数据可以更新到最新状态。
命令传播
Redis所有写操作都会在主服务器上执行。例如,用户通过Redis客户端执行”DEL k3”命令。
这个时候,这条命令会被Redis的主服务器所执行,也就是删掉k3这个键。
那么问题来了,如果只有主服务器删掉了k3这个键,当有用户在从服务器上执行查操作的时候,k3这个键不还是可以被查出来么?
这个时候就需要命令传播机制,虽然用户的请求是有主服务器执行,但主服务器会立即将这条会造成主从服务器数据不一致的命令发送给从服务器,从服务器接收后执行。
这样就能保持主、从服务器数据一致了,也就是所谓的”命令传播”。
新版复制功能实现(Redis 2.8 版本以后)
旧版本的缺陷
要想理解新版本为何更好,还需要先理解旧版本的不足。
上面只提到了,当Redis从服务器向主服务器发送SYNC命令时,主服务器会生成一个RDB文件。举个具体点的例子,假设这个Redis集群从启动开始一共接收了10000条SET命令,执行SYNC命令的具体操作就是将这个10000条SET命令包装成RDB文件发送给从服务器。
讲到这里大家或许就能发现问题,加入从服务器是一个新的服务器,需要所有老数据这样做确实没有问题。但如果这台从服务器本身就有1~9999条数据,它只差一条就能保持和主服务器一致,但主服务器执行SYNC命令时还是会从第1~10000条去生成。
大致分析一下就能明白,首先主服务器生成RDB文件时加入太多不需要的数据,导致浪费了很多内存和CPU,而生成完RDB文件以后发送给从服务器由于文件又比原本需要的大很多,又白白浪费了大量的带宽。
所以,我们说SYNC操作是个非常耗费资源的操作。
PSYNC
为了解决SYNC在断线复制的低效问题,从Redis 2.8开始使用PSYNC代替SYNC。
PSYNC分为完整同步和部分同步两部分,完整同步和SYNC类似,用于从服务器首次同步。
而部分同步则用于处理类似断线重连这种只需要同步部分数据丢失的情况。还是以上面的例子为例,当从服务器有1~9999条数据时,主服务器只会将最后一条数据生成RDB文件发送。
PSYNC的实现原理
PSYNC的实现原理也比较容易理解,就是主服务器、从服务器分别维护一个当前的命令偏移量。
还是以上面的例子举例,主服务器上因为一共接收了10000条命令,所以服务器的偏移量就是10000;而从服务器因为断线,自己只接收到了前9999条命令,这个时候从服务器向主服务器发起同步请求的时候,就会带上这个偏移量,这样主服务器就只会将第10000条命令打包成RDB文件,发送给从服务器了。
主服务器维护了一个复制积压缓冲区,这是一个固定长度、先进先出的队列,就是将执行完的命令记录下来。
例如,当前复制积压缓冲区存放的是9000~10000条命令,这个时候从服务器发来同步请求,带给主服务器的偏移量是9999,那么主服务器就能轻易找到从服务器缺失的那部分命令,直接打包发送。
结尾
主从同步本身就是分布式系统共同的难题,分析这些业界成熟产品的实现不仅能不断提升自己对分布式的理解,更能在潜移默化中影响你的设计理念,让你在系统设计中游刃有余。