前言
Redis 作为最常用的 key-value 服务,一直为我们带来了高性能的保障。但程序嘛,总不可能一直运行下去,而我们所要做的就是将这些风险降到最低。
所以,高可用也是 Redis 必然要考虑的了,而随着 Redis 的广泛使用,市面上也出现了有很多高可用方案。今天,就来好好认识下这些方案,或许也可以为我们自己的程序带来灵感。
高可用机制
Redis 的高可用从总体上来讲是通过 冗余 + 故障转移 来实现的,而对于冗余和故障转移又可以细化为:全部冗余或部分冗余;手动转移或自动转移。
全部冗余+手动转移的方案就是我们最熟悉的主从模式了;当手动转移变为自动转移时即哨兵模式。最后部分冗余 + 自动转移则是集群模式。
由于 Redis 不像 mysql,在数据的完整性、一致性上是没有比较好的保障的,所以当我们在使用高可用方案时,对数据的一致性就期望不了那么高了,这是需要提前注意的。
主从模式
主从模式在高可用方案中是最常用的一种。往往我们会在不同的机器上部署着同一 Redis 程序。在这多台机器里,我们会选择一个节点作为主节点,它负责数据的写入。其他节点作为从节点,定时的和主节点同步数据。一旦主节点不能使用了,那么就可以在从节点中挑选一个作为主节点,重新上岗服务。
主从模式往往还能进行读写分离,将读取数据的压力分散到多个从节点上。
在 Redis 进行主从数据同步时,会执行 bgsave 命令以生成 RDB 文件,同时会在缓冲区记录增量的命令。
当 Redis 将 RDB 文件同步给 Slave 后,会再次的将缓冲区的增量命令发送给从节点,从节点接收到这些数据后,就可以恢复到内存里了。
从节点除了恢复数据外,它还维护了一个复制偏移量,表示主节点向从节点传递命令的字节总数。这个复制偏移量会定时同步给主节点。
而主节点也有属于自己的一个写入偏移量,有了这 2 个参数后,主节点就知道要进行部分复制还是要全量复制了。
主从模式在出现故障时,需要人为进行干预,而且从节点一多,主节点的同步压力就会很大了。
哨兵模式
上面的主从模式需要人工的进行故障节点切换,这种方式对于追求完美的程序员来说,肯定是不够的。所以有了自动切换的哨兵模式。
哨兵模式主要实现了下面几个功能:
- 监控:不断的检测主从节点是否能正常工作。
- 自动转移故障:当某个 master 不能正常工作时,Sentinel 会启动一个故障转移过程,将其中的一个副本提升为 master,并通知其他从节点对应新的 master 相关信息。
- 通知:当某个节点出问题时,会告知所有节点。如果是新的主节点被选举出来,还会告知已连接过来的客户端程序关于主节点新的地址。
在哨兵模式中,会有一个或多个哨兵程序,对当前的 Redis 集群进行监控。哨兵服务之间通过 gossip 协议进行通信。当需要进行故障转移时,会通过选举算法,选出一个 leader 来主导过程。
而这也意味着,Sentinel 程序不能选举出 leader 的话,则不能继续执行后续动作了。包括客户端的请求,也会被阻塞住。
集群
主从模式和哨兵模式都会在多台机器中存储着同一份数据,这样对于内存的利用率并不高。如果能够将数据分散到各个节点上,同时配上主从模式,那么就能高效使用内存了。集群就是这么个机制。
Redis 的集群采用了哈希槽的概念,总共会有 16384 个哈希槽。这些哈希槽会被分配到各个节点上,比如:
- 节点 1 分配了 0 至 5500 的哈希槽。
- 节点 2 分配了 5501 至 11000 的哈希槽。
- 节点 3 分配了 11001 至 16384 的哈希槽。
当有 key 过来时,Redis 会对其进行 CRC16(key) % 16384 的运算,看当前的 key 要分散到哪个哈希槽上,再根据当前的哈希槽定位到对应的节点上。这样就完成了一次 key-value 的存储了。
读取也是按这规则来,不同的是,如果运算结果所对应的节点不在当前节点上,则会转发给对应的节点去处理。
当有节点进行新增或删除时,会重新划分这些哈希槽,当然,影响的只会是周围节点,不会造成整个集群不可用。
在这些节点背后还有属于它们的从节点,一旦主节点不可用,那么这些从节点就会被启用,以保证系统的正常运行。
总结
从主从到哨兵再到集群,它们的实施难度是从易到难。一般对于大多数项目来讲,主从模式就足够应付了。如果并发量比较高,数据量也很大,那再来考虑集群。毕竟,项目架构是在不断演变的,往往有了具体的使用场景,好的方案才能发挥出对应的价值。