ITPub博客

首页 > 数据库 > MySQL > 【系统设计】如何使用缓存

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

MySQL 作者:pooldb 时间:2015-10-23 17:21:59 0 删除 编辑
<span style="font-size:16px;"><strong>一 引言</strong></span><br /> &nbsp; 在构建和维护业务服务应用时,大多数情况下业务系统的性能瓶颈往往是在数据库,解决应用到数据库之间瓶颈,系统的性能会得到极大提升。系统的数据库性能优化方法有很多:<br /> <img src="/attachment/201509/5/22664653_1441462454HGVf.jpg" width="580" height="461" alt="" /><br /> 从底层到上层有数据库模型设计,SQL优化,使用缓存等等。从图中的优化模式来看,其中数据库模型设计的合理程度奠定了应用系统优化的基石,如果模型设计得不合理,那么统随着业务发展,系统后续的优化困难重重,另一方SQL优化也是数据库优化的一个重要方面,慢SQL和 topSQL往往是系统性能杀手,它们是导致系统故障的重要潜在危险;还有一个就是今天和大家一起探讨的是应用缓存。<br /> &nbsp;<br /> 缓存在系统的性能优化中有着举足轻重的作用,它比sql优化 所带来的效果更直接,更高效。但是相比其他优化手段,缓存的使用并不是零成本的,任何系统使用缓存,都会遇到两大问题:<br /> 第一,数据不一致问题;<br /> 第二,系统复杂度大幅度增加;<br /> 这给系统的维护和测试增加了一定的成本,所以缓存的设计就显得非常重要了,糟糕的缓存设计可能让系统累赘不堪,而且让系统变得极难以维护。另外对于不同类型的数据,由于实时性,准确性,复杂性的不同,缓存的策略也有些不同。<br /> <br /> <strong><span style="font-size:16px;">二 如何设计缓存</span></strong><strong><span style="font-size:16px;"></span></strong><br /> 其实缓存的设计面临三个问题,缓存哪些数据,怎么缓存,怎么保证数据一致性。<br /> <strong><span style="font-size:14px;">2.1 缓存哪些数据</span></strong><br /> 系统优化时有一句话必须切记:“优化是无止境的”,所以如果缓存不是必须的,请去掉它,要知道越是业务上复杂的系统,对Cache的使用反而越简单,这是因为对于一个复杂、多变、历史悠久的系统,在Cache方面做过度设计会让人深陷其中;缓存的数据越多,系统的维护成本就越高,所以找准需要缓存的点尤为重要。一般情况下,我们只会缓存给系统带来巨大瓶颈的IO操作,在普通应用里尤其指由top SQL 或者慢 SQL 所带来的DAO查询;找准需要优化的sql,你可以找DBA帮忙。<br /> &nbsp;<br /> <strong><span style="font-size:14px;">2.2 怎么缓存</span></strong><br /> 首先是缓存存储介质的选择: 你可以直接缓存在JVM内存里 或者app 应用服务器本地的localcache,你也可以采用专门的缓存服务器 如果tair、memcache等; 另外DB、文件其实也可以做缓存,他们一般缓存的事复杂计算的中间结果,这我们一般很少用到;如果你的缓存是存放在 jvm本地,那么通常是用 map 实现,如果缓存数据更新比较频繁且对数据正确性比较高,那么你需要考虑为其添加并发控制和失效策略。还有一点比较重要的就是,在集群环境下想要做到数据一致性,比较困难,主动更新比较麻烦而且达不到其想要的降低数据库等IO操作的效果,所以本地缓存的适用场景一般是在读访问非常高,而写操作极少,对数据一致性要求不是特别高的场景;如果采用专门的缓存服务器则避免了很多麻烦,tair (阿里开发的一套缓存系统),是我们经常使用的缓存中间件,它提供了很好的并发控制和失效机制,另外还提供了不同存储引擎可以供我们选择,mdb,rdb,ldb;普通的缓存可以mdb和rdb,两者分别有memcache和redis的影子,不提供持久化,但其响应时间和高QPS的表现都非常好,如果要确保数据不丢失,那么可以采用ldb引擎存储,它提供了对数据持久化的支持,相反牺牲了一点点性能。<br /> 缓存的方式:缓存的方式一般都非常简单,很多时候我们都经常缓存一个方法的执行结果:<br /> <div id="codeText" class="codeText"> <ol style="margin:0 1px 0 0;padding:5px 0pt;" start="1" class="dp-css none_number"> <li> <span style="color:#000000;"><span style="color:#0000FF;">public</span> void getData<span style="color:#0000CC;">(</span><span style="color:#0000CC;">)</span><br /> </span> </li> <li> {<br /> </li> <li> &nbsp;&nbsp;Object data <span style="color:#0000CC;">=</span> null<span style="color:#0000CC;">;</span><br /> </li> <li> &nbsp;&nbsp;data <span style="color:#0000CC;">=</span> getDataFromCache<span style="color:#0000CC;">(</span><span style="color:#0000CC;">)</span><span style="color:#0000CC;">;</span><br /> </li> <li> &nbsp;&nbsp;<span style="color:#0000FF;">if</span><span style="color:#0000CC;">(</span>data <span style="color:#0000CC;">=</span><span style="color:#0000CC;">=</span> null<span style="color:#0000CC;">)</span><span style="color:#0000CC;">{</span><br /> </li> <li> &nbsp;&nbsp;&nbsp;&nbsp;data <span style="color:#0000CC;">=</span> getDataFromDB<span style="color:#0000CC;">(</span><span style="color:#0000CC;">)</span><span style="color:#0000CC;">;</span><br /> </li> <li> &nbsp;&nbsp;&nbsp;&nbsp;putDataToCache<span style="color:#0000CC;">(</span>data<span style="color:#0000CC;">)</span><span style="color:#0000CC;">;</span><br /> </li> <li> &nbsp;&nbsp;&nbsp;&nbsp;}<br /> </li> <li> } </li> </ol> </div> 如果缓存的添加比较多,重复的为每个方法都添加一模样的缓存代码非常不方便,我们很容易会想到用AOP去做方法的缓存框架,然后用@注解去为方法添加缓存。其实Spring3.1已经有现成的缓存框架了,使用@Cacheable 注释可以很方便的为某个方法添加缓存(有兴趣的读者可以去查阅资料研究)<br /> <div id="codeText" class="codeText"> <ol style="margin:0 1px 0 0;padding:5px 0pt;" start="1" class="dp-css none_number"> <li> <span style="color:#000000;">@Cacheable<br /> </span> </li> <li> <span style="color:#0000FF;">public</span> Object getData<span style="color:#0000CC;">(</span><span style="color:#0000CC;">)</span><span style="color:#0000CC;">{</span><br /> </li> <li> &nbsp;&nbsp;&nbsp;return getDataFromDB<span style="color:#0000CC;">(</span><span style="color:#0000CC;">)</span><span style="color:#0000CC;">;</span><br /> </li> <li> } </li> </ol> </div> 当然实际上添加缓存没有那么简单,你还有很多事要去做,比如我们下面要谈到的,怎么保证数据的一致性。<br /> <strong><span style="font-size:14px;">2.3&nbsp;</span></strong><strong><span style="font-size:14px;">怎么保证数据一致性</span></strong><br /> 数据的一致性,其实就是缓存与数据源的同步机制,这非常重要,它其实直接确定了你的缓存策略是什么样的,一般缓存的同步模式我把它分为以下几种:<br /> &nbsp;a.&nbsp;Cache Miss Reload&nbsp;<br /> &nbsp;b.&nbsp;Update Then SetCache&nbsp;<br /> &nbsp;c.&nbsp;补偿机制&nbsp;<br /> &nbsp;d.&nbsp;Reload(Rebuilt)All 等,每种同步机制各有优缺点,在我们的实践中,经常把这几种方法相结合;<br /> <span style="white-space:normal;">【Cache Miss Reload】:最简单,就是上面getData()函数的方式,它的好处是代码较为简洁,数据同步时在数据库CUD的时候,将缓存失效即可,如果业务对数据实时性要求不高,可以直接设置缓存失效时间,而不需要去手动失效它,这可以让代码达到最简的地步;坏处是当缓存失效瞬间,所有的请求都会经过数据库(调用getDataFromDB()函数),可能导致数据库压力过大</span><span style="white-space:normal;">,导致缓存一直加不上,可能会引发DB故障。</span><br style="white-space:normal;" /> <span style="white-space:normal;">【Update Then SetCache】:顾名思义就是在缓存的数据更新的同时也触发程序</span><span style="white-space:normal;">更新缓存:</span><br /> <div id="codeText" class="codeText"> <ol style="margin:0 1px 0 0;padding:5px 0pt;" start="1" class="dp-css none_number"> <li> <span style="color:#000000;"><span style="color:#0000FF;">public</span> void updateData<span style="color:#0000CC;">(</span>Object data<span style="color:#0000CC;">)</span><span style="color:#0000CC;">{</span><br /> </span> </li> <li> &nbsp;&nbsp;boolean success <span style="color:#0000CC;">=</span> doUpdateData<span style="color:#0000CC;">(</span>data<span style="color:#0000CC;">)</span><span style="color:#0000CC;">;</span><br /> </li> <li> &nbsp;&nbsp;<span style="color:#0000FF;">if</span><span style="color:#0000CC;">(</span>success<span style="color:#0000CC;">)</span><span style="color:#0000CC;">{</span><br /> </li> <li> &nbsp;&nbsp;&nbsp;&nbsp;data <span style="color:#0000CC;">=</span> getDataFromDB<span style="color:#0000CC;">(</span><span style="color:#0000CC;">)</span><span style="color:#0000CC;">;</span><br /> </li> <li> &nbsp;&nbsp;&nbsp;&nbsp;putDataToCache<span style="color:#0000CC;">(</span>data<span style="color:#0000CC;">)</span><span style="color:#0000CC;">;</span><br /> </li> <li> &nbsp;&nbsp;&nbsp;}<br /> </li> <li> } </li> </ol> </div> 这可以在很大程度上避免上述所说的缓存失效雪崩效应;坏处是由于并发的原因,存在极小几率你更新的缓存会导致脏数据进入缓存中,可以采用tair提供的 version 参数避免脏数据进入tair中,不过编码复杂度又上升了。<br /> 【补偿机制】有些时候缓存的更新不一定能够成功,也有可能会有脏数据进入缓存,如果要确保数据‘绝对’一致性,我们可以采取适当的补偿机制,如 定时从数据库的值更新到缓存,或者在更新缓存失败时,插入失败日志,定时重新执行缓存更新等<br /> 【Reload(Rebuilt)All】 有些时候你会发现上面的所有同步模式都不生效了,因为有些查询对象的写远大于读(可能是因为最初的数据库设计并没有读写分离),那么这是如果一定要缓存它的话,那可能就要以牺牲一部分的数据实时性为代价了,我们一般采用定时程序 Reload或Rebuilt所有的缓存;<br /> <br /> <strong><span style="font-size:16px;">三 小结</span></strong><br /> &nbsp; &nbsp; 在系统优化的过程中,缓存的使用也是一门艺术。本文阐述了应用系统使用cache的一般性原则,希望对各位读者在使用缓存方面有一定的帮助。<br /> 参考&nbsp;http://tair.taobao.org/<br /> <br /> 如果您觉得从这篇文章受益,可以赞助 <strong><u>北在南方</u></strong>&nbsp;一瓶饮料 ^_^<br style="color:#666666;font-family:宋体, Arial;line-height:26px;background-color:#FFFFFF;" /> <img src="http://img.blog.itpub.net/blog/attachment/201509/15/22664653_1442320801pGpV.jpg" width="571" height="518" alt="" style="color:#666666;font-family:宋体, Arial;line-height:26px;background-color:#FFFFFF;" /><br />

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/30074459/viewspace-1815939/,如需转载,请注明出处,否则将追究法律责任。

下一篇: oracle 面试题
请登录后发表评论 登录
全部评论

注册时间:2014-12-22

  • 博文量
    404
  • 访问量
    273199