Redis如何实现高可用架构?
作者:程序员马丁
在线博客:https://open8gu.com
大话面试,技术同学面试必备的八股文小册,以精彩回答应对深度问题,助力你在面试中拿个offer。
回答话术
Redis 官方提供了三种分布式部署模式:
- 主从模式:通过
slaveof
命令可以让任意的节点成为对应的节点的从节点,主节点通过主从复制机制向从主节点同步数据。其中,主节点提供读写功能,而从节点只提供读功能。当主节点出现故障时,需要手动进行主从切换完成故障转移。 - Sentinel 哨兵模式:哨兵模式是主从的增强版本,它在主从模式的基础上添加了哨兵节点,哨兵节点通过心跳监控节点健康状态,并在主节点出现故障后,自动通过投票机制选举出新的主节点并完成故障转移。不过哨兵本身也需要做保证高可用,因此一般也会同时部署多个哨兵节点组成哨兵集群。
- Cluster 集群模式:Redis Cluster 一般也叫分片集群,它是 Redis 提供的一个去中心化的集群部署方案。它将全部数据划分为 16394 个哈希槽,由集群中的每个节点负责其中的一部分槽位,使用时 Key 将通过哈希取模并最终指派到某个特定的槽位。在集群中,不同的 Redis 节点间通过 Gossip 协议与其他节点保持通信,每个主节点都将可以具备多个从节点,从节点不提供服务,当主节点出现故障时将会自动投票选举出新的主节点,并且完成故障转移。
此外,在 Redis Cluster 之前,也有一些基于中间代理路由或客户端路由的 Redis 的解决方案,比如 Jedis 自带的 ShardedJedis,或者推特的 Twemproxy。
问题详解
1. 主从模式
当 Redis 的访问量以及数据量随着随着业务规模一起扩大,单机部署的一些问题就逐渐体现出来,比如:
- 服务器一旦宕机,所有 Redis 服务都不可用。
- 读写请求全打到单个 Redis 实例上,会遇到性能瓶颈。
为了解决这些问题,Redis 中提供了主从复制(replication)的机制。简单的来说,当我们有多个 Redis 实例时,它们会被划分为两类节点:
- Master 主节点:负责处理客户端的读写请求,以及数据的修改操作。
- Slave 从节点:通过复制主节点的数据,提供备份和读取服务。
一个主节点加一个从节点就可以构成一个最小规模的主从结构,其中,从节点还可以再拥有自己的从节点,从而更进一步的提高容灾能力。
相对单机部署,主从模式的优点十分的明显:
- 配置和使用非常简单。
- 可以做到读写分离,提高了性能。
- 提供了容灾备份能力。
不过它也有不足之处:
- 没有自动故障转移,主节点故障时需要手动切换,操作起来比较麻烦。
- 主节点需要承担所有的写入操作,还是会面临性能瓶颈。
- 数据没有做到分片存储,因此依然要面对单机数据容量限制的问题。
关于主从复制的原理,请参见:✅ Redis 主从复制的原理是什么?
2. 哨兵模式
Sentinel 哨兵模式是 Redis 提供的一种用于监控和管理 Redis 主从节点的机制。
顾名思义,它在主从节点的基础上额外增加了一类哨兵节点,它是一个特殊的 Redis 实例,会定期向主节点和从节点发送心跳检测。
当检测到主节点不可用时,哨兵会自动通过选举机制,将一个从节点升级为新的主节点,并且更新所有其他从节点的配置,使它们成为新主节点的从节点。
不过,哨兵模式同样要面临单点故障问题,因此为了保证哨兵节点的高可用,我们往往需要部署多个哨兵节点让他们组成哨兵集群。
哨兵模式弥补了普通的主从模式不支持自动故障转移的缺点,不过它的配置较复杂,管理起来也不方便,最重要的是,除此之外,它依然没有解决读写请求全部打到主节点带来的性能问题,以及单机部署造成的数据容量限制的问题。
关于哨兵模式,请参见:✅ 什么是 Redis-Sentinel 集群?
3. 集群模式
上面两种方案实际上都只做到了“多从”,而如果想要提高写入性能,并突破单机数据容量的限制,那还需要做到“多主”,对此, Redis 提供了分片集群(Cluster)作为解决方案。
在 Redis 集群中,划分了 16384(2^14)个哈希槽,所有的 Key 在计算后,都必定会落到这些槽位中的任意一个,集群中的每个 Redis 实例都将管理其中一部分的槽位,每个节点与它负责的哈希槽,我们一般称其为“分片”。
当客户端向某个 Redis 节点发起请求时,将会通过哈希函数计算当前请求的 Key 落在哪一个分片,如果该 Key 不属于这个实例管理的分片,就会被转发到对应的分片上的 Redis 节点。 其中,分片的大小不一定要一致,如果有的 Redis 实例性能强一点,或者内存多一些,就可以分配给他更多的槽位。
这种方案中的 Redis 实例同样可以有多个从节点,不过它们只单纯作为备份,而不像传统的主从架构那样提供读服务。
它的特点是:
- 去中心化:整个集群中的所有节点 redis 节点都彼此直接通信,每个节点都存储了集群的哈希槽分配信息。
- 客户端直连:客户端不需要任何代理层,连接上集群任意一个节点即可访问整个集群的数据。
- 自动故障转移:集群中任意节点下线以后,将会通过投票完成主从切换。
分片集群在保证高可用的前提下,实现了数据分片存储与负载均衡,不过它也有对应的缺点:
- 管理难度高:由于是去中心化集群,因此你无法集中的管理节点,尤其是在扩缩容或者故障转移这种场景下,更是很难预测具体的行为。
- 客户端实现较复杂:为了避免多次转发带来的性能问题,因此客户端需要考虑缓存分片配置,同时还要考虑到集群状态调整后缓存数据一致性的问题……
- 不支持批量操作和事务:批量操作的 Key 如果不是全部属于同一个分片,就需要分成多次请求完成,因此也就无法保证 Redis 的事务。
关于 Cluster 集群更多具体的内容,请参见:✅ 什么是 Redis-Cluster 集群?
4. 其他方案
有意思的是,由于 Redis 官方的集群方案出现的比较晚,因此在它之前,行业内已经出现一些其它的解决方案,不过大体都是通过哈希算法对 Key 取模,从而将对 Key 的请求转发到集群中的各个节点。
总的来说,相比起 Redis Cluster,它们的主要区别在进行路由的层级不同,而这些方案大体可以分为两类:
- 客户端路由:直接在客户端就完成 Key 的路由。它最大的优点是方便,完全不需要引入其他的中间层,不过对应的,面对集群进行故障转移或动态扩缩容的情况就会比较难以处理。
- 代理层路由:在客户端和 Redis 集群加一个中间的代理层,在代理层的中间件完成路由。这个方案的优点是代理层的中间件本身能起到管理者的作用,因此它能更好的管理集群,并且在面对故障转移和动态扩缩容时能更加快速的作出响应。它最大缺点是成本较高,因为代理层本身也是需要维护成本,并且为了保障高可用同样需要进行集群部署。一般各大公有云商会考虑采用这种方案。