guava、caffeine、ohc(堆外缓存)详解

coding

一、Guava缓存

Guava Cache适用于以下场景:

  • 你愿意消耗一些内存空间来提升速度。
  • 你预料到某些键会被查询一次以上。
  • 缓存中存放的数据总量不会超出内存容量。(Guava Cache是单个应用运行时的本地缓存。它不把数据存放到文件或外部服务器。如果这不符合你的需求,请尝试Redis这类工具)

仓库坐标如下:

<dependency>

<groupId>com.google.guava</groupId>

<artifactId>guava</artifactId>

<version>19.0</version>

</dependency>

代码详细示例:

@Data

public class CacheVO {

private String name;

public CacheVO(String name) {

this.name = name;

}

}

public class GuavaCacheMangerService {

private static LoadingCache<String, CacheVO> cache;

private static ExecutorService executorService = new ThreadPoolExecutor(8, 8, 8, TimeUnit.SECONDS, new

LinkedBlockingQueue<Runnable>(1204));

static {

cache = CacheBuilder.newBuilder()

// 缓存项在给定时间内没有被读/写访问,则回收。

.expireAfterAccess(500, TimeUnit.SECONDS)

// 缓存项在给定时间内没有被写访问(创建或覆盖),则回收。

// 如果认为缓存数据总是在固定时候后变得陈旧不可用,这种回收方式是可取的。

.expireAfterWrite(500, TimeUnit.SECONDS)

// 初始化容量大小

.initialCapacity(1024 * 100)

// 缓存项的数目不超过固定值

.maximumSize(1024 * 100)

// 可以为缓存增加自动刷新功能,配合CacheLoader reload使用

.refreshAfterWrite(1, TimeUnit.SECONDS)

// 加载缓存

.build(new CacheLoader<String, CacheVO>() {

// 单个加载,要么返回已经缓存的值,要么使用CacheLoader向缓存原子地加载新值。

@Override

public CacheVO load(String s) throws Exception {

return createCacheVO(s);

}

// 批量加载,对每个不在缓存的键,getAll方法会单独调用CacheLoader.load来加载缓存项。

// 如果批量加载比多个单独加载更高效,你可以重载CacheLoader.loadAll来利用这一点。

@Override

public Map<String, CacheVO> loadAll(Iterable<? extends String> keys) throws Exception {

return createBatchCacheVOs(keys);

}

// 异步刷新加载新值,在刷新操作进行时,缓存仍然可以向其他线程返回旧值,

// 而不像回收操作,读缓存的线程必须等待新值加载完成。

@Override

public ListenableFuture<CacheVO> reload(String key, CacheVO oldValue) throws Exception {

if (needRefresh()) {

return Futures.immediateFuture(oldValue);

}

ListenableFutureTask<CacheVO> task = ListenableFutureTask.create(() -> {return createCacheVO(key);});

executorService.execute(task);

return task;

}

});

}

public static boolean needRefresh() {

Random ra =new Random();

return (ra.nextInt(10) % 2) > 0 ? true : false;

}

public static CacheVO createCacheVO(String key){

return new CacheVO(key);

}

public static Map<String, CacheVO> createBatchCacheVOs(Iterable<? extends String> keys) {

Map<String, CacheVO> result = new HashMap<>();

for (String key : keys) {

result.put(key, new CacheVO(key));

}

return result;

}

public static void main(String[] args) throws Exception{

// 单个获取

CacheVO cacheVO1 = cache.get("AA");

// 如果有缓存则返回;否则运算、缓存、然后返回,整个过程是阻塞的

// 在整个加载方法完成前,缓存项相关的可观察状态都不会更改。

CacheVO cacheVO2 = cache.get("BB", () -> {return createCacheVO("BB");});

List<String> list = new ArrayList<>();

list.add("CC");

list.add("DD");

// 批量获取

Map<String, CacheVO> cacheMap = cache.getAll(list);

// 个别清除

cache.invalidate("AA");

// 批量清除

cache.invalidateAll(list);

// 清除所有

cache.invalidateAll();

}

}

二、Caffeine缓存

Caffeine是一种高性能的缓存库,是基于Java 8的最佳(最优)缓存框架,性能各方面优于guava。

