Redis——哨兵模式
Redis哨兵
- Redis——哨兵模式
- 1.什么是哨兵机制(Redis Sentinel)
- 1.1 哨兵的作用
- 2.哨兵的运行机制
- 3.故障处理
- redis常见问题汇总
- 1、redis缓存击穿是什么?如何解决?
- 2、redis缓存穿透是什么?如何解决?
- 3、redis缓存雪崩是什么?如何解决?
1.什么是哨兵机制(Redis Sentinel)
Redis Sentinel,即Redis哨兵,在Redis 2.8版本开始引入。哨兵的核心功能是主节点的自动故障转移。
哨兵机制(sentinel)是Redis解决高可用的一种解决方案:它是由一个或者多个sentinel 实例组成的一个sentinel 系统。
1.1 哨兵的作用
master 状态检测;
如果 master 异常,则会进行 master-slave 切换,将其中一个 slave 作为 master,将之前的 master 作为 slave;
master-slave 切换后,master_redis.conf、slave_redis.conf 和 sentinel.conf 的内容都会发生改变,即 master_redis.conf 中会多一行 slaveof 的配置,sentinel.conf 的监控目标会随之调换。
Redis官方文档的描述如下:
监控(Monitoring):哨兵会不断地检查主节点和从节点是否运作正常。
自动故障转移(Automatic failover):当主节点不能正常工作时,哨兵会开始自动故障转移操作,它会将失效主节点的其中一个从节点升级为新的主节点,并让其他从节点改为复制新的主节点。
配置提供者(Configuration provider):客户端在初始化时,通过连接哨兵来获得当前Redis服务的主节点地址。
通知(Notification):哨兵可以将故障转移的结果发送给客户端。
2.哨兵的运行机制
1、哨兵启动
当一个 Sentinel 启动时,会创建连向被监视的主服务器的网络连接。它可以向主服务器发送命令,并从命令回复中获取相关的信息。对于每个被 Sentinel 监视的主服务器,Sentinel 会创建两个连向主服务器的异步网络:
命令连接:用于向主服务器发送命令(包括ping、info),并接受命令回复,用来获取从节点的信息与Sentinel 、Redis节点的状态信息。
订阅连接:用于订阅主服务器的 sentinel:hello 频道,用来和其他Sentinel 实例建立连接。
2、订阅频道
Sentinel实例会向与其连接的节点(包括主从节点)的 sentinel:hello 频道发送消息, 并订阅该频道(SUBSCRIBE sentinel:hello)。这样一来,当后续的
Sentinel实例与主库建立连接,发送消息时,之前订阅该频道的消息就能够收到新实例加入的消息,然后就可以从这个频道直接获取新实例的信息并建立网络连接。
Sentinel 对 sentinel:hello 频道的订阅会一直持续到 Sentinel 与服务器断开连接为止。在此之前Sentinel 会以每两秒一次的频率,通过命令向所有被监视的主服务器和从服务器发送自己的信息及主节点相关的配置。
3、获取服务器信息
Sentinel 向服务器(主从节点都有)发送 INFO 命令,获取主服务器及它的从服务器信息。
(1)获取主服务器信息
Sentinel 默认会以每十秒一次的频率,通过命令连接向被监视的服务器发送 INFO 命令,并通过分析 INFO 命令的回复来获取主服务器的当前信息。
- 主服务自身信息:包括 run_id 域记录的服务器运行 ID,以及 role 域记录的服务器角色
- 主服务的从服务器信息:包括 IP 地址和端口号
(2)获取从服务器信息
当 Sentinel 发现主服务器有新的从服务器出现时,Sentinel 除了会为这个新的从服务器创建相应的实例结构之外,Sentinel 还会创建连接到从服务器的命令连接和订阅连接。
4、检测服务器状态
Sentinel 向 Redis 服务器发送 PING 命令,检查其状态。
每个 Sentinel 节点会以 每秒一次 的频率对 Redis 节点和 其它 的 Sentinel 节点发送 PING 命令,并通过节点的 回复 来判断节点是否在线。哨兵正是通过这个机制得以判断主节点是否下线,这将涉及到2个新的改变,主观下线与客观下线。
注意:一个有效的 PING 回复可以是:+PONG、-LOADING 或者 -MASTERDOWN。如果 服务器 返回除以上三种回复之外的其他回复,又或者在 指定时间 内没有回复 PING 命令, 那么 Sentinel 认为服务器返回的回复 无效(non-valid)。
3.故障处理
在上文中,我们留了2个概念(主观下线与客观下线)没有解释,这一节我们结合哨兵对故障的处理进行讲解。
1、主库下线判定
(1)主观下线
主观下线适用于所有 主节点 和 从节点。 如果最后一次的有效回复 PING 命令的时间超过 down-after-milliseconds 毫秒,则会判定 该节点 为 主观下线(SDOWN)。
注意:主从节点都可能被标记为主观下线,只不过只会对主机节点的客观下线进行下一步判断。在一般情况下, 每个 Sentinel 会以每 10 秒一次的频率,向它已知的所有 主服务器 和 从服务器 发送 INFO 命令。当一个 主服务器 被 Sentinel 标记为 客观下线 时,Sentinel 向 下线主服务器 的所有 从服务器 发送 INFO 命令的频率,会从 10 秒一次改为 每秒一次。
(2)客观下线
客观下线只适用于主节点。当 Sentinel 将一个主服务器判断为主管下线后,为了确认这个主服务器是否真的下线,会向同样监视这一主服务器的其他 Sentinel 询问( 通过sentinel is-master-down-by-addr指令判断),看它们是否也认为主服务器已经下线。
其他哨兵会根据自己和主库的连接情况,做出 Y 或 N 的响应,Y 相当于赞成票,N 相当于反对票。如果赞成票数(这里是2)是大于等于哨兵配置文件中的 quorum 配置项(比如quorum=2), 则可以判定主库客观下线了。当足够数量的 Sentinel 认为主服务器已下线,就判定其为客观下线(ODOWN),并对其执行故障转移操作。
2、哨兵集群的选举
当一个主服务器被判断为客观下线时,监视这个下线主服务器的各个 Sentinel 会进行选举,选举出一个领头的 Sentinel,并由领头 Sentinel 对下线主服务器执行故障转移操作。
哨兵的选举机制使用Raft(Raft算法详解)选举算法:
每个发现服务客观下线的sentinel,都会要求其他sentinel将自己设置成领头。所有的sentinel都有且只有一次将某个sentinel选举成领头的机会(在一轮选举中),一旦选举某个sentinel为领头,不能更改。领头sentinel是先到先得,一旦当前sentinel设置了领头sentinel,以后要求设置sentinel为领头请求都会被拒绝。
成为领头sentinel的条件如下:
- 拿到半数以上的赞成票;
- 拿到的票数同时还需要大于等于哨兵配置文件中的 quorum 值。
当有3 个哨兵时,quorum 设置为 2,那么,任何一个想成为 Leader 的哨兵只要拿到 2 张赞成票,就可以了。
3、新主库的选出
主库被判定为客观下线后,就需要从剩余的从库中选择一个新的主库。
- 过滤列表中所有处于下线或断线状态的从服务器。
- 过滤列表中所有最近五秒没有回复过 Sentinel Leader 的 INFO 命令的从服务器。
- 过滤所有与已下线主服务器连接断开超过 down-after-milliseconds * 10 毫秒的从服务器(down-after-milliseconds 指定了判断主服务器下线所需的时间)。
然后, Sentinel Leader 先选出优先级最高的从服务器;如果优先级一样高,再选择复制偏移量最大的从服务器;如果结果还不唯一,则选出运行 ID 最小的从服务器。
4、故障的转移
在上文中,Sentinel Leader 已经挑选出了新的主节点,接下来就要进行故障转移了。
首先向这个从节点发送 SLAVEOF no one 命令,将其转换为主节点(5.0 中应该是replicaof no one)。然后Sentinel Leader 会向所有从节点发送 SLAVEOF 命令,让所有从节点指向新的主节点并复制新的主节点上的数据,然后通知客户端主节点已更换,最后将旧的主服务器标记为从服务器。当旧的主服务器重新上线,Sentinel Leader 会向它发送 SLAVEOF 命令,让其成为从服务器
最后,需要注意的是,主从切换并不一定就能完成,下面举个例子,Redis 1主4从,5个哨兵,哨兵配置quorum为2,如果3个哨兵故障,此时主节点宕机。
由于quorum=2,所以当一个哨兵判断主库“主观下线”后,询问另外一个哨兵后也会得到同样的结果,2个哨兵都判定“主观下线”,达到了quorum的值,因此,哨兵集群可以判定主库为“客观下线”。
但哨兵不能完成主从切换。哨兵标记主库“客观下线后”,在选举“哨兵领导者”时,一个哨兵必须拿到超过多数的选票(5/2+1=3票)。但目前只有2个哨兵活着,无论怎么投票,一个哨兵最多只能拿到2票,永远无法达到N/2+1选票的结果。
当没有足够数量的 Sentinel 同意 主服务器 下线时, 主服务器 的 客观下线状态 就会被移除。当 主服务器 重新向 Sentinel 的 PING 命令返回 有效回复 时,主服务器 的 主观下线状态 就会被移除。同样,从服务器的主观下线状态在PING命令得到有效回复后移除。
redis常见问题汇总
1、redis缓存击穿是什么?如何解决?
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。和缓存雪崩不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
解决方案
1.设置热点数据永远不过期:使用mutex,缓存失效的时候(判断拿出来的值为空),不是立即去loaddb,而是先使用缓存工具的某些带成功的返回值操作(例如reids的SETNX或者memcache的add)去set一个mutexkey,当返回成功时,在进行load db操作并回设缓存,否则就重试整个get缓存方法。
2、redis缓存穿透是什么?如何解决?
缓存穿透是指缓存和数据库中都没有的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉。
解决方案
接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击。
【推荐】采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力。
3、redis缓存雪崩是什么?如何解决?
缓存雪崩是指缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。
解决方案
1.缓存数据的过期时间进行错开,防止同一时间大量数据过期现象发生。
2.像缓存穿透一样加锁排列。
3.建立备份缓存,设置A的过期时间,不设置B的过期时间,当缓存A失效的时候,读取缓存B,并且更新A的缓存和B的缓存。