searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

性能和效率的提升—关于guava cache本地缓存的介绍

2023-10-30 01:57:52
29
0

性能和效率的提升—关于guava cache本地缓存的介绍

1. 缓存的作用

      在业务系统中随着数据量的不断增长和应用程序的复杂性的提高,访问和检索数据变得越来越耗时。为了解决这个问题,缓存机制就应运而生。而其中的本地缓存是一种将数据存储在离应用程序更接近的位置的技术。它通过在内存中存储数据副本来提高数据的访问速度和性能。也是下文的主角。

       本地缓存的优势不仅仅体现在提高数据访问速度上,它还可以减轻数据库的负载,提高应用程序的可扩展性和稳定性。通过将常用的数据存储在本地缓存中,应用程序可以避免频繁地访问数据库,从而减少数据库的压力,提高整体性能。

       另外,本地缓存还可以提供更好的用户体验。用户可以更快地获取所需的数据,减少等待时间,提高应用程序的响应速度。无论是在线购物应用程序还是社交媒体平台,本地缓存都可以为用户提供更加流畅和高效的体验。

       在本地缓存的世界里,有多种优秀的开源工具可供选择。对Java生态而言,从最简单的HashMap到更高级的工具包如Ehcache和Guava Cache,开发人员可以根据自己的需求选择最适合的本地缓存解决方案。这些工具不仅提供了基本的缓存功能,还支持缓存过期、缓存回收、缓存失效等高级特性,使得开发人员能够更好地控制和管理缓存数据。

2. 动手写一种最原始的缓存

       缓存的本质是将数据以键值对的形式保存在内存或其他可以快速存取的介质上,在有需要时,可以根据键值快速定位到本地缓存,并将缓存结果取出供业务使用。

       出于上述对缓存的理解,我们很容易能联想到使用 Map 这类数据结构进行结果的保存,下面是一个简单的例子:

// 创建一个HashMap作为缓存
Map<String, User> cache = new HashMap<>();

// 向缓存中添加数据
User user1 = new User("001", "John");
User user2 = new User("002", "Emily");

cache.put(user1.getId(), user1);
cache.put(user2.getId(), user2);

// 从缓存中获取数据
User cachedUser = cache.get("001");
System.out.println(cachedUser.getName()); // 输出:John

// 从缓存中移除数据
cache.remove("001");

// 检查缓存中是否存在某个键
boolean containsKey = cache.containsKey("001");
System.out.println(containsKey); // 输出:false

      上例是使用了一个HashMap缓存用户数据,以用户id作为key,在后续业务需要根据用户id获取用户信息时,可以通过用户id快速获取到对应的用户对象。

      当然,上述只是一个极简单的例子,还缺乏很多作为缓存所必须的特性,例如:

    • 线程安全(上例的HashMap非线程安全)
    • 缓存数据需要有失效时间或失效条件
    • 需要限制缓存的总量,避免缓存过大导致内存溢出
    • 需要有机制能防止缓存击穿
    • 需要有缓存回收机制

       上述特性都是一个缓存工具所必须具备的,在实际的工程实践中,我们没有必须重复去制造轮子,可以直接采用优秀的开源缓存工具来实现对应的效果。

3. 使用guava cache去做本地缓存

       Guava Cache是一个强大且广泛使用的Java缓存库。它提供了丰富的功能和配置选项,使得开发人员能够更好地控制和管理缓存数据。

       Guava Cache提供了一个内存缓存的实现,可以轻松地集成到Java应用程序中。它支持自定义的缓存大小、过期策略、缓存回收机制等,使得开发人员能够根据具体需求进行灵活的配置。

      下面是一个使用Guava Cache的简单示例:

// 创建一个Guava Cache对象
Cache<String, User> cache = CacheBuilder.newBuilder()
    .maximumSize(100)  // 设置缓存的最大容量
    .expireAfterWrite(10, TimeUnit.MINUTES)  // 设置缓存项的过期时间
    .build();

// 向缓存中添加数据
User user1 = new User("001", "John");
User user2 = new User("002", "Emily");

cache.put(user1.getId(), user1);
cache.put(user2.getId(), user2);

// 从缓存中获取数据
User cachedUser = cache.getIfPresent("001");
System.out.println(cachedUser.getName()); // 输出:John

// 从缓存中移除数据
cache.invalidate("001");

