【Spring】SpringCache的应用(转自罗汉堂技术分享)

一、缓存

一般来说,现在的互联网应用、网站或者App的整体流程可以用下面这张图来表示:

用户请求,从界面,到网络转发,到应用服务,最后到存储(数据库或文件系统),然后再返回到界面呈现内容。

 

随着互联网的普及,内容信息越来越复杂,用户数和访问量越来越大。应用需要支撑更多的并发量。

同时,应用服务器和数据库服务器所做的计算也越来越多。

但是,往往应用服务器的资源是有限的。而且,技术变革是缓慢的。

数据库每秒能接收的请求次数也是有限的,或者说,文件的读写也是有限的。

 

如何能有效地利用有限的资源来提供尽可能大的吞吐量?

一个有效的办法就是引入缓存,打破我们标准的流程。

每个环节中,请求可以从缓存中直接获取目标数据并返回,从而减少它们的计算量,来有效提升响应速度。让有限的资源服务更多的用户。

像上图里展示的,缓存的使用其实可以出现在1~4的各个环节中,每个环节的缓存方案都各有特点。

 

二、缓存的特征

1、命中率:命中数/(命中数+未命中数)

①命中:直接通过缓存获取到需要的数据。

②不命中:无法通过缓存获取到需要的数据,需要再次查询数据库,或者执行其它的操作。原因可能是,由于缓存中根本不存在,或者缓存已经过期了。

 

缓存的命中率越高,它就表示我们使用缓存的收益越高,应用的性能越好

这个时候,应用的响应时间会变得更短,吞吐量会变得更高,抗并发能力也会变得更强。

因此,在高并发的互联网系统中,缓存的命中率,是至关重要的一个指标。

 

2、最大元素(最大空间)

它代表的是:缓存中可以存放的最大元素的数量

一旦缓存中,元素数量超过这个值,是指:缓存的数据所占的空间超过了最大支持的空间,将会触发缓存清空策略

根据不同的场景,合理地设计最大元素值,往往可以一定程度上,提高缓存的命中率,从而更有效地使用缓存

 

缓存的存储空间是有限制的,当缓存空间被用满时,如何保证在稳定服务的同时,有效地提升命中率?

这就由缓存的清空策略来处理

适合自身数据特征的清空策略能有效地提高命中率

 

3、常见的清空策略

①FIFO(first in first out):先进先出策略

它是指:最先进入缓存的数据,在缓存空间不够的情况下,或者是超出最大元素限制的时候,会优先被清除掉,以腾出新的空间,来接受新的数据

这个策略算法,主要是比较缓存元素的创建时间

在数据实时性要求场景下,可以选择该类策略,优先保障最新数据可用

 

②LFU(least frequently used):最少使用策略

它是指:无论是否过期,根据元素的被使用次数来判断

清除使用次数最少的元素,来释放空间

这个策略的算法,它主要比较元素的命中次数

在保证高频数据有效性场景下,可以选择这种策略

 

③LRU(Least Recently Used):最近最少使用策略

它是指:无论是否过期,根据元素最后一次被使用的时间戳,清除最远使用时间戳的元素,释放空间

这个策略算法,主要比较元素的最近一次被GET使用时间

在热点数据场景下,较适用,优先保证热点数据的有效性

 

④过期时间

根据过期时间判断,清理过期时间最长的元素

或者

根据过期时间判断,清理最近要过期的元素

 

⑤随机

随机清理

 

三、影响缓存命中率的因素

1、业务场景和业务需求

①缓存通常适合读多写少的业务场景

反之,使用缓存的意义其实并不大,命中率会很低

②业务需求也决定了对实时性的要求,直接影响到缓存的过期时间和更新策略

实时性要求越低,就越适合缓存

在相同key和相同请求数的情况下,缓存的时间越长,命中率就会越高

我们目前遇到的互联网应用,大多数的业务场景下,都是很适合使用缓存的

 

