1. 主从复制
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave);从服务器无法进行写操作。

1.1 完全同步过程

从服务器发送psync ? -1,主服务器回复 FULLRESYNC。
主服务器执行 bgsave 命令,生成 RDB 文件,接着将文件发给从服务器。
主服务器将 replication buffer 缓冲区里所记录的写操作命令发送给从服务器。
1.2 增量复制
从 Redis 2.8 开始,网络断开又恢复后,从主从服务器会采用增量复制的方式继续同步,也就是只会把网络断开期间主服务器接收到的写操作命令,同步给从服务器。
repl backlog buffer【复制回滚缓冲区】: 一个主库只分配一个repl backlog, 环形缓冲区,用于从中找到差异的数据。主要用于部分重新同步,存储最近的写命令。
replication buffer【复制缓冲区】: 主库会给每个新连接的从库分配一个replication buffer,主要用于全量或增量同步时,存储数据传输过程中的数据。

从服务器发送 psync 命令给主服务器, offset 参数不是 -1;
主服务器判断从服务器要读取的数据还在 repl_backlog_buffer 缓冲区里,那么采用增量同步的方式,否则全量同步;
然后主服务将主从服务器断线期间,所执行的写命令发送给从服务器,然后从服务器执行这些命令。
1.3 配置操作
下载
1
2
3
4
5
6
7
8
9
10
11
12
13# 下载
docker pull redis:latest
# 下载配置文件
http://download.redis.io/redis-stable/redis.conf
# 主redis配置, 无需特殊配置, 因为在 docker 内, 需要修改一下bind
vi $PWD/redis1/redis.conf
bind 127.0.0.1 改为 bind 0.0.0.0
# 修改从redis配置, 修改 redis.conf 文件
vi $PWD/redis2/redis.conf
slaveof 172.17.0.1 63791启动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15# 主服务器
docker run \
-p 63791:6379 \
-v $PWD/redis1/redis.conf:/etc/redis/redis.conf \
--privileged=true \
--name redis1 \
-d redis redis-server /etc/redis/redis.conf
# 从服务器
docker run \
-p 63792:6379 \
-v $PWD/redis2/redis.conf:/etc/redis/redis.conf \
--privileged=true \
--name redis2 \
-d redis redis-server /etc/redis/redis.conf测试
1
2
3
4
5
6
7
8
9
10# 查看配置文件
redis-cli info | grep config_file
# 查看从的状态, 看到master_link_status:up就是成功
redis-cli info | grep master
master_host:172.17.0.1
master_port:63791
master_link_status:up
# 然后在主服务器 set key, 在从服务器 get 即可
2. 哨兵
在使用 Redis 主从服务的时候,会有一个问题,就是当 Redis 的主从服务器出现故障宕机时,需要手动进行恢复。
为了解决这个问题,Redis 2.8版本开始引入增加了哨兵模式(Redis Sentinel),因为哨兵模式做到了可以监控主从服务器,并且提供主从节点故障转移的功能。
因为Redis Sentinel实际上就是一个运行在特殊模式下的Redis服务器,所以用户也可以使用命令redis-server sentinel.conf –sentinel去启动一个Sentinel:这里的–sentinel参数用于指示Redis服务器进入Sentinel模式,从而变成一个Redis Sentinel而不是普通的Redis服务器。