// 检查缓存中是否存在某个键
boolean containsKey = cache.asMap().containsKey("001");
System.out.println(containsKey); // 输出:false

       上例是一个简单的guava cache使用示例,它的作用同样是缓存用户信息,以用户id为key,用户对象为value,在业务有需要时,能快速地根据id查找出用户。和第二节的使用HashMap实现的简单缓存不同,使用guava cache工具时,guava cache就帮我们解决了线程安全、缓存回收等等复杂的问题。

4. guava cache的缓存大小限制

       Guava Cache提供了多种方式来限制缓存的大小,以确保在达到指定容量时自动回收过期或不常用的缓存项。以下是几种常见的限制缓存大小的方式:

  1. maximumSize(maximumWeight):通过调用maximumSize()方法或maximumWeight()方法来设置缓存的最大容量或最大权重。maximumSize()方法指定缓存项的数量上限,而maximumWeight()方法指定缓存项的权重上限。当缓存达到容量上限时,Guava Cache会根据缓存项的访问频率、大小等信息自动回收一些缓存项。
  2. size():通过调用size()方法来获取当前缓存的大小,即缓存中存储的缓存项的数量。
  3. CacheBuilder.weigher():通过调用CacheBuilder.weigher()方法指定缓存项的权重计算方式。权重可以用于更精确地控制缓存的大小。例如,可以根据缓存项的大小、复杂性等指标来计算权重。
  4. CacheBuilder.removalListener():通过调用CacheBuilder.removalListener()方法设置缓存项被移除时的监听器。可以在监听器中根据需要执行一些操作,如记录日志、释放资源等。

       需要注意的是,Guava Cache的大小限制并不是严格的,它是基于近似值的。当缓存项数量或权重接近最大限制时,Guava Cache会尽最大努力去回收一些缓存项,以保持缓存的大小在可接受的范围内。

       以下是一个示例,展示了如何使用maximumSize()方法来限制Guava Cache的最大容量:

Cache<String, User> cache = CacheBuilder.newBuilder()
    .maximumSize(100)  // 设置缓存的最大容量为100
    .build();

       在上面的示例中,我们使用maximumSize()方法将缓存的最大容量设置为100。当缓存中的缓存项数量达到100时,Guava Cache会根据一定的策略自动回收一些缓存项。

5. guava cache的缓存回收机制

    guava cache 提供了以下几种缓存的回收机制:

    • 手动回收
    • 基于容量的回收
    • 基于时间的回收
  1. 手动回收

       手动回收是指业务逻辑里,使用guava cache 提供的:invalidate、invalidateAll等方法把一项或多项的缓存设置为无效以达到回收的目的,仍以上例为例:

// 创建一个Guava Cache对象
Cache<String, User> cache = CacheBuilder.newBuilder()
    .maximumSize(100)  // 设置缓存的最大容量
    .expireAfterWrite(10, TimeUnit.MINUTES)  // 设置缓存项的过期时间
    .build();

// 向缓存中添加数据
User user1 = new User("001", "John");
cache.put(user1.getId(), user1);

// 从缓存中移除数据
cache.invalidate("001");

      上例的cache.invalidate就实现了从释放key = 001 的缓存的目的

      2. 基于容量的回收

      基于容量的回收就是上文第四节所述的限制,当达到限制的数量后,guava cache会使用LRU算法淘汰缓存

      3. 基于时间的回收

       基于时间的回收,是指在构建缓存时,我们可以指定缓存的有效期,在缓存时间到有效期后,就将其淘汰并回收。

Cache<String, User> cache = CacheBuilder.newBuilder()
    .maximumSize(100)  // 设置缓存的最大容量为100
    .expireAfterWrite(10, TimeUnit.MINUTES)  // 设置缓存项的写入时间后过期
    .weakValues()  // 将缓存项的值设置为弱引用
    .build();

       例如上文,我们设置了一个缓存项的有效时间是写入后的10分钟内有效,那么写入超过10分钟时,缓存就会失效。

 

 

       本篇文章主要介绍cache的作用及guava cache的基本用法,除此之外,guava cache还有其他扩展的高级特性,例如LoadingCache等,同样地,guava cache内部的很多实现原理也是值得我们学习的,这些将在下一篇文章展开详细介绍。

0条评论
作者已关闭评论
梁****健
11文章数
0粉丝数
梁****健
11 文章 | 0 粉丝
原创

