基于内存和 Redis 的两级 Java 缓存框架

上一篇 / 下一篇  2024-02-29 13:14:52

  1. 简介
  J2Cache 是 OSChina 目前正在使用的两级缓存框架(要求至少 Java 8)。第一级缓存使用内存(同时支持 Ehcache 2.x、Ehcache 3.x 和 Caffeine),第二级缓存使用 Redis(推荐)/Memcached 。由于大量的缓存读取会导致 L2 的网络成为整个系统的瓶颈,因此 L1 的目标是降低对 L2 的读取次数。该缓存框架主要用于集群环境中。单机也可使用,用于避免应用重启导致的缓存冷启动后对后端业务的冲击。
  数据读取
  ·读取顺序 -> L1 -> L2 -> DB
  · 数据更新
  从数据库中读取最新数据,依次更新 L1 -> L2 ,发送广播清除某个缓存信息
  接收到广播(手工清除缓存 & 一级缓存自动失效),从 L1 中清除指定的缓存信息
  2. 实战案例
  2.1 依赖管理
  <dependency>
    <groupId>net.oschina.j2cache</groupId>
    <artifactId>j2cache-core</artifactId>
    <version>2.8.5-release</version>
  </dependency>
  <dependency>
    <groupId>net.oschina.j2cache</groupId>
    <artifactId>j2cache-spring-boot2-starter</artifactId>
    <version>2.8.0-release</version>
  </dependency>
  2.2 配置
  redis:
    # 地址, 多个地址使用‘,’逗号分割
    hosts: localhost:6379
    # 数据库索引
    database: 11
    # 密码
    password: xxxooo
    # 连接超时时间
    timeout: 10s
    # 连接池中的最小空闲连接
    min-idle: 0
    # 连接池中的最大空闲连接
    max-idle: 8
    # 连接池的最大数据库连接数
    max-active: 8
    # #连接池最大阻塞等待时间(使用负值表示没有限制)
    max-wait: -1ms
  ---
  j2cache:
    openSpringCache: true
    # 缓存中不存在时,运行缓存空对象
    allowNullValues: true
    redisClient: lettuce
    l2CacheOpen: true
    # 一级缓存使用caffeine
    L1:
      provider_class: caffeine
    L2:
      #使用springRedis替换二级缓存
      provider_class: net.oschina.j2cache.cache.support.redis.SpringRedisProvider
      config_section: redis
    #使用springRedis进行广播通知缓失效
    broadcast: net.oschina.j2cache.cache.support.redis.SpringRedisPubSubPolicy
  # 上面配置的一级缓存为caffeine, 那么这里对一级缓存的配置就必须以这个caffeine开头  
  caffeine:
    # 配置一级,二级缓存的region,有效时间
    region.xj: 10000, 120s
  ---
  spring:
    cache:
      # 一级缓存使用caffeine
      type: caffeine
  2.3 核心操作类
  @Service
  public class UserService {
    private final UserRepository userRepository ;
    public UserService(UserRepository userRepository) {
      this.userRepository = userRepository ;
    }
    @Transactional
    public User save(User user) {
      return this.userRepository.saveAndFlush(user) ;
    }
    @Cacheable(value = {"xj"}, key = "#id")
    public User get(Long id) {
      return this.userRepository.findById(id).orElse(null) ;
    }
    @Transactional
    @CacheEvict(value = {"xj"}, key = "#id")
    public void remove(Long id) {
      this.userRepository.deleteById(id) ;
    }
  }
  以上是基本的操作,非常简单。
  2.4 Controller接口
  @RestController
  @RequestMapping("/users")
  public class UserController {
    private final UserService userService ;
    // 通过CacheChannel操作j2cache缓存方法
    private final CacheChannel cacheChannel;
    
    public UserController(UserService userService, CacheChannel cacheChannel) {
      this.userService = userService ;
      this.cacheChannel = cacheChannel ; 
    }
    
    @GetMapping("/save")
    public User save() {
      User user = new User() ;
      int num = new Random().nextInt(80);
      user.setAge(num) ;
      user.setName("姓名 - " + num) ;
      user.setSex(num >= 50 ? "男" : "女") ;
      return this.userService.save(user) ;
    }
    
    @GetMapping("/{id}")
    public Object get(@PathVariable("id") Long id) {
      // 从指定的region,指定的key获取数据,如果一级,二级缓存中不存在,则通过第三个参数Function手动获取
      // 如果缓存中不存在时,同时配置了允许缓存空对象,则会缓存一个空对象到缓存中
      return this.cacheChannel.get("xj", id.toString(), key -> this.userService.get(id) , true) ;
    }
    
    @GetMapping("/delete/{id}")
    public Object remove(@PathVariable("id") Long id) {
      this.userService.remove(id) ;
      return "success" ;
    }
    
  }
  2.5 测试
  先通过save接口添加数据
  查询id=2的数据
  level=3 表示本次数据缓存中不存在,从数据库中获取的。刷新页面
  level=2,本次数据从二级缓存redis中获取。再次刷新页面
  level=1,本次数据从一级缓存caffeine中获取。后续再怎么刷新只要缓存没有过期都将从一级缓存中获取。
  测试不存在的数据
  从数据库中查询不存在的数据。
  缓存了空对象。
  测试删除数据
  缓存中会立即清除
  以上是本篇文章的全部内容,希望对你有帮助。
  完毕!!!

TAG: 软件开发 Java java

 

评分:0

我来说两句

Open Toolbar