`

“本地缓存”架构设计

阅读更多

前言

 

最近在做的项目其实是对老系统的一个深度改造,在老系统里缓存使用这块感觉有些瑕疵。在老系统里不管是“配置数据”还是“业务数据”都统一使用redis作为缓存。

 

“业务数据”使用redis作为缓存无可厚非,但“配置数据”使用使用redis就感觉不是很妥。

首先:过渡依赖redis,一些开关配置都依赖redis,如果redis服务挂掉整个服务瘫痪;

其次:增加redis服务的存取压力,几乎每个流程都会判断各种开关是否开启,对应的每个请求都会有数次redis读取请求。

最后:性能上也不好(与“本地jvm缓存相比”),读取redis是毫秒级的开销。

 

基于上述原因,决定对缓存结构进行重新梳理,整体采用共享缓存+本地jvm缓存的方式。

 

共享缓存+本地缓存

 

共享缓存:采用redis,如果能读取到缓存直接缓存返回,如果读取不到缓存先读取数据库,再写入redis缓存。

优点:全局共享,无需同步,一次设置,可以在多个jvm实例共享;

      存储量大,可以缓存上百G的数据;

缺点:依赖redis集群基础服务;

      由于存在网络开销,存取速度较慢(相对于本地缓存)

     

 

本地缓存:针对后端应用服务器,本地缓存指的就是jvm内存。

优点:访问数据非常快,纳秒级别(相对于redis的毫秒级别);

      不依赖外部基础服务。

缺点:但容量有限,不能存放过多内容;

      每个jvm实例都会存一份,存在数据冗余;

      修改后不便于同步等问题。

 

结合各自的优缺点,针对不同的业务场景采用不同的缓存方式,可以使系统性能达到最优。

 

共享内存redis的使用不用多说,对于正常的大量的业务数据缓存,基本都会采用redis做为缓存。对于少量配置数据、开关标记、固定的启动参数,可以采用本地缓存,针对不同的数据类型又有几种不同的本地缓存实现方式,初步分三种不同“本地缓存”,如下图所示:

 

 



 

如前所述,本地jvm缓存的难点在于保证实例间的数据同步,以及缓存数据大小的控制。根据不同业务场景,分为三类缓存数据:“配置开关”、“固定参数”、“热点数据”。本地缓存的引入是这次优化的核心,下面分别对三类本地缓存数据的同步和更新策略进行讲解。

 

1、“配置开关”数据

 

所谓“配置开关”指的是系统“降级开关”或者“备用切换开关”,这种类型的配置数据要求必须在线修改,及时生效。要做到这点,需要借助配置管理工具来完成,一般公司内部都有自己的配置管理工具,如果没有推荐使用淘宝开源的配置管理工具diamond。源码地址:https://github.com/takeseem/diamond,申请一个账号,即可下载源码。

关于淘宝diamond具体用法,可以自行查阅相关资料。大概流程如下:



 

diamond的配置以文件为单位,客户端会定期(如每隔15秒)向服务端发起检查请求配置文件内容是否发生变化(通过比较配置内容的md5值),如果变化则拉取配置。

然后解析配置文件,把变更的配置key-value,更新到本地JVM内存。

 

在任意一个server端修改配置后,同步到各个系统会有一个延迟时间(比如15秒),即客户端轮询的间隔时间。可以根据自己业务需要 适当调整这个时间。

 

小结:client端在启动的时候会把最新配置写入到jvm内存,当服务端配置发生变化后,会自动拉取变化的配置,更新jvm内存。修改后的参数会有短暂的延迟。

如果你的业务要求0延迟,最好用nettyserver端和client端建立长链接来实现同步,成本会稍微高一点。

 

2、“固定参数”数据

 

这种配置数据一般不会改变,我们可以认为这类数据是不变的,程序启动时直接读入jvm内存,如果要改变数据就只能重启程序。或者把频繁变化的数据划分为第一类“配置开关”数据。

 

在我们系统里,根据使用方式的不同“固定参数”数据又被划为两种:

a、独立的配置数据,在程序启动时写入一个全局的HashMap(由于是单线程写,不用考虑线程安全问题),在使用时,根据key直接从HashMap get即可。这种方式很容易扩展,但需要一个常量类来维护这些key的名称。

另外你也可以把参数类型分类,对每个类型定义一个枚举类,初始化的时候初始化枚举值,使用的时候直接指定某个枚举值即可。这种方式个人觉得更优雅些。

 

b、用于生产模板对象的数据,比如在我们系统里,创建一个新页面,需要一个页面模板对象作为“骨架”。这个模板对象,系统设计之初就已经确定,并且不会改变。我们以前的做法是把这些配置数据写到数据库。在需要的创建页面时 首先new一个页面对象,再从数据库中查询数据set到对象中。当然这里的配置数据,也可以放到jvm内存里,每次new对象的时候,从内存中获取set到新对象。

 

改进做法:在程序启动时,创建一个“全局模板对象”需要的参数依旧从数据库中查询(或者配置文件)。在需要创建新页面时,直接调用这个“全局模板对象”的clone方法。这种做法相对来说更优雅,前提是需要“全局模板对象”类实现Cloneable 重写clone接口,实现“深度克隆”。关于如果实现“深克隆”可以参考这篇博客:http://moon-walker.iteye.com/blog/2374195

 

3、“热点数据”

 

这里的“热点数据”可以是配置数据,也可以是业务数据。

场景一:如果配置数据太多,全部放到内存,会占用太多内存,但经常使用的数据又很少。

场景二:如果某类业务数据很多,但只有少量的数据会被经常用到。

针对这两种场景 我们通常第一时间想到的是使用redis这类的全局共享缓存,修改数据时清除redis缓存,下次查询直接查库,再同步缓存。

 

但如果这两种场景中的数据几乎都是查询,没有修改,或者说修改后有一定延迟可以接受,这时可以采用,通过LRU算法(淘汰最近最少使用的缓存算法)实现的“本地缓存”会更合理一些。关于LUR“本地缓存”可以自己实现(采用双向链表即可实现),也可以采用本地Ehcache实现。

 

如果redis挂掉

 

回到文章开头的问题,如果核心配置数据也采用redis,一旦redis挂掉,整个系统服务就会崩溃。现在我们来看看如果使用“本地缓存”来存放核心配置数据,如果redis挂掉,怎么做到系统不挂。

 

首先我们在调用redis存取服务时,使用“本地缓存”做个开关,如果redis缓存出现问题,就绕过redis缓存,直接操作数据库,这个道理很简单:



 

 

 

有人会问,不使用redis你的系统抗得住嘛?我们会对系统按照业务模块进行“微服务”拆分成多个子工程,对每个子工程进行限流处理:也就是系统的最大处理能力,我们做压力测试,计算在没有redis缓存的情况下,系统处理的最大并发数,以这个最大并发数作为redis缓存失效情况下的限流依据:



 

1、获取当前支持的最大并发数,这个采用“本地缓存”中的“配置开关”配置,如果redis挂掉,获取非redis缓存模式下系统支持的最大并发数(注意在redis缓存模式下的并发数肯定大很多,提前通过压力测试评估得到)。

2、获取当前系统正在执行的请求并发数,具体怎么获取可以参考另一篇博文:http://moon-walker.iteye.com/blog/2375240 中的限流部分。

3、判断当前正在执行的并发数是否大于步骤1中获取的最大并发数,如果已经大于,则直接返回“限流提示”,防止服务挂掉。

 

通过上述处理可以保证即便是在redis挂掉的情况下,系统依旧可以运行,由于并发处理能力降低,只能支持部分用户在系统里进行操作;另一部分用户,可能会被要求重试。但比起系统直接挂掉,这已经是较好的降级措施了。如果采用redis缓存做配置管理,就达不到上述效果。

 

 

当然这里指的是后端操作系统,如果是面向全国客户的前端系统,动不动就限流,肯定无法满足需求,对应前端系统可以采用整页静态化,缓存前置等措施,可以参考另一篇博文:http://moon-walker.iteye.com/blog/2332314

  • 大小: 34.1 KB
  • 大小: 62.4 KB
  • 大小: 22.8 KB
  • 大小: 26.4 KB
0
0
分享到:
评论

相关推荐

    Rails缓存架构设计

    ### Rails缓存架构设计 #### 一、高性能Web应用与缓存架构的重要意义 在现代互联网环境下,构建高性能Web应用面临着前所未有的挑战。随着用户数量的激增和技术的发展,Web应用不仅需要处理大规模且高并发的访问...

    大数据架构师教你如何设计缓存架构_光环it学院培训机构.pdf

    缓存架构设计时要考虑以下要点: 1. **缓存一致性**:更新策略如Write-Through、Write-Around、Write-Behind等,保证数据的一致性。 2. **缓存失效策略**:LRU(Least Recently Used)、LFU(Least Frequently Used...

    大型分布式系统中的缓存架构

    在选择和设计缓存架构时,需要考虑系统的特定需求,如并发量、数据规模、数据过期策略以及对高可用性和扩展性的要求。正确地使用缓存能够显著提升系统性能,但也会带来数据一致性、缓存穿透和雪崩等问题,因此在设计...

    阿里双十一系统项目实战(缓存架构+高可用服务架构+微服务架构.txt

    1. **本地缓存**: 如Guava Cache,它可以快速地存储和读取数据,减少远程调用带来的延迟。在双十一期间,通过将热点数据缓存在本地,可以极大地提高系统的响应速度。 2. **分布式缓存**: 常见的分布式缓存有Redis和...

    消除知识盲区 互联网缓存架构从理论到实践

    #### 三、缓存架构设计原则 在设计缓存架构时,需要遵循以下原则: - **一致性**:确保缓存中的数据与源数据保持一致,避免出现数据不一致的问题。 - **容错性**:缓存系统应该具备高可用性和容错能力,能够在部分...

    Android之本地缓存——LruCache(内存缓存)与DiskLruCache(硬盘缓存)统一框架

    Android之本地缓存——LruCache(内存缓存)与DiskLruCache(硬盘缓存)统一框架 [注:本内容来自网络,在此分享仅为帮助有需要的网友,如果侵犯了您的权利,麻烦联系我,我会第一时间删除,谢谢您。]

    亿级流量电商详情页系统的大型高并发与高可用缓存架构实战2

    本课程通过大白话解释和实际操作演示,旨在帮助开发者掌握高级缓存架构设计和高可用分布式系统架构的知识,解决复杂场景下的技术挑战。通过学习,你将能够设计和实现亿级流量电商网站的商品详情页系统,以及应对各种...

    大型分布式系统中的缓存架构.docx

    如果缓存中有请求的页面,代理服务器直接返回,否则向Web服务器请求数据并在本地缓存,以降低对后端的压力。反向代理缓存通常用于缓存小体积的静态文件。 3. 本地应用缓存存在于应用程序进程中,提供快速的本地访问...

    基于WCF的分布式缓存系统设计.pdf

    文章中还包含了一些设计缓存系统时可能用到的代码片段和流程图,例如图4中的伪代码,展现了如何从本地缓存或远程缓存中获取数据,并且展示了缓存数据时的处理逻辑。 从整体上看,本文章深入分析了WCF技术在实现...

    案例实战-高并发业务的多级缓存架构一致性解决方案

    这种架构设计的目的是提高系统性能,减少对底层存储系统的访问压力,从而提升整体的响应速度。 本地缓存,如Java的Ehcache或Guava Cache,存储在应用服务器的内存中,访问速度最快但容量有限。分布式缓存,如Redis...

    JCS1.3开源的缓存架构

    **JCS(Java Caching System)1.3 开源缓存架构详解** JCS(Java Caching System)是一个开源的、高性能的缓存框架,它主要用于提高应用的性能和响应速度,通过将常用数据存储在内存中,避免了频繁地访问数据库或...

    分布式缓存架构1.docx

    ### 分布式缓存架构——Spring Boot搭建Ehcache+Redis的二级缓存 #### 一、引言 随着互联网应用的不断普及和发展,对于数据处理能力的要求越来越高,特别是对于那些高并发、低延迟的应用场景而言,缓存技术成为了...

    网站系统的架构设计.docx

    网站系统架构设计 网站系统架构设计是指根据网站系统的需求,设计和构建一个合理、可扩展、可维护的系统架构。一个好的架构设计可以提高网站系统的性能、可靠性和可扩展性。 网站系统架构设计的重要性 网站系统...

    基于 Java 开发的在线学习平台。采用分布式系统架构和 redis 本地缓存机制.zip

    但需要注意的是,本地缓存可能导致数据一致性问题,因此需要妥善设计缓存更新策略。 5. **微服务架构**:在大型项目中,通常采用微服务架构将复杂的应用程序拆分为一组小型、独立的服务,每个服务都可以独立部署和...

    ios架构与设计

    3. **缓存处理**:将重要的数据存储到本地缓存中,以便在离线状态下也能使用。 4. **UI展示**:根据解析后的数据更新用户界面,提供丰富的用户体验。 #### 五、App架构设计思路 - **软件架构描述**:明确构成系统...

    实现多级缓存架构设计方案.docx

    **实现多级缓存架构设计方案概述** TMC,即透明多级缓存,是由有赞PaaS团队开发的,旨在解决公司内部应用的缓存管理问题,特别是针对高并发下的热点访问。传统的分布式缓存解决方案,如CodisProxy + Redis或zanKV,...

    PHP 缓存 PHP 缓存

    1. **何时使用缓存:**一般建议在完成基本的系统架构设计后再考虑引入缓存。 2. **何时生成缓存:**通常是在读取数据时生成缓存。 3. **缓存穿透:** - 当查询一个不存在的数据时,若未命中缓存,则直接访问后端...

    有赞多级缓存解决方案怎么做的,你知道吗.docx

    TMC 的本地缓存功能是通过对原生 jedis 包的 JedisPool 和 Jedis 类做了改造,在 JedisPool 初始化过程中集成 TMC“热点发现”+“本地缓存”功能 Hermes-SDK 包的初始化逻辑,使 Jedis 客户端与缓存服务端代理层交互...

    IT产品--功能架构设计(模板).zip

    在IT行业中,功能架构设计是构建复杂系统的关键步骤,它为产品的开发提供了蓝图。本话题主要围绕"IT产品--功能架构设计(模板)"这一主题展开,涵盖了物联网平台和边缘计算平台的功能架构设计。以下是这两个领域的详细...

Global site tag (gtag.js) - Google Analytics