记录一下我所了解的几种缓存机制
缓存
缓存是一种存储在内存中,可以快速定位查找数据的数据结构,并且一个缓存算法是包含了冷数据淘汰的机制的。
LRU
LRU-Least Recently Used 的缩写,即近期最少使用算法,一般会使用 双向链表 按照访问顺序对缓存数据进行排序,当满足一定条件后可以将链表中最少使用的数据从缓存中剔除出去。
在Java中,从1.4开始提供了java.util.LinkedHashMap
类来帮助我们简单的实现LRU缓存算法。
- 继承
java.util.LinkedHashMap
- 通过构造函数定义
java.util.LinkedHashMap#accessOrder
为true,即链表排列顺序为访问顺序(默认为false,即插入顺序) - 覆盖
java.util.LinkedHashMap
的removeEldestEntry方法- JDK会在向该Map中put数据时会调用该方法
- 如果返回true,则会将存在时间最长的数据剔除
下面是一个利用java.util.LinkedHashMap
LRU的简单实现
不足
- 从Java1.8
java.util.LinkedHashMap#afterNodeAccess
源码可以看出,当读取一个缓存数据之后,访问顺序链表将会发生重排。 - 由于缓存的特性,所以他必须是全局变量,当并发量比较大的情况下,缓存作为临界资源在发生重排时势必加锁,使对链表的变更操作由异步转换为同步操作。
软引用
Java中的软引用也经常被用在缓存的实现上。
软引用属于Java中四种引用方式中的一种,不同于我们平常使用的强引用,软引用并非只在对象未被根搜索算法查找到时才可能被回收,而是在内存空间不足时,同样会回收这些对象,这是软引用的特性(除了强引用,软引用,还有弱引用,虚引用,但这两种很少用到)。
- 依照这种特性,如果使用软引用来存储缓存数据,则缓存数据会被保存至内存将要溢出时。
以下是我使用软引用实现的缓存结构。
不足
- 数据的清理是不可控的,可能会热点数据的全部失效,瞬时增加下游服务的压力。
Oracle缓存算法
Oracle申请的专利缓存算法,数据存储使用HashMap或其他简单的Map即可,缓存的数据可以定义好他的生命周期,如数据使用次数为10次等。
数据是否进入缓存是依赖一个函数f(),函数的返回值是true或者false,当结果为true时,则将数据put进缓存。
该种缓存算法的命中率极高,原因在于热点数据的get次数远远大于冷数据,即虽然f()相同,但热点数据更多的读取次数保证了数据在缓存中保持的时间更长。
以下是我的简单实现(未实现数据的移除)。
注意
- 数据存储可以使用HashMap,但是因为HashMap是线程不安全的,注意可能并发环境下扩容而导致的死循环问题,具体原因可查看https://my.oschina.net/hosee/blog/673521