redis 主从同步

一般,当系统性能到达瓶颈或者为了保障系统稳定性,会对 DB 或者缓存层做主从同步,甚至是集群。这篇文章着重于 Redis 的主从同步,描述 Redis 主从同步的一些机制,并且使用 docker 来实验 Redis 的主从同步。

redis 主从同步的一般模式为:一台 redis 服务作为主服务器(master),若干台 redis 服务作从服务器(slave)。客户端只能向 master 写数据,不能直接向 slave 写数据, master 会把数据同步到 slave。客户端可以直接从 slave 读取数据。配置主从同步之后有两个优点:

若干台 redis 服务运行在不同的服务器上,这其实就是分布式,这里必然就需要提到 CAP 理论。

CAP 理论

CAP 理论包含以下 3 点:

一致性是对于数据而言,简单来说,多个 redis 服务中每个 redis 中的数据应该是完全一致的。可用性是对于整个系统而言,系统应该一直保持可用的状态,不应该出现宕机,结果与期待的不一样等情况。

对于 C,A 比较好理解,下面重点说下 P 的含义。

对于分布式系统,节点都会分布在不同的网络中,每个节点通过网络进行通信,数据同步。当网络出现故障,节点之间无法进行通信,节点散落在不同的网络中,这就形成了分区。当数据只保存在一个节点中,由于分区的出现导致了这部分数据不可访问,这时分区就不可容忍了。

为了保证分区容忍性,redis 的数据应该保存在多个节点,这样即使某个 redis 出现网络故障,出现分区,也可以从其他节点访问到数据。这种就说明分区容忍性高。

其实可以发现,C,A,P 三个是无法兼得的,为了提高分区容忍性,数据应该同步到多个节点上,在同步数据的过程中,系统就不满足一致性。如果每次写操作都要保证数据都复制到了每个节点,这样就会牺牲系统的可用性。所以,在设计分布式系统的时候,要根据系统的功能性,对于 C,A,P 三个特性做取舍。

redis 主从同步

redis 的主从同步是异步的,这意味着 redis 主从同步不满足数据一致性的要求,在客户端修改了 master 节点的数据之后会立即返回,在合适的时候 master 会把数据同步到 slave。

redis 的主从同步有 3 种方案:

增量同步的是指令,master 会把写数据的指令缓存到 buffer 中,然后异步将指令同步到 slave 节点,slave 依次执行指令来达到同步数据。需要注意,buffer 的大小是有限的,master 不可能会把所有的指令都缓存在 buffer 中,当 buffer 满了之后,新的指令会覆盖最开始的内容。如果网络情况不好,那么很容易出现指令被覆盖的情况。

快照同步的是数据,master 会把内存的数据执行 bigsave 保存到硬盘中,然后发送给 slave。slave 收到数据之后先把数据保存到硬盘中,再加载到内存,这样就完成一次数据同步。在同步过程中,master 的数据有可能会被客户端修改,这时候 master 会使用增量同步的方式把指令保存到 buffer 中,等快照同步结束之后再进行增量同步。

快照同步中 master 节点需要进行磁盘 IO,为了避免这种低效率的 IO,redis 还提供了无盘同步的方式。无盘同步情况下,master 节点会直接把 redis 数据从内存中通过 socket 直接发送到 slave,slave 先把数据保存到硬盘,然后再加载到内存中。

docker 实验主从同步

下面使用 docker 来实验 redis 的主从同步。使用 docker 的一个好处在于不必真的开多台服务器,只需要启动多个 container 就可以了。这里我们启动一个 master,3 个 slave 来测试。

让一台 redis 服务成为 slave 只需要在 redis.conf 中添加一项配置

# master-redis-ip 为 master 节点的 ip
# master-redis-port 为 master 的端口
slaveof master-redis-ip master-redis-port

 整个实验的目录结果如下:

|____slave2
| |____dockerfile
| |____redis.conf
|____slave3
| |____dockerfile
| |____redis.conf
|____master
| |____dockerfile
| |____redis.conf
|____docker-compose.yml
|____slave1
| |____dockerfile
| |____redis.conf

整个配置很简单,如果清楚可以在 github 上 clone 代码,执行以下命令即可

docker-compose build && docker-compose up -d

以上,整个实验环境就搭建成功了,执行 docker exec -it container_id redis-cli -p port 就可以进入 redis 环境。

数据丢失

数据在不同的服务器之间备份,就一定会涉及到数据丢失的问题。存在一种可能性:master 和 slave 断开连接,有部分数据还没有同步到 slave 节点,这就造成了数据不一致的问题,对于客户端的影响就是数据明明写入了,但是读取的时候却没有值。但是由于 redis 主从同步是异步的,无法保存数据一致性的要求,所以这个问题必然有可能存在。不过 redis 提供了 2 个配置来降低数据的可能性。

min-slaves-to-write 2
min-slaves-max-lag 10

min-slaves-to-write 配置规定了当至少 min-slaves-to-write 个 slave 节点连接到了 master 节点,master 节点才会对外提供写服务,如果连接的 slave 节点低于 min-slaves-to-write 配置的值,master 节点就不提供写服务。

那么,master 节点是如何确认 slave 节点是否保持连接的?答案是通过 ping 命令。master 节点会每隔 1s 就向所有的 slave 节点发送 ping 命令,如果收到 slave 节点的回复,那说明与该 slave 节点保持连接。明白这个就可以解释 min-slaves-max-lag 指令的含义了:当 master 节点收到 slave 节点的 ping 回复时间超过 min-slaves-max-lag 秒,master 节点不会想外提供写服务。

有了以上的两个配置之后,可以把数据丢失的问题降低到可以容忍的程度。

整个 redis 主从同步就这么简单,之后会出一篇文章说明如何使用 sentinel 来实现 redis 的高可用。

这里说一个有趣的事情,redis 中主从同步使用的单词是 slave,但是后来有人提出不应该用 slave,这个单词的意思是『奴隶』,使用这个单词有暗示奴隶制度。。。所以,redis 作者在压力下把 slave 改成了 replica,这个改动对于 reids 这种广泛使用的软件,影响是巨大的,也真是服了这些人。

*****
Written by JayChen on 12 December 2018