前面说到了主从同步存在高可用和分布式问题,哨兵机制解决了高可用问题,而集群就是终极方案,一举解决高可用 和分布式问题。
1.数据分区:数据分区或称数据分片是集群最核心的功能,集群将数据分散到多个节点,一方面突破了redis单机内存限制,存储容量大大提升;另一个方面每个主节点都可以对外提供读服务和写服务,大大提高了集群的响应能力。
2.高可用:集群支持主从复制和主节点的故障转移,当任意节点发生故障时候,集群任可对外提供服务。
.
分布式存储中,要把数据映射到多个节点,常见的数据分区规则有三种
2.·方案二:一致性哈希分区
将整个Hash值空间组织成一个虚拟的圆环,然后将缓存的节点的IP或者主机名做Hash取值后,房子在圆环上面,当我们需要确定某一个Key需要存取到哪一个节点时候,先对这个Key做同样的Hash取值,确定在圆环上的位置,然后按照顺时针方向在环上行走,遇到的第一个节点就是要访问的节点。这种方式相比节点取余最大的好处就是加入和删除节点只影响哈希环中相邻的节点,对其他节点无影响
问题:
3.方案三:虚拟槽分区
这个方案在一致性哈希分区基础上引入 虚拟节点的概念,Redis集群使用的便是该方案,其中虚拟节点称为槽,槽是介于数据和实际节点之间的虚拟概念,每个实际节点包含一定数量的槽,每个槽的包含哈希值在一定范围内的数据。
在使用了槽的一致性哈希分区中,槽是数据管理和迁移的基本单位。槽解耦了数据和实际节点 之间的关系,增加
或删除节点对系统的影响很小。
Redis集群通过数据分区来实现数据的分布式存储,通过自动故障转移实现高可用。
集群创建
数据分区是在集群创建的时候完成的
设置节点
Redis集群一般是由多个节点组成,节点数据量至少为6个才能保证组成完成高可用的集群,每个节点需要开启配置cluster-enabled yes
节点握手
节点握手是指一批运行在集群模式下的节点通过Gossip协议彼此通信,达到感知对方的过程。节点握手是集群彼此通信的第一步 ,由客户端发起命令cluster meet{ip}{port}。完成节点握手之后,一个个的Redis节点就组成了一个
多节点的集群。
分配槽
Redis集群把所有的数据映射到16384个槽中,每个节点对应若干个槽,只有当节点分配了槽,才能响应和这些槽关键的命令,通过 cluster addslots命令为节点分配槽。
故障转移
Redis集群的故障转移和哨兵的故障转移类似,但是Redis集群中所有的节点都要承担状态维护的任务
故障发现
Redis集群内节点通过ping/pong消息实现节点通信,集群中每个节点都会定期向其他节点发送ping消息,接收节点回复pong 消息作为响应。如果在cluster-node-timeout时间内通信一直失败,则发送节 点会认为接收节点存在故障,把接收节点标记为主观下线(pfail)状态。
当某个节点判断另一个节点主观下线后,相应的节点状态会跟随消息在集群内传播。通过Gossip消息传播,集群内节点不断收集到故障节点的下线报告。当 半数以上持有槽的主节点都标记某个节点是主观下线时。触发客观下线流程。
故障恢复
故障节点变为客观下线后,如果下线节点是持有槽的主节点则需要在它的从节点中选出一个替换它,从而保证集群的高可用。
部署集群至少需要几个物理节点
在投票选举的环节,故障主节点也算在投票数内,假设集群内节点规模是3主3从,其中有2 个主节点部署在一台机器
上,当这台机器宕机时,由于从节点无法收集到 3/2+1个主节点选票将导致故障转移失败。这个问题也适用于故障发
现环节**。因此部署集群时所有主节点最少需要部署在3台物理机上才能避免单点问题。**
Redis集群提供了灵活的节点扩容和收缩方案,可以在集群不影响对外服务的情况下,为集群添加节点进行扩容或者也可以下线部分节点进行收缩
群扩容和收缩的关键点就在于槽和节点的对应关系,扩容和收缩就是将一部分槽和数据迁移给新节点
缓存击穿:一个访问量较大的key在某个时间过期,导致大量的请求直接打到db上
解决方案:
1.加锁更新,比如请求a发现缓存中没有,对A这个key加锁,同时去数据库查询数据,写入缓存,再返回
给用户,这样后面的请求就可以从缓存中拿到数据了
2.将过期时间写在value中,通过异步不断刷新
3.将过期时间设置永不过期
2.缓存穿透:指每次查询的数据都不在缓存和数据库中,每次请求都直接打到数据库中。原因可能是自身业务代码原因,2.恶意攻击
解决方案
1.缓存空值,null值:存在问题为
3.缓存雪崩:某一时刻发生大规模的缓存失效的情况,例如缓存服务宕机、大量key在同一时间过期,这样的后果就是大量的请求进来直接打到DB上,可能导致整个系统的崩溃,称为雪崩。
解决方案:
提高缓存可用性
1.集群部署:通过集群来提升缓存的可用性
2.多级缓存,设置多级缓存,第一级缓存失效的情况下,访问第二级,每一级的失效时间不同
过期时间
1.均匀过期:为了防止大范围过期,可以将过期时间设置为随机的过期时间
2.热点数据永不过期
熔断降级
1.服务熔断:当缓存服务器宕机或者超时响应的时候,为了防止整个系统出现雪崩,暂停业务系统访问缓存系统。
2.服务降级:当出现大量的缓存失效,而且处于高并发的情况下,在业务系统暂时舍弃一下非核心的业务和数据处理,而直接返回一个提前处理好的fallback错误处理信息。
根据CAP理论,在保证高可用和分区容错性的前提下,无法保证一致性,只能尽可能保存缓存和数据库的最终一致性。
选择适合的缓存更新策略
在日常开发中我们如果采用两级缓存 本地缓存+分布式缓存
所谓本地缓存就是对应服务器的内存缓存比如Caffeine,分布式缓存就是Redis
可以采用消息队列的方式:
1.把热key打散到不同的服务器,降低压力
2.添加二级缓存,提前加载热key到内存中,如果redis宕机走内存查询
所谓缓存预热就是提前把数据库里面的数据刷新到缓存中。
开发基本使用缓存+过期时间的策略,既可以加速数据读写,又保证数据的定时更新,这种模式基本能满足绝大部分的需求。
但是如果两个问题同时出现,可能会出现较大的问题
1.当前的key是一个热点key,并发量特别高
2.重建缓存不能在短时间内完成,在缓存失效的瞬间,有大量的线程来重建缓存,造成后端负载加大,可能会造成应用的崩溃
如何处理呢
解决要点在于
所有一般采用如下的模式
为了满足业务要求添加大量的新MemCache节点但是发现节点没有好转反而下降,将这种现象称呼为缓存的无底洞现象。
那么为什么会产生这种现象呢?
通常来说添加节点使得Memcache集群 性能应该更强了,但事实并非如此。键值数据库由于通常采用哈希函数将 key
映射到各个节点上,造成key的分布与业务无关,但是由于数据量和访问量的持续增长,造成需要添加大量节点做水
平扩容,导致键值分布到更多的 节点上,所以无论是Memcache还是Redis的分布式,批量操作通常需要从不同节点上
获取,相比于单机批量操作只涉及一次网络操作,分布式批量操作会涉及多次网络时间。
如何优化呢
常见优化思路