走进缓存的世界(一) - 开篇
系列文章
概述
对于程序员来说多多少少都懂一点算法,算法是什么?算法是“时间”与“空间”的互换策略。
我们常常研究一个算法的时间复杂度和空间复杂度,如果我们有绝对足够的时间和空间,那么算法就不需要了,可惜这种条件是不存在的,只是在某些情况下我们会协调两者从而达到性能上的平衡。
缓存是一种“用空间换时间”的策略,通俗的讲,缓存就是把一些数据暂时存储起来,避免了某些重复的耗时操作,减少时间开销的一种方法。
商业世界中常说的一句话是“现金为王”。在技术世界里,与之相近的一个说法是“缓存为王”。
从底层到上层主要涉及:数据库模型设计,SQL优化,使用缓存等。从图中的优化模式来看,其中数据库模型设计的合理程度奠定了应用系统优化的基石,如果模型设计得不合理,随着业务发展,后续的优化将会变得很困难。另一方SQL优化也是数据库优化的一个重要方面,慢SQL和top SQL往往是系统性能杀手,它们是导致系统故障的重要潜在危险。
缓存在构建高性能服务器中有着举足轻重的作用,很多时候sql优化, 算法优化所带来的效果可能远远不如缓存带来的优化效果。但是缓存的使用并不是零成本的,任何缓存的增加,都会带来两大问题:
- 数据不一致
- 系统复杂度大幅度增加
如何解决呢?首先考虑去掉缓存。不要为了缓存而缓存,缓存不必要时,应该果断去掉,从而降低系统出错的可能性和系统复杂度。有些对数据实时性,准确性要求极高的系统,不能使用缓存。其次是分析需求,不同的业务会有不同的缓存策略,仔细分析变化与不变的数据,将不变的数据长时间缓存,变化的数据根据数据的业务要求动态调整缓存时间和存储方式。最后就是增加开发人员自身的能力,后面会详细提及各种问题的处理方法。
什么样的缓存才是好缓存
什么样的员工是好员工?最直接最本质的回答:能办事、且办事靠谱就是好员工,答案并不是聪明、勤奋、社交好等等,这些只是成为一个靠谱员工的充要条件。
什么样的缓存才是好缓存呢?答案是:命中率高的缓存。
在解决问题前提下,命中率高的缓存比命中率低的缓存,在硬件投入上一般会小很多,缓存数量比命中率低的缓存数量也少很多,寻址速度肯定也比较快,所以命中率高的缓存是好缓存。
缓存的设计需要考虑缓存的占用空间和命中率。我们当然希望缓存占用空间小,命中率高。命中率高是缓存设计的重要考察因素, 是提高系统性能的关键。占用空间越小,需要的成本越低。低成本,高效能的缓存设计是我们追求的目标。这没有固定的设计方法和公式,需要根据不同的业务灵活调整,但是,关于缓存在业务开发中的设计方法,有一些比较常用的思路与模式,借鉴这些模式,我们可以复用或创新,解决新的业务中所出现的问题。
缓存的命中率
一个实体丢到缓存中后,在被缓存期间,如果一次都没有使用过,这个缓存实体的命中率就是0。这个实体被请求的次数越多,缓存命中率越高。
上面说的是缓存中一个实体的命中率。对于缓存整体来说,命中率=命中数/总查找数。
对于缓存来说:通常最常使用的个体占总体的很小一部分,最不常使用的占整体的很大一部分.。
所以我们经常会看到类似这样的数据:
缓存的1万个元素中,有100个被频繁的使用,几乎每分钟都会被使用一次。2000个数据,每小时被请求一次。3000个数据,每天被请求一次,剩下的数据,被丢到缓存中后一次都没有被使用过。
现在硬件发展很快,如果我们只是需要缓存1万个数据的话,我们完全可以做到不管这1万个数据是否被使用到,全部丢到缓存,这样只要找数据,肯定缓存中有这个数据。而不需要作额外的运算,或者不需要向数据库发出请求。
但是:硬件发展快,数据量发展也快。小型的网站,缓存1万条数据,也就全部缓存了。但是大型网站最少也是上百万的数据量或者上T级别的数据,这些数据量显然不能都丢到缓存。这时候设计一个合理的缓存方案,提高缓存的命中率,就非常重要而且是必须的。
如何提高缓存命中率
纯技术的角度来说,我们只有记录了用户单位时间的请求数,并依照它来把最常使用的数据缓存起来,从而提高缓存命中率。
但更多的时候,我们是根据业务逻辑来提高缓存命中率的。比如:去年,前年发表的博客,这类文章的浏览请求,一般一天也就几次访问,不应该缓存到内存中。
我们应该通过上面逻辑,根据我们实际业务逻辑,提供一个缓存算法,提高缓存的命中率。让在我们硬件允许的条件下,缓存适当的数据,而不是所有数据。
一个反面的例子就是:不管三七二十一,一个大型的博客站点,一篇文章被用户请求的时候,发现不在内存缓存中,就从数据库中读出,然后丢到缓存。
要知道,现在爬虫程序很多的。另外,博客这类搜索引擎友好的站点,绝大多数访问压力是搜索引擎导入的。而这些访问一般都是1小时,或者1天之内,对某篇文章只有几次甚至1次请求,之后再也没有了,这种缓存方法,命中率会非常低。
说了这么多缓存命中率的问题,简单汇总一下缓存命中率的观点:
- 小型网站可以全部缓存,压力也不会很大,可以忽略缓存命中率问题
- 大型服务无法全部缓存,时候就需要架构师设计出对该业务逻辑适用的缓存方法,尽可能提高缓存命中率
- 提高命中率的方法大多是跟业务逻辑捆绑的,需要跟具体问题具体分析
- 对于不能被内存缓存的数据,最简单的提高性能方法就是使用文件缓存
- 文件缓存可以整个内容缓存成一个静态文件,也可以是整个页面的一个区域被缓存成一个文件,也可以是把一个实体序列化成XML文件进行缓存
过期策略
缓存是临时的存储,所以缓存的生命周期是有限的。它依次可能会经历如下活动:
- 进入缓存
- 正常使用期
- 更新缓存
- 离开缓存
最常见的几种过期策略如下:
多长时间没有被请求,则过期,最典型的就是ASP的Section 功能。
依赖于文件变更的缓存,一旦文件被修改,则过期,典型的是WEB站点的Web.config,一旦这个文件变更,不但缓存重起,IIS进程也会进行一次释放工作。
在此基础上,引申出很多依赖关系的缓存过期策略,比如依赖于数据库的缓存过期策略。
当然,业务逻辑里可能会有更复杂的过期策略,比如CSDN新版积分制论坛中,帖子列表缓存会在列表数据缓存达到600时,把它清理到550条数据。