2、缓存的设计(粒度和策略方面)

通常情况下,缓存的粒度越小,命中率就会越高

 

实际的例子:

①当缓存单个对象的时候,比如单个用户信息

只有当该对象的对应的数据,发生变化的时候

我们才需要更新缓存或者移除缓存

 

②当缓存一个集合的时候

例如,我们要缓存所有用户数据

其中任何一个对象对应的数据发生变化时

我们都需要更新或者移除缓存

 

③还有另一种情况,假设其它地方,也需要获取该对象对应的数据时

比如说,其它地方也需要获取单个用户信息

如果缓存的是单个对象,那么就可以直接命中缓存

否则的话,就无法直接命中

这样的话,会更加灵活,缓存的命中率会更高

 

④此外,缓存的更新、过期策略也直接影响到缓存的命中率

当数据发生变化时,直接更新缓存的值,会比移除缓存,或者让缓存过期,它的命中率更高

当然,这个时候的系统复杂度,也会变得更高

 

3、缓存容量和基础设施

①缓存的容量有限,它就容易引起缓存失效和被淘汰

目前,多数的缓存框架或中间件,都采用了LRU算法

 

②同时,缓存的技术选型也是至关重要的

比如:采用应用内置的本地缓存,就比较容易出现单机瓶颈

而采用分布式缓存,就更容易扩展

所以,需要做好系统容量规划,并考虑是否可扩展

 

③此外,不同的缓存框架或中间件,它们的效率和稳定性,也是存在一些差异的

 

④除此之外,还有一些其它的因素,会影响到缓存的命中率

比如:当缓存节点发生故障的时候,需要避免缓存失效,并最大程度地降低影响

业内比较典型的做法,就是通过一致性哈希算法或者节点冗余的方式来避免这个问题

 

⑤理解误区

既然业务需求对数据实时性要求很高,而缓存时间又会影响到缓存命中率,那么,系统就别使用缓存了

其实,非常容易忽略一个重要的因素,就是并发

通常来讲,在相同缓存时间和key的情况下,并发越高,缓存的收益就会越高,即使缓存的时间很短

 

四、如何提高缓存命中率

需要应用尽可能地通过缓存来直接获取数据,并避免缓存失效

这也是比较考验能力的,需要在业务需求,缓存粒度,缓存策略,技术选型等各个方面,去通盘考虑,并做权衡

尽可能地聚焦在高频访问,且时效性要求不高的热点业务上

通过缓存预加载(预热),增加存储容量,调整缓存粒度,更新缓存等手段来提高命中率

 

对于时效性很高,或者是缓存空间有限的情况下:内容跨度越大,或者访问很随机,并且访问量不高的应用来说

缓存命中率可能会长期都很低

可能预热后的缓存,还没来得及被访问,就已经过期了

 

五、缓存分类和应用场景

目前的应用服务框架中,比较常见的,是根据缓存与应用的耦合度,分为本地缓存和分布式缓存

 

1、本地缓存指的是:应用中的缓存组件

它最大的优点是:应用和Cache是在同一个进程的内部,请求缓存非常得快速,没有过多的网络开销

在单机应用中,不需要集群支持或者集群情况下,各节点无需互相通知的场景下,使用本地缓存比较合适

 

它的缺点是:缓存跟应用程序耦合,多个应用程序无法直接共享缓存

各应用或集群的各个节点,都需要维护自己单独的缓存,有时对内存也是一种浪费

 

我们涉及到本地缓存,通常一般是编程实现,使用成员变量,局部变量,静态变量等来缓存数据

当然,也有提供好的现成的框架,比如Guava Cache、Ehcache

 

2、分布式缓存

它指的是:应用分离的缓存组件或服务

它最大的优点是:自身就是一个独立的应用,与本地应用是隔离的,多个应用可以直接共享缓存

比如Redis、Memcache,它们都可以用来作为分布式缓存

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值