性能和效率的提升—关于guava cache本地缓存的介绍

2023-10-30 01:57:52
29
0

性能和效率的提升—关于guava cache本地缓存的介绍

1. 缓存的作用

      在业务系统中随着数据量的不断增长和应用程序的复杂性的提高,访问和检索数据变得越来越耗时。为了解决这个问题,缓存机制就应运而生。而其中的本地缓存是一种将数据存储在离应用程序更接近的位置的技术。它通过在内存中存储数据副本来提高数据的访问速度和性能。也是下文的主角。

       本地缓存的优势不仅仅体现在提高数据访问速度上,它还可以减轻数据库的负载,提高应用程序的可扩展性和稳定性。通过将常用的数据存储在本地缓存中,应用程序可以避免频繁地访问数据库,从而减少数据库的压力,提高整体性能。

       另外,本地缓存还可以提供更好的用户体验。用户可以更快地获取所需的数据,减少等待时间,提高应用程序的响应速度。无论是在线购物应用程序还是社交媒体平台,本地缓存都可以为用户提供更加流畅和高效的体验。

       在本地缓存的世界里,有多种优秀的开源工具可供选择。对Java生态而言,从最简单的HashMap到更高级的工具包如Ehcache和Guava Cache,开发人员可以根据自己的需求选择最适合的本地缓存解决方案。这些工具不仅提供了基本的缓存功能,还支持缓存过期、缓存回收、缓存失效等高级特性,使得开发人员能够更好地控制和管理缓存数据。

2. 动手写一种最原始的缓存

       缓存的本质是将数据以键值对的形式保存在内存或其他可以快速存取的介质上,在有需要时,可以根据键值快速定位到本地缓存,并将缓存结果取出供业务使用。

       出于上述对缓存的理解,我们很容易能联想到使用 Map 这类数据结构进行结果的保存,下面是一个简单的例子:

// 创建一个HashMap作为缓存
Map<String, User> cache = new HashMap<>();

// 向缓存中添加数据
User user1 = new User("001", "John");
User user2 = new User("002", "Emily");

cache.put(user1.getId(), user1);
cache.put(user2.getId(), user2);

// 从缓存中获取数据
User cachedUser = cache.get("001");
System.out.println(cachedUser.getName()); // 输出:John

// 从缓存中移除数据
cache.remove("001");

// 检查缓存中是否存在某个键
boolean containsKey = cache.containsKey("001");
System.out.println(containsKey); // 输出:false

      上例是使用了一个HashMap缓存用户数据,以用户id作为key,在后续业务需要根据用户id获取用户信息时,可以通过用户id快速获取到对应的用户对象。

      当然,上述只是一个极简单的例子,还缺乏很多作为缓存所必须的特性,例如:

    • 线程安全(上例的HashMap非线程安全)
    • 缓存数据需要有失效时间或失效条件
    • 需要限制缓存的总量,避免缓存过大导致内存溢出
    • 需要有机制能防止缓存击穿
    • 需要有缓存回收机制

       上述特性都是一个缓存工具所必须具备的,在实际的工程实践中,我们没有必须重复去制造轮子,可以直接采用优秀的开源缓存工具来实现对应的效果。

3. 使用guava cache去做本地缓存

       Guava Cache是一个强大且广泛使用的Java缓存库。它提供了丰富的功能和配置选项,使得开发人员能够更好地控制和管理缓存数据。

       Guava Cache提供了一个内存缓存的实现,可以轻松地集成到Java应用程序中。它支持自定义的缓存大小、过期策略、缓存回收机制等,使得开发人员能够根据具体需求进行灵活的配置。

      下面是一个使用Guava Cache的简单示例:

// 创建一个Guava Cache对象
Cache<String, User> cache = CacheBuilder.newBuilder()
    .maximumSize(100)  // 设置缓存的最大容量
    .expireAfterWrite(10, TimeUnit.MINUTES)  // 设置缓存项的过期时间
    .build();

// 向缓存中添加数据
User user1 = new User("001", "John");
User user2 = new User("002", "Emily");

cache.put(user1.getId(), user1);
cache.put(user2.getId(), user2);

// 从缓存中获取数据
User cachedUser = cache.getIfPresent("001");
System.out.println(cachedUser.getName()); // 输出:John

// 从缓存中移除数据
cache.invalidate("001");

