redis普通缓存放入并设置时间异常_Guava 本地缓存使用教程

  • 准备工作

  • 创建缓存

  • 使用缓存

    • Cache 读取缓存

    • LoadingCache 读取缓存

  • 修改缓存

    • Cache 修改缓存

    • LoadingCache 修改缓存

  • 其他方法

在前面的文章 Spring Cache 使用教程:注解方式和API接口方式 中讲到了在程序开发中,在一些需要复杂或耗时的IO或CPU操作中,我们经常会使用缓存,将操作的结果保存起来,方便下次直接从缓存中读取结果。在上篇文章中,我们用的是Spring Cache,使用Redis作为缓存服务器。然而有些情况下,比如

  • 程序使用单机模式部署(只有一台服务器)
  • 数据只用来读取,不会修改

对于这类情况,使用Redis缓存效率较低(需要进行网络传输),这个时候就可以考虑使用本地缓存了。本地缓存顾名思义,就是运行在应用程序中的缓存,直接在程序内存中,读写效率更高。

在Java中,guava 为我们提供了一个很好的本地缓存 guava cache。(而且guava也包含了很多实用工具类。)所以这篇文章主要介绍如何使用 guava 缓存。

guava 提供了两种缓存

  • Cache 必须手动存入缓存数据,相当于一个加强版的Map
  • LoadingCache 可以自动加载数据的缓存,继承了 Cache

下面结合具体的代码介绍上面两种缓存的使用

准备工作

  • 添加依赖

com.google.guavaguava27.1-jre
  • 需要缓存的数据类
 private static class Data {
    private Integer id;
    private String value;
    
    // gatter and setter
}

创建缓存

详细介绍请参看代码和注释

/**
 * 普通的缓存 Cache
 */
private Cache dataCache = CacheBuilder.newBuilder()      // 使用 CacheBuilder 创建
        .expireAfterAccess(Duration.ofMinutes(30))  // 设置过期时间,在每次访问的半小时后过期,(再次访问则重新等待半小时)// .expireAfterWrite(Duration.ofHour(2))     在每次写入缓存2小时后过期
        .maximumSize(10240)     // 缓存最大数量
        .concurrencyLevel(4)    // 指定并发修改的数量,默认4
        .initialCapacity(2048)      // 设置初始化大小,避免经常扩容
        .build();       // 无参的 build 方法创建 Cache/**
 * 可以自动加载数据的缓存  LoadingCache
 */private LoadingCache dataLoadingCache = CacheBuilder.newBuilder()
        .expireAfterAccess(Duration.ofMinutes(30))      // 设置访问后30min的过期时间
        .initialCapacity(1024)
        .maximumSize(10240)
        .refreshAfterWrite(Duration.ofHours(2))     // 在写入缓存后的2小时后,自动刷新缓存
        .build(new CacheLoader() {       // build(CacheLoader) 创建 LoadingCache 缓存@Override   // 加载缓存的方法, 必须实现public Data load(Integer key) throws Exception {return getDataById(key);        // 从数据库查询数据
            }@Override       // 重写批量加载缓存的方法, 不批量查询缓存可以不重写,批量查询必须重写, 父类方法会直接抛异常public Map loadAll(Iterable extends Integer> keys) throws Exception {
                List ids = Lists.newArrayList(keys.iterator());        // Lists 是 guava 提供的工具类,贼好用
                List dataList = listDataByIds(ids);       // 从数据库批量查询数据return dataList.stream().collect(Collectors.toMap(Data::getId, Function.identity()));      // 必须要转成Map,因为loadIngCache 会把这些再存入缓存中
            }
        });

使用缓存

Cache 读取缓存

  • V getIfPresent(Object key); 如果存在则获取缓存数据,不存在返回null,缓存数据为null也返回null
  • V get(K key, Callable extends V> loader) throws ExecutionException; 获取缓存,缓存不存在则通过 loader 获取数据,并将数据缓存起来
  • ImmutableMap getAllPresent(Iterable> keys);  批量获取存在的缓存数据,ImmutableMap 为不可修改的Map(不能对其执行修改操作,否则抛异常)