2.1 哨兵的功能
监控(Monitoring)
哨兵会不断地检查主节点和从节点是否运作正常。
自动故障转移(Automatic failover)
当主节点不能正常工作时,哨兵会开始自动故障转移操作,它会将失效主节点的其中一个从节点升级为新的主节点,并让其他从节点改为复制新的主节点。
配置提供者(Configuration provider)
客户端在初始化时,通过连接哨兵来获得当前Redis服务的主节点地址。
通知(Notification)
哨兵可以将故障转移的结果发送给客户端。
其中,监控和自动故障转移功能,使得哨兵可以及时发现主节点故障并完成转移;而配置提供者和通知功能,则需要在与客户端的交互中才能体现。
2.2 哨兵集群的组建
哨兵实例之间可以相互发现,要归功于 Redis 提供的 pub/sub 机制,也就是发布 / 订阅机制。
在主从集群中,主库上有一个名为__sentinel__:hello
的频道,不同哨兵就是通过它来相互发现,实现互相通信的。在下图中,哨兵 1 把自己的 IP(172.16.19.3)和端口(26579)发布到__sentinel__:hello
频道上,哨兵 2 和 3 订阅了该频道。那么此时,哨兵 2 和 3 就可以从这个频道直接获取哨兵 1 的 IP 地址和端口号。然后,哨兵 2、3 可以和哨兵 1 建立网络连接。

1 | # sentinel monitor <master-name> <master-host> <master-port> <quorum> |
2.3 哨兵监控Redis库
这是由哨兵向主库发送 INFO 命令来完成的。就像下图所示,哨兵 2 给主库发送 INFO 命令,主库接受到这个命令后,就会把从库列表返回给哨兵。接着,哨兵就可以根据从库列表中的连接信息,和每个从库建立连接,并在这个连接上持续地对从库进行监控。哨兵 1 和 3 可以通过相同的方法和从库建立连接。

2.4 主库下线的判定
首先要理解两个概念:主观下线和客观下线。
主观下线
任何一个哨兵都是可以监控探测,并作出Redis节点下线的判断。
客观下线
有哨兵集群共同决定Redis节点是否下线。
当某个哨兵(如下图中的哨兵2)判断主库“主观下线”后,就会给其他哨兵发送 is-master-down-by-addr
命令。接着,其他哨兵会根据自己和主库的连接情况,做出 Y 或 N 的响应,Y 相当于赞成票,N 相当于反对票。

如果赞成票数(这里是2)是大于等于哨兵配置文件中的 quorum
配置项(比如这里如果是quorum=2), 则可以判定主库客观下线了。
2.5 哨兵 Leader 的选举
为了避免哨兵的单点情况发生,所以需要一个哨兵的分布式集群。哨兵的选举机制其实很简单,就是一个Raft选举算法: 选举的票数大于等于num(sentinels)/2+1时,将成为领导者,如果没有超过,继续选举。
任何一个想成为 Leader 的哨兵,要满足两个条件。第一,拿到半数以上的赞成票;第二,拿到的票数同时还需要大于等于哨兵配置文件中的 quorum 值。
以 3 个哨兵为例,假设此时的 quorum 设置为 2,那么,任何一个想成为 Leader 的哨兵只要拿到 2 张赞成票,就可以了。
2.6 哨兵Leader 故障转移
主从故障转移操作包含以下四个步骤:
- 第一步:在已下线主节点(旧主节点)属下的所有「从节点」里面,挑选出一个从节点,并将其转换为主节点。
- 第二步:让已下线主节点属下的所有「从节点」修改复制目标,修改为复制「新主节点」;
- 第三步:将新主节点的 IP 地址和信息,通过「发布者/订阅者机制」通知给客户端;
- 第四步:继续监视旧主节点,当这个旧主节点重新上线时,将它设置为新主节点的从节点;
新主库的选出
- 过滤掉不健康的(下线或断线),没有回复过哨兵ping响应的从节点
- 选择
salve-priority
从节点优先级最高(redis.conf)的 - 选择复制偏移量最大,只复制最完整的从节点

