原理
主从复制
复制模式
- 全量复制:Master 全部同步到 Slave
- 部分复制:Slave 数据丢失进行备份
问题点: - 同步故障
- 复制数据延迟(不一致)
- 读取过期数据(Slave 不能删除数据)(为什么Slave 不能删除数据就导致了可能读取过期数据呢?)
- 从节点故障
- 主节点故障
- 配置不一致
- maxmemory 不一致:丢失数据
- 优化参数不一致:内存不一致.
- 避免全量复制
- 选择小主节点(分片)、低峰期间操作.
- 如果节点运行 id 不匹配(如主节点重启、运行 id 发生变化),此时要执行全量复制,应该配合哨兵和集群解决.
- 主从复制挤压缓冲区不足产生的问题(网络中断,部分复制无法满足),可增大复制缓冲区( rel_backlog_size 参数).
- 复制风暴
哨兵机制
节点下线
- 主观下线
- 即 Sentinel(即哨兵) 节点对 Redis 节点失败的主观判断,超出超时时间认为 Master 已经宕机。
- Sentinel 集群的每一个 Sentinel 节点会定时对 Redis 集群的所有节点发心跳包检测节点是否正常。如果一个节点在
down-after-milliseconds
时间内没有回复 Sentinel 节点的心跳包,则该 Redis 节点被该 Sentinel 节点主观下线。
- 客观下线
- 所有 Sentinel 节点对 Redis 节点失败要达成共识,即超过 quorum 个统一。
- 当节点被一个 Sentinel 节点记为主观下线时,并不意味着该节点肯定故障了,还需要 Sentinel 集群的其他 Sentinel 节点共同判断为主观下线才行。
- 该 Sentinel 节点会询问其它 Sentinel 节点,如果 Sentinel 集群中超过 quorum 数量的 Sentinel 节点认为该 Redis 节点主观下线,则该 Redis 客观下线(民主的就是客观的吗哈哈,不过确实是进步,但最终判断的时候还是集中意见)。
Leader选举和故障转移
- 选举出一个 Sentinel 作为 Leader:集群中至少有三个 Sentinel 节点,但只有其中一个节点可完成故障转移.通过以下命令可以进行失败判定或领导者选举。
- 选举流程
如果需要从redis集群选举一个节点为主节点,首先需要从Sentinel集群中选举一个Sentinel节点作为Leader。每一个Sentinel节点都可以成为Leader,当一个Sentinel节点确认redis集群的主节点主观下线后,会请求其他Sentinel节点要求将自己选举为Leader。被请求的Sentinel节点如果没有同意过其他Sentinel节点的选举请求,则同意该请求(选举票数+1),否则不同意。如果一个Sentinel节点获得的选举票数达到Leader最低票数(quorum和Sentinel节点数/2+1的最大值),则该Sentinel节点选举为Leader;否则重新进行选举。如果此过程有多个 Sentinel 节点成为领导者,则等待一段时间再重新进行选举。
当Sentinel集群选举出Sentinel Leader后,由Sentinel Leader从redis从节点中选择一个redis节点作为主节点:
- 过滤故障的节点
- 选择优先级slave-priority最大的从节点作为主节点,如不存在则继续
- 选择复制偏移量(数据写入量的字节,记录写了多少数据。主服务器会把偏移量同步给从服务器,当主从的偏移量一致,则数据是完全同步)最大的从节点作为主节点,如不存在则继续
- 选择runid(redis每次启动的时候生成随机的runid作为redis的标识)最小的从节点作为主节点
Sentinel 集群运行过程中故障转移完成,所有 Sentinel 又会恢复平等。Leader 仅仅是故障转移操作出现的角色。
**为什么Sentinel集群至少3节点:**一个Sentinel节选举成为Leader的最低票数为quorum和Sentinel节点数/2+1的最大值,如果Sentinel集群只有2个Sentinel节点,则:
Sentinel节点数/2 + 1
= 2/2 + 1
= 2
即Leader最低票数至少为2,当该Sentinel集群中由一个Sentinel节点故障后,仅剩的一个Sentinel节点是永远无法成为Leader。由此公式(n/2+1=n-最大故障节点数)可以推导出,Sentinel集群允许1个Sentinel节点故障则需要3个节点的集群;允许2个节点故障则需要5个节点集群。
定时任务
- 每 1s 每个 Sentinel 对其他 Sentinel 和 Redis 执行 ping,进行心跳检测。
- 每 2s 每个 Sentinel 通过 Master 的 Channel 交换信息(pub - sub)。
- 每 10s 每个 Sentinel 对 Master 和 Slave 执行 info,目的是发现 Slave 节点、确定主从关系。
分布式集群(Cluster)
集中式
将集群元数据(节点信息、故障等等)集中存储在某个节点上。
- 优势
- 元数据的更新读取具有很强的时效性,元数据修改立即更新
- 劣势
- 数据集中存储
Gossip
Gossip 协议 (比特币就是用的该协议)
寻址分片
hash取模
hash槽(目前用的是这个hash取模算法)
- CRC16(key)%16384
其他hash取模算法:
hash(key)%机器数量- 问题
- 机器宕机,造成数据丢失,数据读取失败
- 伸缩性
一致性hash
问题
- 一致性哈希算法在节点太少时,容易因为节点分布不均匀而造成缓存热点的问题。
- 解决方案
- 可以通过引入虚拟节点机制解决:即对每一个节点计算多个 hash,每个计算结果位置都放置一个虚拟节点。这样就实现了数据的均匀分布,负载均衡。
- 解决方案
使用场景
热点数据
存取数据优先从 Redis 操作,如果不存在再从文件(例如 MySQL)中操作,从文件操作完后将数据存储到 Redis 中并返回。同时有个定时任务后台定时扫描 Redis 的 key,根据业务规则进行淘汰,防止某些只访问一两次的数据一直存在 Redis 中。
例如使用 Zset 数据结构,存储 Key 的访问次数/最后访问时间作为 Score,最后做排序,来淘汰那些最少访问的 Key。
会话维持 Session
话维持 Session 场景,即使用 Redis 作为分布式场景下的登录中心存储应用。每次不同的服务在登录的时候,都会去统一的 Redis 去验证 Session 是否正确。但是在微服务场景,一般会考虑 Redis + JWT 做 Oauth2 模块。
其中 Redis 存储 JWT 的相关信息主要是留出口子(口子是啥?),方便以后做统一的防刷(防刷是啥意思?)接口,或者做登录设备限制等。
分布式锁 SETNX
命令格式:SETNX key value
:当且仅当 key 不存在,将 key 的值设为 value。若给定的 key 已经存在,则 SETNX 不做任何动作。
- 超时时间设置:获取锁的同时,启动守护线程,使用 expire 进行定时更新超时时间。如果该业务机器宕机,守护线程也挂掉,这样也会自动过期。如果该业务不是宕机,而是真的需要这么久的操作时间,那么增加超时时间在业务上也是可以接受的,但是肯定有个最大的阈值。
- 但是为了增加高可用,需要使用多台 Redis,就增加了复杂性,就可以参考 Redlock。
表缓存
Redis 缓存表的场景有黑名单、禁言表(操,很有“用处”)等。访问频率较高,即读高。根据业务需求,可以使用后台定时任务定时刷新 Redis 的缓存表数据。
消息队列 list
主要使用了 List 数据结构。
List 支持在头部和尾部操作,因此可以实现简单的消息队列。
- 发消息:在 List 尾部塞入数据。
- 消费消息:在 List 头部拿出数据。
同时可以使用多个 List,来实现多个队列,根据不同的业务消息,塞入不同的 List,来增加吞吐量。
计数器 string
主要使用了 INCR、DECR、INCRBY、DECRBY 方法。
INCR key:给 key 的 value 值增加一
DECR key:给 key 的 value 值减去一
其他
缓存崩溃(如缓存雪崩)出现后应对
- 事前:Redis 高可用,主从 + 哨兵,Redis Cluster,避免全盘崩溃。
- 事中:本地 ehcache 缓存 + hystrix 限流 & 降级,避免数据库承受太多压力。
- 事后:Redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。