1. 主从复制
- 一主多从模式,采用读写分离的方式
- 主服务器可以进行读写操作,当发生写操作时自动将写操作同步给从服务器,而从服务器一般是只读,并接受主服务器同步过来写操作命令,然后执行这条命令。
- 主从服务器之间的命令复制是异步进行的
1.1 第一次同步
- 建立连接、协商同步
- 主服务器会把所有的数据都同步给从服务器(全量复制)。
- 主服务器同步数据给从服务器
- 主服务器会执行 bgsave 命令来生成 RDB 文件,然后把文件发送给从服务器
- 从服务器收到 RDB 文件后,会先清空当前的数据,然后载入 RDB 文件(异步)
- 为了保证主从服务器的数据一致性,主服务器在下面这三个时间间隙中将收到的写操作命令,写入到 replication buffer 缓冲区里
- 主服务器生成 RDB 文件期间;
- 主服务器发送 RDB 文件给从服务器期间;
- 从服务器加载 RDB 文件期间;
- 主服务器发送新写操作命令给从服务器
- 主服务器将 replication buffer 缓冲区里所记录的写操作命令发送给从服务器,从服务器执行来自主服务器 replication buffer 缓冲区里发来的命令,这时主从服务器的数据就一致了
1.2 命令传播
- 主从服务器在完成第一次同步后,双方之间就会维护一个 TCP 连接。
- 连接是长连接的,目的是避免频繁的 TCP 连接和断开带来的性能开销
1.3 分摊主服务器的压力
- 从服务器可以有自己的从服务器,可以把拥有从服务器的从服务器当作经理角色,它不仅可以接收主服务器的同步数据,自己也可以同时作为主服务器的形式将数据同步给从服务器
- 通过这种方式,主服务器生成 RDB 和传输 RDB 的压力可以分摊到充当经理角色的从服务器。
1.3.1 增量复制
- 网络断开又恢复后,从主从服务器会采用增量复制的方式继续同步,也就是只会把网络断开期间主服务器接收到的写操作命令,同步给从服务器。
- 主服务器怎么知道要将哪些增量数据发送给从服务器呢
- repl_backlog_buffer ,是一个「环形」缓冲区,用于主从服务器断连后,从中找到差异的数据;
- replication offset ,标记上面那个缓冲区的同步进度,主从服务器都有各自的偏移量,主服务器使用 master_repl_offset 来记录自己「写 」到的位置,从服务器使用 slave_repl_offset 来记录自己「读」到的位置。
- 如果判断出从服务器要读取的数据还在 repl_backlog_buffer 缓冲区里,那么主服务器将采用增量同步的方式;
- 相反,如果判断出从服务器要读取的数据已经不存在 repl_backlog_buffer 缓冲区里,那么主服务器将采用全量同步的方式。
2. 哨兵机制
- 实现主从节点故障转移。它会监测主节点是否存活,如果发现主节点挂了,它就会选举一个从节点切换为主节点,并且把新主节点的相关信息通知给从节点和客户端
- 哨兵其实是一个运行在特殊模式下的 Redis 进程,相当于是"观察者节点",观察的对象是主从节点。
- 主要负责三件事情:监控、选主、通知。
- 哨兵节点通过 Redis 的发布者/订阅者机制,哨兵之间可以相互感知,相互连接,然后组成哨兵集群,同时哨兵又通过 INFO 命令,在主节点里获得了所有从节点连接信息,于是就能和从节点建立连接,并进行监控了。
- 流程:
- 判断主节点下线
- 某个哨兵判定主节点下线(主观下线)后,就会向其他哨兵发起命令,其他哨兵收到这个命令后,就会根据自身和主节点的网络状况,做出赞成投票或者拒绝投票的响应。当赞成数到达预定值后,标记为客观下线
- 选出哨兵leader
- 拿到半数以上的赞成票
- 拿到的票数同时还需要大于等于哨兵配置文件中的 quorum 值
- 哨兵leader进行主从故障转移
- 在已下线主节点(旧主节点)属下的所有「从节点」里面,挑选出一个从节点,并将其转换为主节点,选择的规则:
- 过滤掉已经离线的从节点
- 过滤掉历史网络连接状态不好的从节点
- 将剩下的从节点,进行三轮考察:优先级、复制进度、ID 号。在每一轮考察过程中,如果找到了一个胜出的从节点,就将其作为新主节点
- 让已下线主节点属下的所有「从节点」修改复制目标,修改为复制「新主节点」
- 将新主节点的 IP 地址和信息,通过「发布者/订阅者机制」通知给客户端
- 继续监视旧主节点,当这个旧主节点重新上线时,将它设置为新主节点的从节点
- 在已下线主节点(旧主节点)属下的所有「从节点」里面,挑选出一个从节点,并将其转换为主节点,选择的规则:
- 判断主节点下线
3. 切片集群模式
- 当 Redis 缓存数据量大到一台服务器无法缓存时,就需要使用 Redis 切片集群方案,它将数据分布在不同的服务器上,以此来降低系统对单主节点的依赖,从而提高 Redis 服务的读写性能。
- 采用哈希槽(Hash Slot),来处理数据和节点之间的映射关系。一个切片集群共有 16384 个哈希槽 ,这些哈希槽类似于数据分区,每个键值对都会根据它的 key,被映射到一个哈希槽中
- 根据键值对的 key,按照 CRC16 算法 计算一个 16 bit 的值
- 再用 16bit 值对 16384 取模,得到 0~16383 范围内的模数,每个模数代表一个相应编号的哈希槽
- 哈希槽怎么被映射到具体的 Redis 节点上的呢
- 平均分配: 在使用 cluster create 命令创建 Redis 集群时,Redis 会自动把所有哈希槽平均分布到集群节点上。比如集群中有 9 个节点,则每个节点上槽的个数为 16384/9 个。
- 手动分配: 可以使用 cluster meet 命令手动建立节点间的连接,组成集群,再使用 cluster addslots 命令,指定每个节点上的哈希槽个数。
- 在手动分配哈希槽时,需要把 16384 个槽都分配完,否则 Redis 集群无法正常工作
4. 集群脑裂
4.1 什么是脑裂?
- 由于网络问题,主节点与从节点失联,但是主节点与客户端网络正常,客户端仍然向主节点写数据,但是此时主节点无法同步数据给从节点。哨兵模式选举新的leader作为主节点,这时集群有两个主节点了。
4.2 为什么集群脑裂会导致数据丢失?
- 网络好了之后,哨兵会把原来的主节点降级为从节点,然后从节点会向新的主节点请求数据同步。
- 第一次同步是全量同步,该从节点会清除自己本地的数据,然后再全量同步。这样会导致客户端之前写入的数据都丢失了
4.3 如何解决
- 当主节点发现从节点下线或者通信超时的总数量小于阈值时,那么禁止主节点进行写数据,直接把错误返回给客户端。
- 等到新主库上线时,就只有新主库能接收和处理客户端请求,此时,新写的数据会被直接写到新主库中。而原主库会被哨兵降为从库,即使它的数据被清空了,也不会有新数据丢失。