// 一般都用第二种方法 
// 但其实这个方法也有点不合理的地方,使用 Callable 接口获取数据,这个接口的方法是会抛异常的,然后被转化成了 ExecutionException 重新抛出来,还得手动处理一下
// 个人觉得用 Function 这种接口更好,获取参数,然后返回数据,有异常自己处理
private Data getDataByIdFromCache(Integer id) {
    try {
        return dataCache.get(id, () -> getDataById(id));     // 获取缓存
    } catch (ExecutionException e) {
        log.error("获取缓存异常", e);   // 记录错误日志
        // e.printStackTrace(); 本地测试直接答应你就可以
    }
    // 执行异常,直接从数据库里查
    return getDataById(id);
}

LoadingCache 读取缓存

由于 LoadingCache 继承了 Cache,所以上面的方法都有,此外还有以下获取缓存的方法

  • V get(K key) throws ExecutionException; 获取缓存,不存在调用创建时传入的 load 方法加载缓存数据
  • V getUnchecked(K key);  同上,不抛出ExecutionException异常
  • ImmutableMap getAll(Iterable extends K> keys) throws ExecutionException; 批量查询缓存数据
// 批量查询缓存数据
private List listDataFromCache(List ids) {
    try {
        ImmutableMap allData = dataLoadingCache.getAll(ids);return new ArrayList<>(allData.values());
    } catch (ExecutionException e) {
        log.error("批量获取缓存异常", e);
    }return listDataByIds(ids);
}

修改缓存

Cache 修改缓存

  • void put(K key, V value);  存入缓存数据
  • void putAll(Map extends K, ? extends V> m); 批量放入缓存
  • void invalidate(Object key); 使缓存 key 对应的数据过期
  • void invalidateAll(Iterable> keys);   批量过期
  • void invalidateAll();   全部缓存数据过期

LoadingCache 修改缓存

除上面 Cache 方法外,LoadingCache 还有

  • void refresh(K key);  刷新缓存

使用示例

// 删除数据时,将对应的缓存也清掉
private void deleteById(Integer id) {
    // delete from db
    dataCache.invalidate(id);
}

// 修改数据时,需要刷新对应的缓存
private void updateData(Data data) {
    // update data into db

    // LoadingCache 可以刷新缓存
    dataLoadingCache.refresh(data.getId());

    // Cache 手动存入新的
    dataCache.put(data.getId(), data);
}

// 添加新数据时,Cache 手动存入, 也可以等查的时候再存
private void addData(Data data) {
    // Cache 需要手动存入
    dataCache.put(data.getId(), data);  
}

其他方法

  • long size();    缓存数据的数量
  • CacheStats stats();     获取缓存的统计对象,主要统计缓存命中数量,命中率,报错率,加载数据的时间等
  • ConcurrentMap asMap(); 将缓存转化成 ConcurrentMap,对map的修改会影响到实际的缓存

以上就这本篇文章关于 Guava 缓存的使用介绍了。如果有上面不懂的或者有想了解的后端知识点,欢迎私信我。

下篇文章会介绍如何使对 Spring Cache (Redis实现)进行封装, 使得能够对缓存进行批量操作。

关注我的 CSDN 账号 查看更多博客