判定主库客观下线 和 是否能够主从切换?
Redis 1主4从,5个哨兵,哨兵配置quorum为2,如果3个哨兵故障,当主库宕机时,哨兵能否判断主库“客观下线”?能否自动切换?
1、哨兵集群可以判定主库“主观下线”。由于quorum=2,所以当一个哨兵判断主库“主观下线”后,询问另外一个哨兵后也会得到同样的结果,2个哨兵都判定“主观下线”,达到了quorum的值,因此,哨兵集群可以判定主库为“客观下线”。
2、但哨兵不能完成主从切换。哨兵标记主库“客观下线后”,在选举“哨兵领导者”时,一个哨兵必须拿到超过多数的选票(5/2+1=3票)。但目前只有2个哨兵活着,无论怎么投票,一个哨兵最多只能拿到2票,永远无法达到N/2+1
选票的结果。
3. Redis Cluster集群
Redis-cluster是一种服务器Sharding技术,Redis3.0以后版本正式提供支持。Redis Cluster 功能 : 负载均衡,故障切换,主从复制。
Redis Cluster要求至少需要3个master才能组成一个集群,同时每个master至少需要有一个slave节点。各个节点之间保持TCP通信。当master发生了宕机, Redis Cluster自动会将对应的slave节点提拔为master,来重新对外提供服务。

3.1 特点
多主多从,去中心化
从节点作为备用,复制主节点,不做读写操作,不提供服务。
不支持处理多个key
因为数据分散在多个节点,在数据量大高并发的情况下会影响性能;
支持动态扩容节点
这是我认为算是Rerdis Cluster最大的优点之一
节点之间相互通信,相互选举,不再依赖sentinel
准确来说是主节点之间相互“监督”,保证及时故障转移。
3.2 分片和重分片
与单机版Redis将整个数据库放在同一台服务器上的做法不同,Redis集群通过将数据库分散存储到多个节点上来平衡各个节点的负载压力。
具体来说,Redis集群会将整个数据库空间划分为16384个槽(slot)来实现数据分片(sharding),而集群中的各个主节点则会分别负责处理其中的一部分槽。当用户尝试将一个键存储到集群中时,客户端会先计算出键所属的槽,接着在记录集群节点槽分布的映射表中找出处理该槽的节点,最后再将键存储到相应的节点中。

当用户想要向集群添加新节点时,只需要向Redis集群发送几条简单的命令,集群就会将相应的槽以及槽中存储的数据迁移至新节点。与此类似,当用户想要从集群中移除已存在的节点时,被移除的节点也会将自己负责处理的槽以及槽中数据转交给集群中的其他节点负责。
最重要的是,无论是向集群添加新节点还是从集群中移除已有节点,整个重分片(reshard)过程都可以在线进行,Redis集群无须因此而停机。
3.3 连接集群
为了连接集群,我们需要向客户端提供集群的节点地址。因为节点与节点之间一般都可以自动发现,所以我们通常只需要给定一个节点作为入口即可:
1 | >>> nodes = [{"host":"127.0.0.1", "port":"30001"}] |
3.4 散列标签
在默认情况下,Redis将根据用户输入的整个键计算出该键所属的槽,然后将键存储到相应的槽中。但是在某些情况下,出于性能方面的考虑,或者为了在同一个节点上对多个相关联的键执行批量操作,我们也会想要将一些原本不属于同一个槽的键放到相同的槽里面。
为了满足这一需求,Redis为用户提供了散列标签(hash tag)功能,该功能会找出键中第一个被大括号{}包围并且非空的字符串子串(sub string),然后根据子串计算出该键所属的槽。这样一来,即使两个键原本不属于同一个槽,但只要它们拥有相同的被包围子串,那么程序计算出的散列值就是一样的,因此Redis集群就会把它们存储到同一个槽中。
1 | 127.0.0.1:30002> CLUSTER KEYSLOT {user}::10086 |
4. 头脑风暴
主从复制有全量复制和增量复制,全量就是rdb,增量就是一个环形缓冲区。
哨兵是特殊的redis命令进行监控,哨兵要组成一个集群监控主库。选主哨兵leader然后主观和客观判断是否主从切换。
Redis cluster 集群是分区的方案,数据存在很多节点。每个节点又会有主从复制。