代码仓库如下:

<dependency>

<groupId>com.github.ben-manes.caffeine</groupId>

<artifactId>caffeine</artifactId>

<version>2.4.0</version>

</dependency>

代码详细示例如下:

public class CaffeineCacheMangerService {

private static LoadingCache<String, CacheVO> cache;

private static AsyncLoadingCache<String, CacheVO> asyncCache;

private static AsyncLoadingCache<String, CacheVO> asyncCache1;

private static ExecutorService executorService = new ThreadPoolExecutor(8, 8, 8, TimeUnit.SECONDS, new

LinkedBlockingQueue<Runnable>(1204));

static {

cache = Caffeine.newBuilder()

// 初始化缓存长度

.initialCapacity(1024 * 10)

// 最大长度

.maximumSize(1024 * 10)

// 更新策略

.refreshAfterWrite(10, TimeUnit.SECONDS)

// 设置缓存的过期时间

.expireAfterWrite(10, TimeUnit.SECONDS)

.build(new CacheLoader<String, CacheVO>() {

// 同步加载

@CheckForNull

@Override

public CacheVO load(@Nonnull String key) throws Exception {

return createCacheVO(key);

}

// getAll将会对缓存中没有值的key分别调用CacheLoader.load方法来构建缓存的值。

// 我们可以重写CacheLoader.loadAll方法来提高getAll的效率。

@Nonnull

@Override

public Map<String, CacheVO> loadAll(@Nonnull Iterable<? extends String> keys) throws Exception {

return createBatchCacheVOs(keys);

}

});

// 异步加载 同步load写法,最后也会转异步

asyncCache = Caffeine.newBuilder()

.maximumSize(1024 * 10)

.expireAfterWrite(10, TimeUnit.SECONDS)

.buildAsync(new CacheLoader<String, CacheVO>() {

@CheckForNull

@Override

public CacheVO load(@Nonnull String key) throws Exception {

return createCacheVO(key);

}

@Nonnull

@Override

public Map<String, CacheVO> loadAll(@Nonnull Iterable<? extends String> keys) {

return createBatchCacheVOs(keys);

}

});

// 异步加载 异步load写法

asyncCache1 = Caffeine.newBuilder()

.maximumSize(1024 * 10)

.expireAfterWrite(10, TimeUnit.SECONDS)

.buildAsync(new AsyncCacheLoader<String, CacheVO>() {

@Nonnull

@Override

public CompletableFuture<CacheVO> asyncLoad(@Nonnull String key, @Nonnull Executor executor) {

return asyncCreateCacheVO(key, executor);

}

@Nonnull

@Override

public CompletableFuture<Map<String, CacheVO>> asyncLoadAll(@Nonnull Iterable<? extends String> keys, @Nonnull Executor executor) {

return asyncCreateBatchCacheVOs(keys, executor);

}

});

}

public static CompletableFuture<CacheVO> asyncCreateCacheVO(String key, Executor executor) {

return CompletableFuture.supplyAsync(() -> createCacheVO(key), executor);

}

public static CompletableFuture<Map<String, CacheVO>> asyncCreateBatchCacheVOs(Iterable<? extends String> keys, Executor executor) {

return CompletableFuture.supplyAsync(() -> createBatchCacheVOs(keys),executor);

}

public static CacheVO createCacheVO(String key) {

return new CacheVO(key);

}

public static Map<String, CacheVO> createBatchCacheVOs(Iterable<? extends String> keys) {

Map<String, CacheVO> result = new HashMap<>();

for (String key : keys) {

result.put(key, new CacheVO(key));

}

return result;

}

public static void main(String[] args) throws Exception {

CacheVO cacheVO1 = cache.get("AA");

List<String> list = new ArrayList<>();

list.add("BB");

list.add("CC");

Map<String, CacheVO> map = cache.getAll(list);

// 如果有缓存则返回;否则运算、缓存、然后返回,整个过程是阻塞的

// 即使多个线程同时请求该值也只会调用一次Function方法

CacheVO cacheVO2 = cache.get("DD", (k) -> createCacheVO(k));

System.out.println(JSON.toJSONString(cacheVO2));

// 单个清除

cache.invalidate("AA");

// 批量清除

cache.invalidateAll(list);

// 全部清除

cache.invalidateAll();

// 返回一个CompletableFuture

CompletableFuture<CacheVO> future = asyncCache.get("EE");

CacheVO asyncCacheVO = future.get();

System.out.println(JSON.toJSONString(asyncCacheVO));

// 返回一个CompletableFuture<MAP<>>

CompletableFuture<Map<String, CacheVO>> allFuture = asyncCache.getAll(list);

Map<String, CacheVO> asyncMap = allFuture.get();

System.out.println(JSON.toJSONString(asyncMap));

CompletableFuture<CacheVO> future1 = asyncCache1.get("FF");

CacheVO asyncCacheVO1 = future1.get();

System.out.println(JSON.toJSONString(asyncCacheVO1));

CompletableFuture<Map<String, CacheVO>> allFuture1 = asyncCache1.getAll(list);

Map<String, CacheVO> asyncMap1 = allFuture.get();

System.out.println(JSON.toJSONString(asyncMap1));

}

}

