【系统设计】如何使用缓存

一 引言
  在构建和维护业务服务应用时,大多数情况下业务系统的性能瓶颈往往是在数据库,解决应用到数据库之间瓶颈,系统的性能会得到极大提升。系统的数据库性能优化方法有很多:

从底层到上层有数据库模型设计,SQL优化,使用缓存等等。从图中的优化模式来看,其中数据库模型设计的合理程度奠定了应用系统优化的基石,如果模型设计得不合理,那么统随着业务发展,系统后续的优化困难重重,另一方SQL优化也是数据库优化的一个重要方面,慢SQL和 topSQL往往是系统性能杀手,它们是导致系统故障的重要潜在危险;还有一个就是今天和大家一起探讨的是应用缓存。
 
缓存在系统的性能优化中有着举足轻重的作用,它比sql优化 所带来的效果更直接,更高效。但是相比其他优化手段,缓存的使用并不是零成本的,任何系统使用缓存,都会遇到两大问题:
第一,数据不一致问题;
第二,系统复杂度大幅度增加;
这给系统的维护和测试增加了一定的成本,所以缓存的设计就显得非常重要了,糟糕的缓存设计可能让系统累赘不堪,而且让系统变得极难以维护。另外对于不同类型的数据,由于实时性,准确性,复杂性的不同,缓存的策略也有些不同。

二 如何设计缓存
其实缓存的设计面临三个问题,缓存哪些数据,怎么缓存,怎么保证数据一致性。
2.1 缓存哪些数据
系统优化时有一句话必须切记:“优化是无止境的”,所以如果缓存不是必须的,请去掉它,要知道越是业务上复杂的系统,对Cache的使用反而越简单,这是因为对于一个复杂、多变、历史悠久的系统,在Cache方面做过度设计会让人深陷其中;缓存的数据越多,系统的维护成本就越高,所以找准需要缓存的点尤为重要。一般情况下,我们只会缓存给系统带来巨大瓶颈的IO操作,在普通应用里尤其指由top SQL 或者慢 SQL 所带来的DAO查询;找准需要优化的sql,你可以找DBA帮忙。
 
2.2 怎么缓存
首先是缓存存储介质的选择: 你可以直接缓存在JVM内存里 或者app 应用服务器本地的localcache,你也可以采用专门的缓存服务器 如果tair、memcache等; 另外DB、文件其实也可以做缓存,他们一般缓存的事复杂计算的中间结果,这我们一般很少用到;如果你的缓存是存放在 jvm本地,那么通常是用 map 实现,如果缓存数据更新比较频繁且对数据正确性比较高,那么你需要考虑为其添加并发控制和失效策略。还有一点比较重要的就是,在集群环境下想要做到数据一致性,比较困难,主动更新比较麻烦而且达不到其想要的降低数据库等IO操作的效果,所以本地缓存的适用场景一般是在读访问非常高,而写操作极少,对数据一致性要求不是特别高的场景;如果采用专门的缓存服务器则避免了很多麻烦,tair (阿里开发的一套缓存系统),是我们经常使用的缓存中间件,它提供了很好的并发控制和失效机制,另外还提供了不同存储引擎可以供我们选择,mdb,rdb,ldb;普通的缓存可以mdb和rdb,两者分别有memcache和redis的影子,不提供持久化,但其响应时间和高QPS的表现都非常好,如果要确保数据不丢失,那么可以采用ldb引擎存储,它提供了对数据持久化的支持,相反牺牲了一点点性能。

缓存的方式:缓存的方式一般都非常简单,很多时候我们都经常缓存一个方法的执行结果:

public void getData()

{

  Object data = null;

  data = getDataFromCache();

  if(data == null){

    data = getDataFromDB();

    putDataToCache(data);

    }

}

如果缓存的添加比较多,重复的为每个方法都添加一模样的缓存代码非常不方便,我们很容易会想到用AOP去做方法的缓存框架,然后用@注解去为方法添加缓存。其实Spring3.1已经有现成的缓存框架了,使用@Cacheable 注释可以很方便的为某个方法添加缓存(有兴趣的读者可以去查阅资料研究)

