java实现本地缓存的示例代码

 更新时间:2024年01月08日 14:59:04   作者:代码小人物  
在高性能服务架构设计中,缓存是不可或缺的环节,因此这篇文章主要为大家详细介绍了java中如何实现本地缓存,感兴趣的小伙伴可以了解一下

在高性能服务架构设计中,缓存是不可或缺的环节。在实际项目中,我们通常会将一些热点数据存储在Redis或Memcached等缓存中间件中,只有在缓存访问未命中时才查询数据库。

在提高访问速度的同时,还可以减轻数据库的压力。

为什么要使用本地缓存

随着不断的发展,这个架构也得到了完善。在某些场景下,仅仅使用Redis类的远程缓存可能还不够。需要进一步与本地缓存配合使用,比如Guava或者Caffeine,从而再次提高程序的响应速度和服务性能。

由此,形成了以本地缓存作为一级缓存、远程缓存作为二级缓存的二级缓存架构。

总结:

  • 本地缓存基于本地环境的内存,访问速度非常快。对于一些变化频率不高、实时性要求不高的数据,可以放在本地缓存中,以提高访问速度。
  • 使用本地缓存可以减少与Redis类的远程缓存的数据交互,减少网络I/O开销,减少这个过程中网络通信的耗时。

本地存储的基本功能

  • 它可以存储、读取和写入。
  • 原子操作(线程安全),例如ConcurrentHashMap。
  • 可以设置缓存的最大限制。
  • 超过最大限制有相应的淘汰策略,如LRU、LFU。
  • 统计监控。

方案选择

1.使用ConcurrentHashMap

缓存的本质是KV存储在内存中的数据结构,对应JDK中的线程安全ConcurrentHashMap,但是要实现缓存,需要考虑消除、最大限制、消除缓存过期时间等功能。

优点ConcurrentHashMap是实现简单,不需要引入第三方包,所以比较适合一些简单的业务场景。

缺点是如果需要更多的功能,需要定制开发,成本会比较高,稳定性和可靠性难以保证。

对于更复杂的场景,建议使用相对稳定的开源工具。

2. 使用Guava缓存

Guava是Google团队开源的一个Java核心增强库。它包括集合、并发原语、缓存、IO、反射和其他工具箱。性能和稳定性有保证,应用广泛。

  • Guava Cache 支持许多功能:
  • 支持最大容量限制。
  • 支持两种过期删除策略。
  • 支持简单的统计功能。 它是基于LRU算法实现的。

示例代码如下

 <dependency><dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>31.1-jre</version>
</dependency>
@Slf4j
public class GuavaCacheTest {
    public static void main(String[] args) throws ExecutionException {
        Cache<String, String> cache = CacheBuilder.newBuilder()
                .initialCapacity(5)  // 初始容量
                .maximumSize(10)   // 缓存的最大数量,超过该数量将被淘汰
                .expireAfterWrite(60, TimeUnit.SECONDS) // 过期时间
                .build();

        String orderId = String.valueOf(123456789);
        String orderInfo = cache.get(orderId, () -> getInfo(orderId));
        log.info("orderInfo = {}", orderInfo);

    }

    private static String getInfo(String orderId) {
        String info = "";
        // 首先查redis
        log.info("get data from redis");
        // redis不存在 查db
        log.info("get data from mysql");
        info = String.format("{orderId=%s}", orderId);
        return info;
    }
}

3. 使用Encache

Encache是​一个纯Java进程内缓存框架,快速且精简。它是 Hibernate 中默认的 CacheProvider。 与Caffeine和Guava Cache相比,Encache功能更丰富,可扩展性更强。 支持LRU、LFU、FIFO等多种缓存淘汰算法。

缓存支持三种类型:堆内存储、堆外存储、磁盘存储(支持持久化)。

支持多种集群方案,解决数据共享问题。

使用方法如下:

 <dependency><dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.9.7</version>
</dependency>
@Slf4j
public class EhcacheTest {
    private static final String ORDER_CACHE = "orderCache";
    public static void main(String[] args) {
        CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
                // 创建实例
                .withCache(ORDER_CACHE, CacheConfigurationBuilder
              // 声明一个容量为20的堆内缓存
                        .newCacheConfigurationBuilder(String.class, String.class, ResourcePoolsBuilder.heap(20)))
                .build(true);
        // 得到缓存实例
        Cache<String, String> cache = cacheManager.getCache(ORDER_CACHE, String.class, String.class);

        String orderId = String.valueOf(123456789);
        String orderInfo = cache.get(orderId);
        if (StrUtil.isBlank(orderInfo)) {
            orderInfo = getInfo(orderId);
            cache.put(orderId, orderInfo);
        }
        log.info("orderInfo = {}", orderInfo);
    }

    private static String getInfo(String orderId) {
        String info = "";
        // 首先从redis查
        log.info("get data from redis");
        // 不存在 查db
        log.info("get data from mysql");
        info = String.format("{orderId=%s}", orderId);
        return info;
    }
}

本地缓存常见问题

缓存一致性

二级缓存和数据库中的数据必须一致。一旦数据被修改,本地缓存和远程缓存应在数据库修改的同时同步更新。

解决方案1:使用MQ。

目前的部署一般都是集群部署,不同节点有多个本地缓存。 可以利用MQ的广播模式,在数据修改时向MQ发送消息,由节点监听并消费该消息,删除本地缓存,达到最终一致性。

解决方案二:Canal+MQ。

如果你的业务代码中不想发送MQ消息,也可以应用近年来比较流行的方法:订阅数据库变更日志,然后操作缓存。 Canal订阅Mysql的Binlog日志,当发生变化时向MQ发送消息,从而也实现了数据的一致性。

如何提高缓存命中率

根据业务场景设计合理的时效性。

缓存适用于读多写少的场景,尽可能关注访问频率高、时效性要求不高的热点业务。 访问频率越高,点击率越高。 时效性越低,缓存时间越长,相同key、相同请求数下命中率越高。

设计合理的缓存粒度。

缓存的粒度越小,缓存命中率越高。 单个key缓存的数据单元越小,被改变的可能性就越小。

设计合理的缓存过期策略。

这里的缓存过期策略并不是redis内置的定期删除和惰性删除策略,而是根据业务场景优化了key的过期时间。 例如,如果用户的关键信息同时过期,那么当多个用户同时查询时,都会落入数据库,也就是说避免缓存同时失效。

合理的缓存预加载。

redis缓存必须从数据库加载,所以当第一次使用数据时,redis需要从数据库加载数据。 我们可以在用户访问之前将需要的数据提前加载到缓存中,这样用户第一次访问时就可以直接去缓存而不用去查询数据库。

防止缓存崩溃和击穿。

缓存击穿和崩溃也会影响缓存命中率。当然,如果发生的话,应用失败的可能性很大。

设计合理的缓存容量。

注意缓存容量,如果太小,会触发redis内存淘汰机制。线上redis一般配置maxmemory-policy allkeys-lru算法进行内存消除。这样就会删除一些key,造成缓存穿透,从而降低缓存命中率,所以需要合理配置缓存容量。

到此这篇关于java实现本地缓存的示例代码的文章就介绍到这了,更多相关java本地缓存内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringSecurity中的Filter Chain(过滤器链)

    SpringSecurity中的Filter Chain(过滤器链)

    Spring Security的Filter Chain是由一系列过滤器组成的管道,每个过滤器执行特定的安全功能,Spring Security能够提供强大而灵活的安全控制机制,从而保护你的应用程序不受各种网络安全威胁的侵害,本文介绍SpringSecurity中的Filter Chain,感兴趣的朋友跟随小编一起看看吧
    2024-06-06
  • Java中的Semaphore源码分析

    Java中的Semaphore源码分析

    这篇文章主要介绍了Java中的Semaphore源码分析,Semaphore是一个访问公共资源的线程数量如限流、停车等,它是一个基于AQS实现的共享锁,主要是通过控制state变量来实现,需要的朋友可以参考下
    2023-11-11
  • Spring Data JPA实现数据访问层简化高效开发

    Spring Data JPA实现数据访问层简化高效开发

    这篇文章主要介绍了Spring Data JPA实现数据访问层简化高效开发,数据访问层的开发是一个重要且繁琐的部分,传统的JDBC操作需要编写大量的重复代码,包括数据库连接的建立、SQL语句的执行、结果集的处理等,需要的朋友可以参考下
    2025-05-05
  • 解决启用 Spring-Cloud-OpenFeign 配置可刷新项目无法启动的问题

    解决启用 Spring-Cloud-OpenFeign 配置可刷新项目无法启动的问题

    这篇文章主要介绍了解决启用 Spring-Cloud-OpenFeign 配置可刷新项目无法启动的问题,本文重点给大家介绍Spring-Cloud-OpenFeign的原理及问题解决方法,需要的朋友可以参考下
    2021-10-10
  • Java中JSON数据操作与处理全面指南

    Java中JSON数据操作与处理全面指南

    JSON(JavaScript Object Notation)是数据交换的轻量级格式,它以易于人类阅读和机器解析的方式组织数据,基于键值对,在Java开发中,处理JSON数据通常涉及使用Jackson、Gson、Fastjson等库,本文给大家介绍JSON数据操作与处理全面指南,感兴趣的朋友一起看看吧
    2024-10-10
  • 都9102年了,你还用for循环操作集合吗

    都9102年了,你还用for循环操作集合吗

    这篇文章主要给大家介绍了关于java中for循环操作集合使用的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者使用java具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-03-03
  • Java接口和抽象类原理详解

    Java接口和抽象类原理详解

    这篇文章主要介绍了Java接口和抽象类原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-02-02
  • 浅谈JAVA字符串匹配算法indexOf函数的实现方法

    浅谈JAVA字符串匹配算法indexOf函数的实现方法

    这篇文章主要介绍了浅谈字符串匹配算法indexOf函数的实现方法,indexOf函数我们可以查找一个字符串(模式串)是否在另一个字符串(主串)出现过。对此感兴趣的可以来了解一下
    2020-07-07
  • 若依前后端打成一个JAR包部署的完整步骤

    若依前后端打成一个JAR包部署的完整步骤

    这篇文章主要介绍了如何将若依前后端分离项目打包成jar,不使用nginx转发,前端修改了路由模式和环境变量配置,后端增加了依赖、配置了Thymeleaf和访问路径,需要的朋友可以参考下
    2025-01-01
  • 解决java.sql.SQLException:索引中丢失 IN或OUT 参数::x问题

    解决java.sql.SQLException:索引中丢失 IN或OUT 参数::x问题

    这篇文章主要介绍了解决java.sql.SQLException:索引中丢失 IN或OUT 参数::x问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12

最新评论