44bfbd5dd8db0192593ad55ac2074f3f.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
二级缓存是指在应用程序中同时使用两种不同的缓存技术,通常是将本地缓存和分布式缓存结合使用,以提高缓存的效率和可靠性。GuavaRedis都是常用的缓存库,下面介绍它们如何实现二级缓存。 1. Guava实现二级缓存 Guava是一个开源的Java工具库,其中包含了许多常用的工具类和数据结构,包括本地缓存Guava本地缓存是指将数据存储在应用程序内存中的缓存,可以用于提高应用程序的性能和响应速度。但是,本地缓存的生命周期受到应用程序的生命周期限制,一旦应用程序结束,缓存中的数据也就不存在了。为了解决这个问题,我们可以将Guava本地缓存和分布式缓存结合使用,实现二级缓存。 具体实现方法如下: 1)创建Guava本地缓存 ```java LoadingCache<String, Object> localCache = CacheBuilder.newBuilder() .maximumSize(1000) .expireAfterWrite(10, TimeUnit.MINUTES) .build(new CacheLoader<String, Object>() { @Override public Object load(String key) throws Exception { // 从数据库或其他数据源中加载数据 return loadDataFromDatabase(key); } }); ``` 2)创建Redis分布式缓存 ```java JedisPool jedisPool = new JedisPool("localhost", 6379); Jedis jedis = jedisPool.getResource(); ``` 3)在应用程序中使用二级缓存 ```java public Object getObject(String key) { Object value = null; try { // 先从本地缓存中获取数据 value = localCache.get(key); } catch (Exception e) { e.printStackTrace(); } if (value == null) { // 如果本地缓存中没有数据,则从Redis缓存中获取数据 byte[] bytes = jedis.get(key.getBytes()); if (bytes != null) { value = SerializationUtils.deserialize(bytes); // 将数据存储到本地缓存中 localCache.put(key, value); } } if (value == null) { // 如果Redis缓存中也没有数据,则从数据库或其他数据源中加载数据 value = loadDataFromDatabase(key); // 将数据存储到Redis缓存本地缓存中 byte[] bytes = SerializationUtils.serialize(value); jedis.set(key.getBytes(), bytes); localCache.put(key, value); } return value; } ``` 2. Redis实现二级缓存 Redis是一个开源的内存数据库,可以用于存储和管理缓存数据。Redis分布式缓存的优点是可以存储大量的数据,并且可以跨多个应用程序共享数据,但是它的缺点是需要额外的硬件和网络资源来支持,同时也存在单点故障的风险。为了解决这个问题,我们可以将Redis缓存本地缓存结合使用,实现二级缓存。 具体实现方法如下: 1)创建Redis缓存客户端 ```java JedisPool jedisPool = new JedisPool("localhost", 6379); Jedis jedis = jedisPool.getResource(); ``` 2)创建Guava本地缓存 ```java LoadingCache<String, Object> localCache = CacheBuilder.newBuilder() .maximumSize(1000) .expireAfterWrite(10, TimeUnit.MINUTES) .build(new CacheLoader<String, Object>() { @Override public Object load(String key) throws Exception { // 从Redis缓存中加载数据 byte[] bytes = jedis.get(key.getBytes()); if (bytes != null) { return SerializationUtils.deserialize(bytes); } // 如果Redis缓存中没有数据,则从数据库或其他数据源中加载数据 return loadDataFromDatabase(key); } }); ``` 3)在应用程序中使用二级缓存 ```java public Object getObject(String key) { Object value = null; try { // 先从本地缓存中获取数据 value = localCache.get(key); } catch (Exception e) { e.printStackTrace(); } if (value == null) { // 如果本地缓存中没有数据,则从Redis缓存中获取数据 byte[] bytes = jedis.get(key.getBytes()); if (bytes != null) { value = SerializationUtils.deserialize(bytes); // 将数据存储到本地缓存中 localCache.put(key, value); } } if (value == null) { // 如果Redis缓存中也没有数据,则从数据库或其他数据源中加载数据 value = loadDataFromDatabase(key); // 将数据存储到Redis缓存本地缓存中 byte[] bytes = SerializationUtils.serialize(value); jedis.set(key.getBytes(), bytes); localCache.put(key, value); } return value; } ``` 以上就是GuavaRedis实现二级缓存的方法。需要注意的是,二级缓存的实现需要综合考虑应用程序的性能、复杂度、可靠性和安全性等方面的因素,选择合适的缓存技术和策略,才能达到最优的效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值