// 检查缓存中是否存在某个键
boolean containsKey = cache.asMap().containsKey("001");
System.out.println(containsKey); // 输出:false

       上例是一个简单的guava cache使用示例,它的作用同样是缓存用户信息,以用户id为key,用户对象为value,在业务有需要时,能快速地根据id查找出用户。和第二节的使用HashMap实现的简单缓存不同,使用guava cache工具时,guava cache就帮我们解决了线程安全、缓存回收等等复杂的问题。

4. guava cache的缓存大小限制

       Guava Cache提供了多种方式来限制缓存的大小,以确保在达到指定容量时自动回收过期或不常用的缓存项。以下是几种常见的限制缓存大小的方式:

  1. maximumSize(maximumWeight):通过调用maximumSize()方法或maximumWeight()方法来设置缓存的最大容量或最大权重。maximumSize()方法指定缓存项的数量上限,而maximumWeight()方法指定缓存项的权重上限。当缓存达到容量上限时,Guava Cache会根据缓存项的访问频率、大小等信息自动回收一些缓存项。
  2. size():通过调用size()方法来获取当前缓存的大小,即缓存中存储的缓存项的数量。
  3. CacheBuilder.weigher():通过调用CacheBuilder.weigher()方法指定缓存项的权重计算方式。权重可以用于更精确地控制缓存的大小。例如,可以根据缓存项的大小、复杂性等指标来计算权重。
  4. CacheBuilder.removalListener():通过调用CacheBuilder.removalListener()方法设置缓存项被移除时的监听器。可以在监听器中根据需要执行一些操作,如记录日志、释放资源等。

       需要注意的是,Guava Cache的大小限制并不是严格的,它是基于近似值的。当缓存项数量或权重接近最大限制时,Guava Cache会尽最大努力去回收一些缓存项,以保持缓存的大小在可接受的范围内。

       以下是一个示例,展示了如何使用maximumSize()方法来限制Guava Cache的最大容量:

Cache<String, User> cache = CacheBuilder.newBuilder()
    .maximumSize(100)  // 设置缓存的最大容量为100
    .build();

       在上面的示例中,我们使用maximumSize()方法将缓存的最大容量设置为100。当缓存中的缓存项数量达到100时,Guava Cache会根据一定的策略自动回收一些缓存项。

5. guava cache的缓存回收机制

    guava cache 提供了以下几种缓存的回收机制:

    • 手动回收
    • 基于容量的回收
    • 基于时间的回收
  1. 手动回收

       手动回收是指业务逻辑里,使用guava cache 提供的:invalidate、invalidateAll等方法把一项或多项的缓存设置为无效以达到回收的目的,仍以上例为例:

// 创建一个Guava Cache对象
Cache<String, User> cache = CacheBuilder.newBuilder()
    .maximumSize(100)  // 设置缓存的最大容量
    .expireAfterWrite(10, TimeUnit.MINUTES)  // 设置缓存项的过期时间
    .build();

// 向缓存中添加数据
User user1 = new User("001", "John");
cache.put(user1.getId(), user1);

// 从缓存中移除数据
cache.invalidate("001");

      上例的cache.invalidate就实现了从释放key = 001 的缓存的目的

      2. 基于容量的回收

      基于容量的回收就是上文第四节所述的限制,当达到限制的数量后,guava cache会使用LRU算法淘汰缓存

      3. 基于时间的回收

       基于时间的回收,是指在构建缓存时,我们可以指定缓存的有效期,在缓存时间到有效期后,就将其淘汰并回收。

Cache<String, User> cache = CacheBuilder.newBuilder()
    .maximumSize(100)  // 设置缓存的最大容量为100
    .expireAfterWrite(10, TimeUnit.MINUTES)  // 设置缓存项的写入时间后过期
    .weakValues()  // 将缓存项的值设置为弱引用
    .build();

       例如上文,我们设置了一个缓存项的有效时间是写入后的10分钟内有效,那么写入超过10分钟时,缓存就会失效。

 

 

       本篇文章主要介绍cache的作用及guava cache的基本用法,除此之外,guava cache还有其他扩展的高级特性,例如LoadingCache等,同样地,guava cache内部的很多实现原理也是值得我们学习的,这些将在下一篇文章展开详细介绍。

文章来自个人专栏
缓存介绍
1 文章 | 1 订阅
0条评论
作者已关闭评论
作者已关闭评论
0
0