@Cacheable

public Object getData(){

   return getDataFromDB();

}

当然实际上添加缓存没有那么简单,你还有很多事要去做,比如我们下面要谈到的,怎么保证数据的一致性。
2.3 怎么保证数据一致性
数据的一致性,其实就是缓存与数据源的同步机制,这非常重要,它其实直接确定了你的缓存策略是什么样的,一般缓存的同步模式我把它分为以下几种:
 a. Cache Miss Reload 
 b. Update Then SetCache 
 c. 补偿机制 
 d. Reload(Rebuilt)All 等,每种同步机制各有优缺点,在我们的实践中,经常把这几种方法相结合;
【Cache Miss Reload】:最简单,就是上面getData()函数的方式,它的好处是代码较为简洁,数据同步时在数据库CUD的时候,将缓存失效即可,如果业务对数据实时性要求不高,可以直接设置缓存失效时间,而不需要去手动失效它,这可以让代码达到最简的地步;坏处是当缓存失效瞬间,所有的请求都会经过数据库(调用getDataFromDB()函数),可能导致数据库压力过大,导致缓存一直加不上,可能会引发DB故障。
【Update Then SetCache】:顾名思义就是在缓存的数据更新的同时也触发程序更新缓存:

public void updateData(Object data){

  boolean success = doUpdateData(data);

  if(success){

    data = getDataFromDB();

    putDataToCache(data);

   }

}

这可以在很大程度上避免上述所说的缓存失效雪崩效应;坏处是由于并发的原因,存在极小几率你更新的缓存会导致脏数据进入缓存中,可以采用tair提供的 version 参数避免脏数据进入tair中,不过编码复杂度又上升了。
【补偿机制】有些时候缓存的更新不一定能够成功,也有可能会有脏数据进入缓存,如果要确保数据‘绝对’一致性,我们可以采取适当的补偿机制,如 定时从数据库的值更新到缓存,或者在更新缓存失败时,插入失败日志,定时重新执行缓存更新等
【Reload(Rebuilt)All】 有些时候你会发现上面的所有同步模式都不生效了,因为有些查询对象的写远大于读(可能是因为最初的数据库设计并没有读写分离),那么这是如果一定要缓存它的话,那可能就要以牺牲一部分的数据实时性为代价了,我们一般采用定时程序 Reload或Rebuilt所有的缓存;

三 小结
    在系统优化的过程中,缓存的使用也是一门艺术。本文阐述了应用系统使用cache的一般性原则,希望对各位读者在使用缓存方面有一定的帮助。

时间: 2024-11-08 19:13:33

【系统设计】如何使用缓存的相关文章

Oracle ZFS在行业标准SPC-2基准测试中创下新的世界纪录

北京,2013年9月12日--最新的 Oracle ZFS Storage ZS3-4实现了存储性能委员会(Storage Performance Council)SPC-2针对处理吞吐量的最佳结果,其性价比比IBM System Storage DS8870 和HP P9500 XP http://www.aliyun.com/zixun/aggregation/29800.html">Disk Array高三倍多. 以其17,244.22 SPC-2 MBPS的集聚吞吐量及$22.53

可缓存的CMS(网站内容管理器)系统设计

缓存|设计 对于一个日访问量达到百万级的网站来说,速度很快就成为一个瓶颈.除了优化内容发布系统的应用本身外,如果能把不需要实时更新的动态页面的输出结果转化成静态网页来发布,速度上的提升效果将是显著的,因为一个动态页面的速度往往会比静态页面慢2-10倍,而静态网页的内容如果能被缓存在内存里,访问速度甚至会比原有动态网页有2-3个数量级的提高.动态缓存和静态缓存的比较 基于反向代理加速的站点规划 基于apache mod_proxy的反向代理加速实现 基于squid的反向代理加速实现 面向缓存的页面

基于反向代理的Web缓存加速——可缓存的CMS系统设计

