为什么 需要集群?
- 性能
Redis 本身的 QPS 已经很高了,但是如果在一些并发量非常高的情况下,性能还是会受到影响。这个时候我们希望有更多的 Redis 服务来完成工作
- 扩展
出于存储的考虑,因为 Redis 所有的数据都放在内存中,如果数据量大,很容易受到硬件的限制。升级硬件收效和成本比太低,所以我们需要有一种横向扩展的方法
- 可用性
可用性和安全的问题。如果只有一个 Redis 服务,一旦服务宕机,那么所有的客户端都无法访问,会对业务造成很大的影响。另一个,如果硬件发生故障,而单机的数据无法恢复的话,带来的影响也是灾难性的
可用性、数据安全、性能都可以通过搭建多个 Reids 服务实现。其中有一个是主节点(master),可以有多个从节点(slave)。主从之间通过数据同步,存储完全相同的数据。如果主节点发生故障,则把某个从节点改成主节点,访问新的主节点。
Redis主从复制(replication)
主从复制配置
例如一主多从,101 是主节点,在每个 slave 节点的 redis.conf 配置文件增加一行
slaveof 192.168.1.101 6379
在主从切换的时候,这个配置会被重写成:
# Generated by CONFIG REWRITE
replicaof 192.168.1.101 6379
或者在启动服务时通过参数指定 master 节点:
./redis-server --slaveof 192.168.1.101 6379
或在客户端直接执行 slaveof xx xx,使该 Redis 实例成为从节点
启动后,查看集群状态:
redis> info replication
从节点不能写入数据(只读),只能从 master 节点同步数据。get 成功,set 失败
127.0.0.1:6379> set vincent 666
(error) READONLY You can't write against a read only replica.
主节点写入后,slave 会自动从 master 同步数据
断开复制:
redis> slaveof no one
此时从节点会变成自己的主节点,不再复制数据
主从复制原理
连接阶段
slave node 启动时(执行 slaveof 命令),会在自己本地保存 master node 的信息,包括 master node 的 host 和 ip
slave node 内部有个定时任务 replicationCron(源码 replication.c),每隔 1秒钟检查是否有新的 master node 要连接和复制,如果发现,就跟 master node 建立socket 网络连接,如果连接成功,从节点为该 socket 建立一个专门处理复制工作的文件
事件处理器,负责后续的复制工作,如接收 RDB 文件、接收命令传播等
当从节点变成了主节点的一个客户端之后,会给主节点发送 ping 请求
数据同步阶段
master node 第一次执行全量复制,通过 bgsave 命令在本地生成一份 RDB 快照,将 RDB 快照文件发给 slave node(如果超时会重连,可以调大 repl-timeout 的值)。slave node 首先清除自己的旧数据,然后用 RDB 文件加载数据。
生成RDB 期间,master 接收到的命令怎么处理?
开始生成 RDB 文件时,master 会把所有新的写命令缓存在内存中。在 slave node保存了 RDB 之后,再将新的写命令复制给 slave node
命令传播阶段
master node 持续将写命令,异步复制给 slave node
延迟是不可避免的,只能通过优化网络
repl-disable-tcp-nodelay no
当设置为 yes 时,TCP 会对包进行合并从而减少带宽,但是发送的频率会降低,从节点数据延迟增加,一致性变差;具体发送频率与 Linux 内核的配置有关,默认配置为40ms。当设置为 no 时,TCP 会立马将主节点的数据发送给从节点,带宽增加但延迟变
小
一般来说,只有当应用对 Redis 数据不一致的容忍度较高,且主从节点之间网络状况不好时,才会设置为 yes;多数情况使用默认值 no
如果从节点有一段时间断开了与主节点的连接是不是要重新全量复制一遍?如果可以增量复制,怎么知道上次复制到哪里?
通过 master_repl_offset 记录的偏移量
redis> info replication
主从复制的不足
主从模式解决了数据备份和性能(通过读写分离)的问题,但是还是存在一些不足:
1、RDB 文件过大的情况下,同步非常耗时
2、在一主一从或者一主多从的情况下,如果主服务器挂了,对外提供的服务就不可用了,单点问题没有得到解决。如果每次都是手动把之前的从服务器切换成主服务器,这个比较费时费力,还会造成一定时间的服务不可用