背景
现在互联网模式(一个网站或一个应用),整体流程可以概括描述为 :
浏览器→应用服务器→数据库或文件(存储)→应用服务器→浏览器,这是一个
标准流程,通过浏览器(或App界面)发起请求,经过服务器、数据库计算整
合后反馈浏览器呈现内容。随着互联网的普及,内容信息越来越复杂,使用者
和访问量越来越大,我们的应用需要支撑更多的并发量,同时我们的应用服务
器和数据库服务器所做的计算也越来越多。但是往往我们的应用服务器资源是
有限的,且技术变革是缓慢的,数据库每秒能接受的请求次数也是有限的(或
者文件的读写也是有限的),如何能够有效利用有限的资源来提供尽可能大的
吞吐量?一个有效的办法就是减少计算量,缩短请求流程——这就是缓存。缓存
的出现就是打破上述的标准流程,其中的任何一个环节都可以被截断,请求可
以从缓存中直接获取目标数据并返回。通过这种打破常规的方式,有效减少计
算量,缩短请求流程,有效提升响应速度,节省硬件资源,让有限的资源服务
更多的用户。
缓存特征
根据面向对象的软件思维来看,缓存就是一个对象类型,那么必然有它的属性:
1、命中率 :命中率=返回正确结果数/请求缓存次数,命中率问题是缓存中的
一个非常重要的问题,它是衡量缓存有效性的重要指标。命中率越高,表明缓
存的使用率越高。
2、最大元素(或最大空间):缓存中可以存放的最大元素的数量,一旦缓存中
元素数量超过这个值(或者缓存数据所占空间超过其最大支持空间),那么将会
触发缓存启动清空策略根据不同的场景合理的设置最大元素值往往可以一定程度
上提高缓存的命中率,从而更有效的时候缓存。
3、清空策略:
a. FIFO(first in first out)
先进先出策略,最先进入缓存的数据在缓存空间不够的情况下(超出最大元素限制)
会被优先被清除掉,以腾出新的空间接受新的数据。策略算法主要比较缓存元素的创建时间。
b、b. LFU(less frequently used)
最少使用策略,无论是否过期,根据元素的被使用次数判断,清除使用次数较少的元素释放空间。
策略算法主要比较元素的hitCount(命中次数)。
c. LRU(least recently used)[获取最近使用的时间,比较后清除最远使用的元素。清除最少使用元素]
最近最少使用策略,无论是否过期,根据元素最后一次被使用的时间戳,清除最远使用时间戳的
元素释放空间。策略算法主要比较元素最近一次被get使用时间。
缓存介质
1、(从硬件介质上来看,无非就是内存和硬盘两种)从技术上划分,可以分成几种,内存、硬盘文件、数据库。
a.内存:将缓存存储于内存中是最快的选择,无需额外的I/O开销,但是内存的缺点是没有持久化落地物理磁盘,一旦应用异常break down,重新启动数据很难或者无法复原。
b.硬盘:一般来说,很多缓存框架会结合使用内存和硬盘,在内存分配空间满了或是在异常的情况下,可以被动或主动的将内存空间数据持久化到硬盘中,达到释放空间或备份数据的目的。
c.数据库:前面我们有提到,增加缓存的策略的目的之一就是为了减少数据库的I/O压力。现在使用数据库做缓存介质是不是又回到了老问题上了?其实,数据库也有很多种类型,像那些不支持SQL,
只是简单的key、value的存储结构的特殊数据库(如berkleydb、redis),响应速度和吞吐量都远远高于我们常用的关系型数据库等。
2、根据缓存与应用的耦合程度,划分为local cache(本地缓存)和remote cache(分布式缓存):
本地缓存:
1、编程式缓存实现
a. 成员变量或局部变量实现
简单代码示例如图2所示。
图2 简单代码示例图
图2 简单代码示例图
以局部变量map结构缓存部分业务数据,减少频繁的重复数据库I/O操作。缺点仅限于类的自身作用域内,类间无法共享缓存。
b. 静态变量实现
最常用的单例实现静态资源缓存,代码示例如下:
public class CityUtils {
private static final HttpClient httpClient = ServerHolder.createClientWithPool();
private static Map<integer, string=""> cityIdNameMap = new HashMap<integer, string="">();
private static Map<integer, string=""> districtIdNameMap = new HashMap<integer, string="">();
static {
HttpGet get = new HttpGet("http://gis-in.sankuai.com/api/location/city/all");
BaseAuthorizationUtils.generateAuthAndDateHeader(get,
BaseAuthorizationUtils.CLIENT_TO_REQUEST_MDC,
BaseAuthorizationUtils.SECRET_TO_REQUEST_MDC);
try {
String resultStr = httpClient.execute(get, new BasicResponseHandler());
JSONObject resultJo = new JSONObject(resultStr);
JSONArray dataJa = resultJo.getJSONArray("data");
for (int i = 0; i < dataJa.length(); i++) {
JSONObject itemJo = dataJa.getJSONObject(i);
cityIdNameMap.put(itemJo.getInt("id"), itemJo.getString("name"));
}
} catch (Exception e) {
throw new RuntimeException("Init City List Error!", e);
}
}
static {
HttpGet get = new HttpGet("http://gis-in.sankuai.com/api/location/district/all");
BaseAuthorizationUtils.generateAuthAndDateHeader(get,
BaseAuthorizationUtils.CLIENT_TO_REQUEST_MDC,
BaseAuthorizationUtils.SECRET_TO_REQUEST_MDC);
try {
String resultStr = httpClient.execute(get, new BasicResponseHandler());
JSONObject resultJo = new JSONObject(resultStr);
JSONArray dataJa = resultJo.getJSONArray("data");
for (int i = 0; i < dataJa.length(); i++) {
JSONObject itemJo = dataJa.getJSONObject(i);
districtIdNameMap.put(itemJo.getInt("id"), itemJo.getString("name"));
}
} catch (Exception e) {
throw new RuntimeException("Init District List Error!", e);
}
}
public static String getCityName(int cityId) {
String name = cityIdNameMap.get(cityId);
if (name == null) {
name = "未知";
}
return name;
}
public static String getDistrictName(int districtId) {
String name = districtIdNameMap.get(districtId);
if (name == null) {
name = "未知";
}
return name;
}
}</integer,></integer,></integer,></integer,>
2、EhCache
主要特性:
a.快速,针对大型高并发系统场景,ehcache的多线程机制有相应的优化改善。
b.简单,很小的jar包,简单配置就可直接使用,单机场景下无需过多的其他服务依赖。
c.支持多种的缓存策略,灵活。
d.缓存数据有两级:内存和磁盘,与一般的本地内存缓存相比,有了磁盘的存储空间,将可以支持更大量的数据缓存需求。
e.具有缓存和缓存管理器的侦听接口,能更简单方便的进行缓存实例的监控管理。
f.支持多缓存管理器实例,以及一个实例的多个缓存区域。
注意事项:
整体上看,ehcache的使用还是相对简单便捷的,提供了完整的各类API接口。需要注意的是,虽然ehcache支持磁盘的持久化,
但是由于存在两级缓存介质,在一级内存中的缓存,如果没有主动的刷入磁盘持久化的话,在应用异常down机等情形下,依然
会有缓存数据丢失的出现,为此可以根据需要将缓存刷到磁盘,将缓存条目刷到磁盘的操作可以通过cache.flush()方法来执行,
需要注意的是,对于对象的磁盘写入,前提是要将对象进行序列化。
还没有评论,来说两句吧...