缓存不应该被过度使用,在不同场景下,选用不同的方式缓存数据。
简单场景,存入一些占用内存不多的数据,而且这些数据不会主动发生变化,服务器启动后就永久存储,修改和删除都是全手动执行。而且服务器重启时不需要再自动恢复到之前的状态。这种数据直接用Map等放到内存中即可。
而一个标准Cache的主要特征有:
过期时间
容量规划(重要)
清除策略(重要)
命中率统计
其中,常见的清除策略、淘汰机制(Eviction policy)有:
FIFO(First In First Out):先进先出算法,即先放入缓存的先被移除;
LRU(Least Recently Used):最久未使用算法,使用时间距离现在最久的那个被移除;
LFU(Least Frequently Used):最近最少使用算法,一定时间段内使用次数(频率)最少的那个被移除;
其中,过期时间包括:
TTL(Time To Live):存活期,即从缓存中创建时间点开始直到它到期的一个时间段(不管在这个时间段内有没有访问都将过期)
TTI(Time To Idle):空闲期,即一个数据多久没被访问将从缓存中移除的时间。
基于以上特征,使用HashMap作为本地cache似乎很不适当。某些场景下面,倒是可以用JDK自带的WeakHashMap。
通过简单的包装,就可以为你的HashMap增加过期时间和容量规划。而且比其他cache更高效。openfire的DefaultCache就是一个很好的例子:
DefaultCache.java
DefaultCache的核心是HashMap,另外增加了一层很薄的包装来实现过期和LRU。DefaultCache包括两个LinkedList,一个用于存储插入顺序,另一个用于存储插入时间。当cache size达到临界值时,从最尾部删除。有朋友测试过,比ehcache快5倍。
如果本地cache不能满足你的要求,ehcache是个很好的选择。不仅仅作为分布式的cache,甚至作为状态同步,ehcache都有非常优秀的案例。也可以实现多机copy。分享一个数据:某生产系统中ehcache每天处理17,466,415次replication。
不过,也听说ehcache代码写得很复杂,设计有些问题,相比之下 Google的Guava Cache就要轻量得多,但是只支持内存缓存。
Guava Cache与ConcurrentMap很相似。最基本的区别是ConcurrentMap会一直保存所有添加的元素,直到显式地移除。相对地,Guava Cache通常都设定为自动回收元素。在某些场景下,尽管LoadingCache 不回收元素,它也是很有用的,因为它会自动加载缓存。
通常来说,Guava Cache适用于:
你愿意消耗一些内存空间来提升速度。
你预料到某些键会被查询一次以上。
缓存中存放的数据总量不大,不会超出内存容量。(否则,请尝试EHCache、Memcached这类工具)
如果你的场景符合上述的每一条,Guava Cache就适合你。
Ehcache使用的注意点:
1、比较少的更新数据表的情况
2、对并发要求不是很严格的情况
3、多台应用服务器中的缓存是不能进行实时同步的。
4、对一致性要求不高的情况下
ehcache支持集群,但因为Ehcache本地缓存的特性(属于 分散式缓存 ),目前无法很好的解决不同服务器间缓存同步的一致性问题,所以我们在一致性要求非常高的场合下,尽量使用Redis、Memcached等 集中式缓存 。
Ehcache has a lot more features than a Map:
limit the maximum number of elements in memory
overflow to disk (if the above number is exceeded)
set a time-to-live and time-to-idle for elements
allows replication within a cluster
If you don't need any of those, you can safely use a Map - it will be easier to configure.