Redis 集群以及应用
1 Redis 集群
1.1 主从复制
通过持久化功能,Redis保证了即使在服务器重启的情况下也不会损失(或少量损失)数据,因为持久化会把内存中数据保存到硬盘上,重启会从硬盘上加载数据。
但是由于数据是存储在一台服务器上的,如果这台服务器出现硬盘故障等问题,也会导致数据丢失。为了避免单点故障,通常的做法是将数据库复制多个副本以部署在不同的服务器上,这样即使有一台服务器出现故障,其他服务器依然可以继续提供服务。为此, Redis 提供了复制(replication)功能,可以实现当一台数据库中的数据更新后,自动将更新的数据同步到其他数据库上。
在复制的概念中,数据库分为两类,一类是主数据库(master),另一类是从数据库(slave)。主数据库可以进行读写操作,当写操作导致数据变化时会自动将数据同步给从数据库。而从数据库一般是只读的,并接受主数据库同步过来的数据。一个主数据库可以拥有多个从数据库,而一个从数据库只能拥有一个主数据库。
1.1.1 复制模式
全部复制:Master 全部同步到 Slave
部分复制:Slave 数据丢失进行备份
1.1.2 问题点
- 同步故障:
- 复制数据延迟(不一致)
- 读取过期数据(Slave 不能删除数据)
- 从节点故障
- 主节点故障
- 配置不一致:
- maxmemory 不一致:丢失数据
- 优化参数不一致:内存不一致.
- 避免全量复制:
- 选择小主节点(分片)、低峰期间操作.
- 如果节点运行 id 不匹配(如主节点重启、运行 id 发送变化),此时要执行全量复制,应该配合哨兵和集群解决.
- 主从复制挤压缓冲区不足产生的问题(网络中断,部分复制无法满足),可增大复制缓冲区( rel_backlog_size 参数).
- 复制风暴
1.2 哨兵机制
当主数据库遇到异常中断服务后,开发者可以通过手动的方式选择一个从数据库来升格为主数据库,以使得系统能够继续提供服务。然而整个过程相对麻烦且需要人工介入,难以实现自动化。 为此,Redis 2.8中提供了哨兵工具来实现自动化的系统监控和故障恢复功能。
哨兵的作用就是监控redis主、从数据库是否正常运行,主出现故障自动将从数据库转换为主数据库。
配置哨兵监控一个系统时,只需要配置其监控主数据库即可,哨兵会自动发现所有复制该主数据库的从数据库。
当redis集群的主节点故障时,Sentinel集群将从剩余的从节点中选举一个新的主节点,有以下步骤:
- 故障节点主观下线
- 故障节点客观下线
- Sentinel集群选举Leader
- Sentinel Leader决定新主节点
1.2.2 节点下线
主观下线
- 即 Sentinel 节点对 Redis 节点失败的偏见,超出超时时间认为 Master 已经宕机。
- Sentinel 集群的每一个 Sentinel 节点会定时对 Redis 集群的所有节点发心跳包检测节点是否正常。如果一个节点在
down-after-milliseconds
时间内没有回复 Sentinel 节点的心跳包,则该 Redis 节点被该 Sentinel 节点主观下线。
客观下线
所有 Sentinel 节点对 Redis 节点失败要达成共识,即超过 quorum 个统一。
当节点被一个 Sentinel 节点记为主观下线时,并不意味着该节点肯定故障了,还需要 该 Sentinel 节点询问其它 Sentinel 节点,如果 Sentinel 集群中超过 quorum 数量的 Sentinel 节点认为该 Redis 节点主观下线,则该 Redis 客观下线。
如果客观下线的redis节点是从节点或者是Sentinel节点,则操作到此为止,没有后续的操作了;如果客观下线的redis节点为主节点,则开始故障转移,从从节点中选举一个节点升级为主节点。
1.2.3 Sentinel集群选举Leader
如果需要从redis集群选举一个节点为主节点,首先需要从Sentinel集群中选举一个Sentinel节点作为Leader。
选举出一个 Sentinel 作为 Leader:集群中至少有三个 Sentinel 节点,但只有其中一个节点可完成故障转移.
选举流程:
-
每一个Sentinel节点都可以成为Leader,当一个Sentinel节点确认redis集群的主节点主观下线后,会请求其他Sentinel节点要求将自己选举为Leader。
-
被请求的Sentinel节点如果没有同意过其他Sentinel节点的选举请求,则同意该请求(选举票数+1),否则不同意。
-
如果一个Sentinel节点获得的选举票数达到Leader最低票数(quorum和Sentinel节点数/2+1的最大值),则该Sentinel节点选举为Leader;否则重新进行选举。
-
如果此过程有多个 Sentinel 节点成为领导者,则等待一段时间再重新进行选举。
1.2.4 Sentinel Leader决定新主节点
当Sentinel集群选举出Sentinel Leader后,由Sentinel Leader从redis从节点中选择一个redis节点作为主节点:
- 过滤故障的节点
- 选择优先级slave-priority最大的从节点作为主节点,如不存在则继续
- 选择复制偏移量(数据写入量的字节,记录写了多少数据。主服务器会把偏移量同步给从服务器,当主从的偏移量一致,则数据是完全同步)最大的从节点作为主节点,如不存在则继续
- 选择runid(redis每次启动的时候生成随机的runid作为redis的标识)最小的从节点作为主节点
1.2.5 故障转移
- Sentinel 选出一个合适的 Slave 作为新的 Master(slaveof no one 命令)。
- 向其余 Slave 发出通知,让它们成为新 Master 的 Slave( parallel-syncs 参数)。
- 等待旧 Master 复活,并使之称为新 Master 的 Slave。
- 向客户端通知 Master 变化。
Sentinel 集群运行过程中故障转移完成,所有 Sentinel 又会恢复平等。Leader 仅仅是故障转移操作出现的角色。
1.2.6 为什么Sentinel集群至少3节点
一个Sentinel节选举成为Leader的最低票数为quorum和Sentinel节点数/2+1的最大值,如果Sentinel集群只有2个Sentinel节点,则
Leader最低票数至少为2,当该Sentinel集群中由一个Sentinel节点故障后,仅剩的一个Sentinel节点是永远无法成为Leader。
也可以由此公式可以推导出,Sentinel集群允许1个Sentinel节点故障则需要3个节点的集群;允许2个节点故障则需要5个节点集群。
1.2.7 读写分离
定时任务
- 每 1s 每个 Sentinel 对其他 Sentinel 和 Redis 执行 ping,进行心跳检测。
- 每 2s 每个 Sentinel 通过 Master 的 Channel 交换信息(pub - sub)。
- 每 10s 每个 Sentinel 对 Master 和 Slave 执行 info,目的是发现 Slave 节点、确定主从关系。
1.3 分布式集群(Cluster)
从主从-哨兵-集群可以看到redis的不断完善;主从复制是最简单的节点同步方案无法主从自动故障转移。哨兵可以同时管理多个主从同步方案同时也可以处理主从自动故障转移,通过配置多个哨兵节点可以解决单点网络故障问题,但是单个节点的性能压力问题无法解决。 集群解决了前面两个方案的所有问题。
使用集群,只需要将每个数据库节点的cluster-enable配置打开即可。每个集群中至少需要三个主数据库才能正常运行。
即使使用哨兵,redis每个实例也是全量存储,每个redis存储的内容都是完整的数据,浪费内存且有木桶效应。为了最大化利用内存,可以采用集群,就是分布式存储。即每台redis存储不同的内容。
集群至少需要3主3从,且每个实例使用不同的配置文件,主从不用配置,集群会自己选。
特性
-
Redis-Cluster采用无中心结构
集群的其中一个主要设计目标是达到线性可扩展性(linear scalability)。
每个节点都和其它节点通过互ping保持连接,每个节点保存整个集群的状态信息,可以通过连接任意节点读取或者写入数据
(甚至是没有数据的空节点)。 -
只有当集群中的大多数节点同时fail整个集群才fail
一般情况下是集群当中超过一半以上的节点fail的时候,集群才会fail。
Redis提供一定程度的可用性,可以在某个节点宕机或者不可达的情况下继续处理命令.(只要集群中大多数Master可达、且失效的Master至少有一个Slave可达,即集群非Fail状态,集群都是可用) -
整个集群有16384个slot
hash槽(寻址分片):Redis 集群的分片特征在于将键空间分拆了16384个槽位,每一个节点负责其中一些槽位。
当需要在 Redis 集群中放置一个key-value 时,根据 CRC16(key) mod 16384的值,决定将一个key放到哪个桶中。读取一个key时也是相同的算法。
还有hash取模、一致性hash(一致性哈希算法在节点太少时,容易因为节点分布不均匀而造成缓存热点的问题。解决方案:可以通过引入虚拟节点机制解决。即对每一个节点计算多个 hash,每个计算结果位置都放置一个虚拟节点。这样就实现了数据的均匀分布,负载均衡。) -
当主节点fail时从节点会升级为主节点
fail的主节点online之后自动变成了从节点
2 使用场景
-
热点数据
存取数据优先从 Redis 操作,如果不存在再从文件(例如 MySQL)中操作,从文件操作完后将数据存储到 Redis 中并返回。同时有个定时任务后台定时扫描 Redis 的 key,根据业务规则进行淘汰,防止某些只访问一两次的数据一直存在 Redis 中。 -
会话维持 Session
使用 Redis 作为分布式场景下的登录中心存储应用。每次不同的服务在登录的时候,都会去统一的 Redis 去验证 Session 是否正确。 -
分布式锁 SETNX
命令格式:
SETNX key value
:当且仅当 key 不存在,将 key 的值设为 value。若给定的 key 已经存在,则 SETNX 不做任何动作。
- 超时时间设置:获取锁的同时,启动守护线程,使用 expire 进行定时更新超时时间。如果该业务机器宕机,守护线程也挂掉,这样也会自动过期。如果该业务不是宕机,而是真的需要这么久的操作时间,那么增加超时时间在业务上也是可以接受的,但是肯定有个最大的阈值。
- 但是为了增加高可用,需要使用多台 Redis,就增加了复杂性,就可以参考 Redlock.
- 表缓存
Redis 缓存表的场景有黑名单、禁言表等。访问频率较高,即读高。根据业务需求,可以使用后台定时任务定时刷新 Redis 的缓存表数据。