海量数据下如何正确访问Redis服务才不会挂掉?

树枝和树叶


要保证Redis不会挂掉,也就是提高Redis的高可用性,可以从这么几个方面考虑。

集群式部署方式

  • Redis单副本:也就是只部署一台Redis,不需要节点之间的数据同步,架构简单,部署方便;但是单台机器毕竟是有风险的,按照题目中【海量数据】的场景,是不能达到高可用要求的。

  • Redis主从:主从实例可以部署在不同的物理服务器上,充分利用多台服务器的资源,在主库发生故障的时候,可以进行主备切换,从而保证系统的稳定运行,甚至可以做到读写分离,主库专门用作写操作,一台或多台备库进行读操作;但是当主库发生故障的时候(如果没有HA方案的话),是需要手动进行主备切换的。

  • Redis Sentinel:部署架构分为两部分【Sentinel集群】和【数据集群】;Sentinel集群是由多个Sentinel节点组成的分布式集群,通常是2N+1台服务器,可以实现故障发现和转移、客户端通知等功能;数据集群用于存储数据;它能够解决主从模式下的自动切换问题,并且数据集群是可以横向扩展的;当然这个架构实现和部署起来,也更为复杂一些;并且这个架构不能做到读写分离。

  • Redis Cluster:Redis 3.0集群,是分布式集群解决方案之一,物理架构中配置2N个节点(主从一一对应),主节点提供读写操作,从节点作为备份;数据分布保存在多个节点上,是一种无中心的架构,如果有部分节点发生故障,能够实现故障自动转移和切换,用投票机制完成备库升级为主库(下文的Redis分片章节,还会介绍到Redis Cluster)。

Redis分片

Redis在3.0之前只支持单实例,在此之前,在数据量比较大的情况,通常有几种方案可以做到把数据分片保存到多台服务器上。

  • 客户端分片:分片逻辑被放到了客户端上,由客户端根据路由规则,把数据保存不同的Redis实例中,读取的时候也是根据规则,去对应的实例上读取数据;但是当Redis实例数量发生变化的时候(增加实例或减少实例),需要手动地调整分片的规则程序;并且这种部署方式,也增加了运维的成本。

  • Redis代理组件:在这种架构中,客户端不再直接访问Redis实例,而是访问代理组件,由它管理路由的规则;客户端不需要关心有几个Redis实例,数据被路由到哪个实例上;但是由于在客户端和Redis之间增加了一层代理,多多少少也会产生一些性能上的损耗。

  • Redis Cluster:上文中提到的Redis 3.0集群,这里对数据的存储和路由方式,再介绍几句:Redis把所有的Key分成了16384个slot,每个Redis实例负责其中一部分slot;每个Redis都知道每个slot在哪个节点上存储(实例节点定期做数据交换);当客户端访问到一个Redis实例的时候,如果数据不在这个实例上,那么会通过重定向命令引导客户端访问数据所在的实例。



热点数据挑战单节点的极限

虽然我们已经把数据分散保存到多台Redis实例上了,但是如果有一个热点数据被频繁访问,超过了单实例的服务器极限,那么该如何解决呢?通常的手段就是做读写分离,部署多台只读节点,对外提供服务。

我将持续分享Java开发、架构设计、程序员职业发展等方面的见解,希望能得到你的关注。


会点代码的大叔


海量数据下正确的访问redis要注意的事情有很多,基本上可以从服务治理,数据,redis正确使用三个方面来讲。

  • 服务治理

既然是海量数据,那么服务肯定要拆分成多个服务,最常见的采用“大中台,小前台”的概念,中台分各个服务中心,各个中心去维护自己中心负责的服务,向上游前台提供数据和服务。比如一个做内容付费的公司可以有内容中心、商品中心、交易中心

用户中心、促销中心、基础中心、开放平台等,中心之间采用RPC通信或者数据共享。

  • 数据划分
在服务治理的基础上,各个中心负责维护自己的数据,那么数据的存储有MySQL、MongoDB(存储非结构化数据)、Redis,这些数据存储介质都做成独立实例,互不影响。那么海量数据分散到各个中心了,压力也会相对少一些,做到每个中心都有自己独立的一个或多个数据库实例、Redis实例。每个中心再根据自己中心的数据做好分库分表、缓存同步等工作。下图是一张数据库和redis缓存同步的方案,通过实时刷新+定时任务同步补偿机制,将大部分热点业务数据实时同步至redis。
  • redis的正确使用

在做好服务治理和数据划分的基础上,这个时候就是重点讲如何正确使用redis的时候了,个人列举了部分细则仅供大家参考:

  1. 熟练使用五种数据结构(String、Set|、Hash、List、ZSet)以及每种数据结构的适用场景和注意事项;

  2. 防止缓存雪崩,即避免大批量缓存同一时间段集中过期,导致大量请求都怼到数据库上,导致数据库连接数爆满、宕机;

  3. 防止缓存穿透,避免redis中热点key存入了null或者不存在,导致大量请求绕过redis请求数据库去了;

  4. 避免大key的存在:比如一个redis集群是16G,共8个节点,每个节点平均分配2G的内存,这时候如果有一个大的hash key占用内存超过2G了,这个时候尽管集群还有剩余的空间,这个大key的写入依旧会失败,单个key是无法做到集群的,另外再想想如果一个hash存储了大量的数据,考虑一下性能问题?

  5. 禁止使用keys、flushall、flushdb等,运维同学通过redis的rename机制禁掉命令,或者使用scan的方式渐进式处理;

  6. 批量读写redis请采用pipeline管道的方式;

  7. 在代码中采用线程连接池的方式定义每一个redis bean

  8. 这里和大家一起温习一下redis过期策略和内存淘汰机制:redis采用的是惰性删除和定期删除的过期策略,内存淘汰机制有:

volatile-lru :在设置了过期时间的键空间中,移除最近最少使用的key。allkeys-lru : 移除最近最少使用的key (推荐)volatile-random : 在设置了过期时间的键空间中,随机移除一个键,不推荐allkeys-random : 直接在键空间中随机移除一个键,没人用volatile-ttl : 在设置了过期时间的键空间中,有更早过期时间的key优先移除 不推荐noeviction : 不做过键处理,只返回一个写操作错误。 不推荐

如果是你,你会使用哪种过期策略?

所以,个人认为redis用好并不难,难的是把服务治理好,redis跟着服务走就可以了。

以上,是个人对redis使用的一些认识和看法,大家还有什么好的使用建议和想法,欢迎下方留言讨论,共同成长~


java架构设计


挂掉?一般都是配置不足,如果你服务器和redis在同同一机器,机器内存和服务器内存 几乎占了机器的全部内存,这时候你访问取一个比较大的数据肯定会挂的。加钱吧😄


分享到:


相關文章: