Java工具篇之Guava-cache内存缓存

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 常在业务系统中做开发,不会点高级知识点,有点不好意思了。在业务系统中,提高系统响应速度,提供系统高并发能力,其实方向很简单,三个方向,六个字而已: **缓存降级限流。**当然这是在排除代码质量非常差的情况,如果代码质量很差,都是while循环和高内存占用,那么其实再怎么做都于事无补。除非你有一个马云爸爸,性能不够,机器来凑嘛。阿里云前来支持(1000台机器够了吗?)

本篇主要是本地缓存代码实战,提供业务中常用的本地缓存使用代码片段(直接跳过看标题五)

常在业务系统中做开发,不会点高级知识点,有点不好意思了。在业务系统中,提高系统响应速度,提供系统高并发能力,其实方向很简单,三个方向,六个字而已: 缓存降级限流。
当然这是在排除代码质量非常差的情况,如果代码质量很差,都是while循环和高内存占用,那么其实再怎么做都于事无补。除非你有一个马云爸爸,性能不够,机器来凑嘛。阿里云前来支持(1000台机器够了吗?)

一、什么是Guava Cache

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>29.0-jre</version>
</dependency>

其实就是Google提供的一个开发工具包,里面有很多好用的Java开工具,比如我们本文将的Cache缓存能力。
说到缓存,每个业务系统中现在都会用到缓存,常用的缓存数据库就是Redis和Memcache,这两款kv数据库最常用的场景就是当缓存使用,极其适合在微服务架构下做缓存使用。速度是极高的,但是跟本地缓存来比,还是算慢的,毕竟本地缓存其实就相当于一个Map集合,本地缓存获取没有网络IO。但是最大的缺点是每台服务器的本地缓存是不能共享的。所以如果要用分布式缓存就可以跳过了。因为本文将的本地缓存使用。

说到底其实缓存我们就可以理解为是一个Map集合,不过生产中我们不能用Map来做缓存,除非是缓存的数据只有一点点一点点。否则如果数据量瞬时或者数据积累量很大,很容易就直接就把Map撑爆。导致内存溢出,服务宕机下线风险。 所以我们必须要对Map做控制。

  1. 控制数据量大小
  2. 控制数据生命周期
  3. 如果能做些数据命中率统计更好了

对,以上就是Guava Cache已经为我们做好的能力了。我们只用使用就可以了

二、什么场景适合缓存

不长更新的数据都可以使用缓存,只要我们定时去刷新缓存获取最新的数据就可以了。
注意: 凡是使用GuavaCache的地方都可以使用RedisCache,但是使用RedisCache的地方不一定可以使用GuavaCache。因为前面我们也说了Guava是本地缓存,不支持多服务器数据共享,如果要共享缓存数据直接用Redis是更好的选择。

三、使用本地缓存,高并发会把机器打爆

这个担心是逻辑思考的必然,使用缓存主要是提高系统响应效率的,如果用不过把机器搞爆就不好了。所以这种担心很有必要,但是只要弄清楚没参数或者它的实现原理就不用担心了。4和5是快速入门即代码片段,直接根据代码去做不会有问题。

四、快速入门API

CacheBuilder

属性 作用 例子
removalListener 缓存移除的监听 对指定key的删除,做监听
maximumSize 设置最大缓存数量 当达到最大数量,会删除多余的缓存记录
expireAfterWrite 设置过期时间 过期的缓存自动移除
recordStats 统计信息 统计缓存命中率

1. 设置最大缓存数量

    Cache<String,String> cache = CacheBuilder.newBuilder()
                                 .maximumSize(2).build();
    cache.put("key1","value1");
    cache.put("key2","value2");
    cache.put("key3","value3");
    // 第一个key是null,因为指定缓存数量是2个,当超过就删除前面一条
    System.out.println("第一个值:" + cache.getIfPresent("key1"));
    System.out.println("第一个值:" + cache.getIfPresent("key2"));
    System.out.println("第一个值:" + cache.getIfPresent("key3"));

2. 设置过期时间

    Cache<String,String> cache = CacheBuilder.newBuilder()
                                 .maximumSize(2)
                                 .expireAfterWrite(3,TimeUnit.SECONDS)
                                     .build();
    cache.put("key1","value1");
    int time = 1;
    while(true){
        System.out.println("第" + time ++ "次取到的key1的值为:" + cache.getIfPresent("key1"));
        Thread.sleep(1000)
    }

