如何实现Redis的高可用
Redis的高可用主要是通过哨兵和集群来实现。
哨兵
哨兵(Sentinel):哨兵是一种分布式的架构,它包含若干哨兵节点和数据节点。每个哨兵节点都会对其他的哨兵节点和数据节点进行监控。当发现节点不可达时,哨兵节点会与其他哨兵节点进行协商,当多数哨兵节点都认为该节点不可达的时候,就会将该节点做下线标识。如果被标识的是Master节点,哨兵节点会进行主节点选举,选举出一个新的Master节点,并且通知给应用方。并且哨兵架构还具有自动故障恢复的功能,在节点被标识以后,进入主观下线状态,会进行客观下线检测。如果在指定的时间内,检测到了被标识节点的心跳,则主观下线状态被取消。
一组哨兵可以监控一个主节点,也可以监控多个主节点。
哨兵架构下如何实现负载均衡和读写分离
哨兵架构下,客户端可以由哨兵节点中获取其所有的slave节点的信息,可以由此实现读写分离和负载均衡。
哨兵架构下如何感知节点变化,更新节点信息
哨兵架构下,当主节点被哨兵节点做了下线标识,并由哨兵节点选举出新的主节点后,会向名为switch-master的频道发布节点变更的通知信息,订阅了该频道的客户端可以接受这个通知,以进行获取新的主节点信息。
也可以由客户端主动的去查询当前的主节点信息,如果主节点信息发生了更改,则更新连接。
集群
Redis使用虚拟槽的机制实现数据分片。它将所有的数据根据key的Hash函数(CRC16)映射到0-16383个虚拟槽中。 这种方案由如下优点:
- 数据是和虚拟槽进行映射的,这种方案解耦了数据和每个节点间的关系,每个节点只需要维护节点和虚拟槽的映射关系,而不需要维护节点和数据的关系了,大大降低了在线伸缩的难度。在增加节点时和删除节点时,只需要维护每个节点和虚拟槽的映射关系即可。
- Hash可以比较均衡地将数据分配到各个虚拟槽中,而每个节点自身又维护一定数量的虚拟槽,因此数据比较均衡地被分散到了各个节点中。
也有一些缺点:
- Redis的事务只支持单节点的事务,不支持分布式事务。
- 批量操作处于不同节点上的数据,不被支持。
- 大key的数据可能被分散到多个节点中,这会影响数据的一致性,是不被允许的。
- 集群不支持多数据库,单机下的Redis可以支持16个数据库,而以集群进行部署以后,只支持使用DB0。
- 复制结构只支持一层的主从复制,而不支持嵌套树状复制结构。
Redis的主从复制是如何实现的
主要是利用psync命令来完成的。Redis的主从复制分为全量复制和增量复制。psync命令携带两个重要的参数,RUNID和OFFSET。RUNID为上次复制的主库的RUNID,主库的RUNID每次运行时都会重新随机生成,并且是唯一的。OFFSET是记录的复制的偏移量。根据psync命令携带的RUNID和OFFSET的值来决定进行全量复制,还是增量复制。
全量复制
首先slave向Master发送psync命令。如果psync命令携带的RUNID是空的,OFFSET=-1则进行全量复制。
然后主库会进行BGSAVE,得到RDB文件,主库会将在BGSAVE期间进行的写命令写入缓冲区,在BGSAVE结束以后将该RDB文件、缓冲区中的命令、以及主库的信息:如RUNID等发送给从库。缓冲区中的命令会存储到从库的AOF日志中。
然后从库删除旧数据,加载RDB文件,开启AOF,进行数据的恢复。
增量复制
在全量复制结束后,主从库数据的一致性主要依赖于增量复制来实现。 要明白增量复制的原理,我们首先要明白,主库进行写操作的时候,会将写命令存储在一个环形缓冲区中。当环形缓冲区写满,则会抛弃缓冲区中存在最久的命令。
过程如下:
- 从库发送psync命令给主库,携带RUNID和OFFSET。
- 若参数校验成功,则开始增量复制,主库将环形缓冲区的命令发送给从库,从库将这些命令写入AOF文件中,并在内存中执行这些文件中的指令,实现数据同步。
若发生以下的情况则会校验失败,转为进行全量复制:
- psync命令携带的RUNID和主库的RUNID不一致。说明主库发生了变更,需要改为全量复制。
- OFFSET指向环形缓冲区以外的位置,需要进行全量复制。