web|缓存|设计 对于一个日访问量达到百万级的网站来说,速度很快就成为一个瓶颈.除了优化内容发布系统的应用本身外,如果能把不需要实时更新的动态页面的输出结果转化成静态网页来发布,速度上的提升效果将是显著的,因为一个动态页面的速度往往会比静态页面慢2-10倍,而静态网页的内容如果能被缓存在内存里,访问速度甚至会比原有动态网页有2-3个数量级的提高. 动态缓存和静态缓存的比较 基于反向代理加速的站点规划 基于apache mod_proxy的反向代理加速实现 基于squid的反向代理加速实现 面向

缓存系统设计(Cache Framework)

缓存设计图,本文主要针对数据数据缓存,和普通对象的内存缓存,还有XML缓存 内存缓存加载机制 内存缓存类图

java文件操作及缓存机制依旧有瓶颈

我们在使用windows系统时发现,复制一个文件的再粘贴的速度大体上是一致的,我们可以猜想这是微软内部的工作人员设计的一个算法,而这个操作所能实现的最大速度是由什么影响的呢?? 那么我们可不可以自己设计一个方法来代替windows自带的复制粘贴方法来达到更快的传输速度呢?? 下面的一段代码就以缓冲的形势来实现了文件的copy工作: publicvoid copybyte(String srcFile,String destFile)throws IOException{ //创建输入流 Inpu

[译] 系统设计入门 | 掘金翻译计划

本文讲的是[译] 系统设计入门 | 掘金翻译计划, 原文地址:github.com/donnemartin- 译文出自:掘金翻译计划 译者:XatMassacrE.L9m.Airmacho.xiaoyusilen.jifaxu 这个 链接 用来查看本翻译与英文版是否有差别(如果你没有看到 README.md 发生变化,那就意味着这份翻译文档是最新的). 更多内容请见:github.com/xitu/system- 系统设计入门 翻译 有兴趣参与翻译? 以下是正在进行中的翻译: 巴西葡萄牙语 简体

【译】系统设计入门之面试题解答 —— 设计一个网页爬虫

本文讲的是[译]系统设计入门之面试题解答 -- 设计一个网页爬虫, 原文地址:Design a web crawler 原文作者:Donne Martin 译文出自:掘金翻译计划 译者:吃土小2叉 校对者:lsvih 设计一个网页爬虫 注意:这个文档中的链接会直接指向系统设计主题索引中的有关部分,以避免重复的内容.你可以参考链接的相关内容,来了解其总的要点.方案的权衡取舍以及可选的替代方案. 第一步:简述用例与约束条件 把所有需要的东西聚集在一起,审视问题.不停的提问,以至于我们可以明确使用场景

我在系统设计上犯过的14个错

  在上篇<架构师画像>的文章中提到了自己在系统设计上犯过的一些错,觉得还挺有意义的,这篇文章就来回顾下自己近八年来所做的一些系统设计,看看犯的一些比较大的血淋淋的错误(很多都是推倒重来),这八年来主要做了三个基础技术产品,三个横跨三年的大的技术项目(其中有两个还在进行中),发现大的错误基本集中在前面几年,从这个点看起来能比较自豪的说在最近的几年在系统设计的掌控上确实比以前成熟了很多. 第1个错 在设计服务框架时,我期望服务框架对使用者完全不侵入,于是做了一个在外部放一个.xml文件来描述sp

盘古:阿里云飞天分布式存储系统设计深度解析

回顾视频:https://yq.aliyun.com/edu/lesson/play/392 本次视频直播的整理文章整理完毕,如下内容. 盘古是什么? 上图列举了目前主流的云计算厂商,我们发现一个很有趣的事情:所有云计算厂商都是"富二代",它们的分布式存储技术全部采用自研技术,而没有用大家耳熟能详的开源分布式系统. 飞天梦 第一代飞天人的梦想是在大量廉价的PC服务器上,对外提供各种计算和存储服务.具体到以下几个组件:夸父,主要负责网络:女娲,主要负责协同:伏羲,主要负责调度:盘古,主要