3. 统计命中率

    Cache<String,String> cache = CacheBuilder.newBuilder()
                                 .maximumSize(3)
                                 .recordStats()
                                     .build();
    cache.put("key1","value1");
    cache.put("key2","value2");
    cache.put("key3","value3");
    
    cache.getIfPresent("key1")
    cache.getIfPresent("key1")
    cache.getIfPresent("key2")
    cache.getIfPresent("key3")
    // 获取统计信息
    System.out.println(cache.stats());

CacheStats

属性值 含义
requestCount 返回cache查找缓存的次数
hitCount 命中缓存的次数
missCount 未命中缓存的次数
missRate 返回缓存请求未命中的比率,未命中次数除以请求次数
loadCount 返回缓存调用load方法加载新值的次数
loadSuccessCount 返回缓存加载新值的成功次数
loadExceptionCount 返回缓存加载新值出现异常的次数
loadExceptionRate 返回缓存加载新值出现异常的比率
totalLoadTime 返回缓存加载新值所耗费的总时间
averageLoadPenalty 缓存加载新值的耗费的平均时间,加载的次数除以加载的总时间
evictionCount 返回缓存中条目被移除的次数

五、代码片段

    private LoadingCache<Long,UserInfoDTO> userCache;
    
    {
        userCache =  CacheBuilder.newBuilder().maximumSize(30)//缓存30条数据
                .expireAfterWrite(10,TimeUnit.SECONDS) // 缓存时间10s
                    .build(// 缓存加载器,如果没有找到key,就去加载这个key到缓存中
                new CacheLoader<Long,UserInfoDTO>(){
                    @Override
                    public UserInfoDTO load(Long key) throws Exception{
                        return userService.queryById(key);
                    }
                }
            )
    }
    
    public UserInfoDTO queryUserInfoByIdFromCache(Long userId){
        return userCache.get(userId);
    }
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
5天前
|
算法 安全 Java
性能工具之 JMeter 自定义 Java Sampler 支持国密 SM2 算法
【4月更文挑战第28天】性能工具之 JMeter 自定义 Java Sampler 支持国密 SM2 算法
17 1
性能工具之 JMeter 自定义 Java Sampler 支持国密 SM2 算法
|
5天前
|
存储 Java
深入理解Java虚拟机:JVM内存模型
【4月更文挑战第30天】本文将详细解析Java虚拟机(JVM)的内存模型,包括堆、栈、方法区等部分,并探讨它们在Java程序运行过程中的作用。通过对JVM内存模型的深入理解,可以帮助我们更好地编写高效的Java代码,避免内存溢出等问题。
|
8天前
|
算法 Java Go
Go vs Java:内存管理与垃圾回收机制对比
对比了Go和Java的内存管理与垃圾回收机制。Java依赖JVM自动管理内存,使用堆栈内存并采用多种垃圾回收算法,如标记-清除和分代收集。Go则提供更多的手动控制,内存分配与释放由分配器和垃圾回收器协同完成,使用三色标记算法并发回收。示例展示了Java中对象自动创建和销毁,而Go中开发者需注意内存泄漏。选择语言应根据项目需求和技术栈来决定。
|
3天前
|
存储 缓存 监控
|
6天前
|
Arthas 监控 IDE
去哪儿网开源的一个对应用透明,无侵入的Java应用诊断工具
今天 V 哥给大家带来一款开源工具Bistoury,Bistoury 是去哪儿网开源的一个对应用透明,无侵入的java应用诊断工具,用于提升开发人员的诊断效率和能力。
|
6天前
|
存储 机器学习/深度学习 Java
【Java探索之旅】数组使用 初探JVM内存布局
【Java探索之旅】数组使用 初探JVM内存布局
17 0
|
7天前
|
安全 Java 编译器
Java面向对象思想以及原理以及内存图解(下)
Java面向对象思想以及原理以及内存图解(下)
17 0
|
7天前
|
Java
Java面向对象思想以及原理以及内存图解(上)
Java面向对象思想以及原理以及内存图解
18 0
|
9天前
|
缓存 Java 编译器
Java内存模型JMM详解
Java内存模型JMM详解
12 0
|
9天前
|
Java 开发者
【JAVA】Java内存模型中的happen-before
Happen-before关系帮助开发者理解多线程程序中操作的执行顺序和可见性,从而避免竞态条件和数据不一致性问题。它提供了一种可预测的规则来确保线程之间的正确交互。
20 0