三、堆外缓存

1、堆外内存是什么?

在JAVA中,JVM内存指的是堆内存。机器内存中,不属于堆内存的部分即为堆外内存。

  • Unsafe类操作堆外内存
  • NIO类操作堆外内存 代码示例:
// 反射获取Unsafe实例

Field f = Unsafe.class.getDeclaredField("theUnsafe");

f.setAccessible(true);

Unsafe unsafe = (Unsafe)f.get(null);

// 分配一块内存空间

long address = unsafe.allocateMemory(1024);

unsafe.putLong(address, 1);

System.out.println(unsafe.getAddress(address));

// 释放内存

unsafe.freeMemory(address);

// 通过nio包下的ByteBuffer直接分配内存

ByteBuffer buffer = ByteBuffer.allocateDirect(10 * 1024 * 1024);

2、堆外内存垃圾回收

参考下面链接: https://www.jianshu.com/p/35cf0f348275

3、为什么用堆外内存

本地缓存是非常快速的,但是大量的本地缓存会带来gc的压力,所以这个时候堆外内存是一个很好的选择。

4、堆外缓存(ohc)

仓库坐标如下:

            <dependency>

<groupId>org.caffinitas.ohc</groupId>

<artifactId>ohc-core</artifactId>

<version>0.7.0</version>

</dependency>

代码示例如下:

public static OHCache<byte[], byte[]> ohcCache;

static{

ohcCache = OHCacheBuilder.<byte[], byte[]>newBuilder()

.keySerializer(new CacheSerializer<byte[]>() {

@Override

public void serialize(byte[] bytes, ByteBuffer byteBuffer) {

byteBuffer.put(bytes);

}

@Override

public byte[] deserialize(ByteBuffer byteBuffer) {

return new byte[]{byteBuffer.get()};

}

@Override

public int serializedSize(byte[] bytes) {

return bytes.length;

}

})

.valueSerializer(new CacheSerializer<byte[]>() {

@Override

public void serialize(byte[] bytes, ByteBuffer byteBuffer) {

byteBuffer.putInt(bytes.length);

byteBuffer.put(bytes);

}

@Override

public byte[] deserialize(ByteBuffer byteBuffer) {

byte[] arr = new byte[byteBuffer.getInt()];

byteBuffer.get(arr);

return arr;

}

@Override

public int serializedSize(byte[] bytes) {

return bytes.length;

}

})

// number of segments (must be a power of 2), defaults to number-of-cores * 2

.segmentCount(2)

// hash table size (must be a power of 2), defaults to 8192

.hashTableSize(1024)

.capacity(2 * 1024 * 8)

.timeouts(true)

// 每个段的超时插槽数

.timeoutsSlots(64)

// 每个timeouts-slot的时间量(以毫秒为单位)

.timeoutsPrecision(512)

// "LRU" 最旧的(最近最少使用的)条目被逐出

// "W_TINY_LFU" 使用频率较低的条目被逐出,以便为新条目腾出空间

.eviction(Eviction.W_TINY_LFU)

.build();

}

以上是 guava、caffeine、ohc(堆外缓存)详解 的全部内容, 来源链接: utcz.com/z/509100.html

回到顶部