利用redis-sentinel实现redis主从高可用设计
redis最简单的使用方式就是启动一个redis实例,然后客户端直接连到这个redis实例进行操作,很多业务场景单个redis实例便可以满足内存及请求的需求。然而在实际应用当中,尤其是在线上环境中,可用性非常重要,显然单一的redis实例可用性就很难保证,无论是进程挂了还是机器挂了等等各种原因很容易就会导致单一redis实例不可用。为了提供高可用的能力,redis本身提供了redis-sentinel服务。
redis-sentinel工作方式
详细的redis-sentinel内容可以参考官网的文档sentinel。在这里我们简单的介绍一下:
- redis服务需要主从部署,一个主节点,至少一个从节点
- 至少部署三个redis-sentinel实例
+--------+ +--------+
| Master |---------+---------| Slave |
+--------+ | +--------+
|
+------------+--------------+
| | |
+-----------+ +-----------+ +-----------+
| Sentinel1 | | Sentinel2 | | Sentinel3 |
+-----------+ +-----------+ +-----------+
每个Sentinel都会和Master、Slave以及其它Sentinel通讯,检测各实例的存活情况,如果发现Master实例挂掉了,则各Sentinel之间会协商根据规则选出一个Slave来变为Master。这样就使得redis服务可以继续使用。
从服务端来讲,redis-sentinel引入解决了单个redis实例挂掉之后不能提供服务的问题。但是对客户端来说由于redis-sentinel的引入,可能导致redis的主从切换,因此直接给客户端redis实例地址的话则可能因为主从的切换而导致redis地址不是预期的地址。为了完全的实现高可用我们还需要解决这个问题,那解决这个问题常见的就有以下三种方案:
- 客户端方案
- LVS方案
- 代理方案
客户端方案
在redis客户端中支持redis-sentinel,通过redis-sentinel来发现redis实例。
优点:
- 简化服务端部署,只需部署redis实例和redis-sentinel即可
- 可以在客户端做配置,实现读写分离功能
缺点:
- 需要客户端支持redis-sentinel,很多流行的当前在用的客户端都不支持
- 对于已经使用的现有代码,需要修改业务使用客户端部分的代码
- 一些第三方应用使用redis,而我们可能无法修改这种应用的代码
LVS方案
采用LVS虚IP,在redis-sentinel发生主从切换后也执行虚IP漂移操作。
优点:
- 对客户端完全透明,现有业务代码无需做任何更改
缺点:
- 服务端部署稍微复杂,LVS部署需要root权限
- LVS自身高可用需要有备机,在隔离部署时备机不能被利用,存在资源浪费
- 单一虚IP,只能规定为redis主节点或redis从节点,想要支持读写分离的话得部署多个虚ip,同时需要客户端支持
代理方案
在客户端和redis服务端之间加一层代理,代理通过redis-sentinel来发现redis实例
优点:
- 对客户端完全透明,现有业务代码无需做任何更改
- 可以在代理层做配置,实现读写分离功能
缺点:
- redis服务有性能损失
- 代理自身存在单点问题
方案选择
在实际应用环境中,要选择一种高可用方案除了考虑高可用本身更应考虑以下几点:
- 维护简单,这包括服务的部署、升级、迁移等
- 从现有使用方式迁移到高可用使用方式改造小
因此综合的来看,选择代理方案是一种比较折衷稳妥的方案,原因主要如下:
- 客户端方案,在已有大量服务在运行的情况下,需要全部重新改造一遍,这个难度很大,而且对第三方应用来说可能是无法更改的
- LVS方案,部署麻烦问题比较突出
- 代理方案对后端服务来说,维护起来简单
那么选用代理方案的话,也需要正视它的缺点:
- redis服务性能损失:考察一个服务的性能主要就是两个指标,吞吐和延迟。对于一款高性能的代理来说,吞吐几乎可以做到和redis差不多,即便是有差距的话,获益于代理的无状态特性,可以直接横向扩容来弥补;而对于延迟,从客户端来讲,由于多个中间环节,几乎延迟总会增加,但是从整个业务层面来讲,因为代理的引入而增加的redis请求耗时几乎都可以忽略不计。
- 代理自身的单点问题:要解决这个问题有两个手段,一是再在代理前面架一层LVS,二是提供多个代理,在客户端实现可以访问多个后端。在这两种选择中,推荐第二种,因为代理本身无状态,可以很容易的部署多个代理,通过修改业务客户端代码,实现对代理的负载均衡和失效及恢复。
支持redis-sentinel的代理predixy