二级缓存在实际项目中的应用

发布于:2025-09-09 ⋅ 阅读:(25) ⋅ 点赞:(0)

二级缓存在项目中的应用

目录


1. 二级缓存简介

1.1 什么是二级缓存

二级缓存(Second-Level Cache) 是Hibernate框架中的一个重要特性,它提供了应用程序级别的缓存机制。与一级缓存(Session级别)不同,二级缓存是SessionFactory级别的缓存,可以在多个Session之间共享数据。

1.2 缓存层次结构

应用层缓存架构
├── 一级缓存 (L1 Cache)
│   ├── Session级别
│   ├── 生命周期短
│   └── 自动管理
├── 二级缓存 (L2 Cache)
│   ├── SessionFactory级别
│   ├── 生命周期长
│   └── 需要配置
└── 查询缓存 (Query Cache)
    ├── 查询结果缓存
    ├── 依赖二级缓存
    └── 需要显式启用

1.3 二级缓存的特点

  • 跨Session共享: 多个Session可以共享缓存数据
  • 生命周期长: 与SessionFactory生命周期一致
  • 可配置性: 支持多种缓存提供者
  • 透明性: 对应用程序透明,无需修改业务代码
  • 选择性缓存: 可以指定哪些实体需要缓存

1.4 支持的缓存提供者

缓存提供者 特点 适用场景
EHCache 成熟稳定,功能丰富 单机应用
Redis 分布式,高性能 集群环境
Hazelcast 内存网格,分布式 微服务架构
Caffeine 高性能,低延迟 高并发场景
Infinispan 企业级,事务支持 复杂业务场景

2. 应用场景

2.1 适用场景

2.1.1 读多写少的场景
// 用户信息查询 - 读多写少
@Entity
@Cacheable
public class User {
    @Id
    private Long id;
    private String username;
    private String email;
    // 用户信息变化不频繁,适合缓存
}
2.1.2 数据变化不频繁
// 字典数据 - 变化不频繁
@Entity
@Cacheable
public class Dictionary {
    @Id
    private Long id;
    private String type;
    private String code;
    private String value;
    // 字典数据相对稳定,适合缓存
}
2.1.3 复杂查询结果
// 统计报表数据 - 复杂查询
@Entity
@Cacheable
public class ReportData {
    @Id
    private Long id;
    private String reportType;
    private LocalDate reportDate;
    private BigDecimal amount;
    // 报表数据计算复杂,适合缓存
}

2.2 不适用场景

2.2.1 频繁更新的数据
// 实时数据 - 频繁更新
@Entity
public class RealTimeData {
    @Id
    private Long id;
    private BigDecimal price;
    private LocalDateTime updateTime;
    // 价格数据实时变化,不适合缓存
}
2.2.2 敏感数据
// 用户密码 - 敏感数据
@Entity
public class UserCredentials {
    @Id
    private Long id;
    private String password;
    private String salt;
    // 密码等敏感信息不应缓存
}

2.3 典型应用案例

2.3.1 电商系统
// 商品信息缓存
@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Product {
    @Id
    private Long id;
    private String name;
    private BigDecimal price;
    private String description;
    private String category;
  
    // 商品信息相对稳定,适合缓存
    // 价格可能变化,需要及时更新
}
2.3.2 内容管理系统
// 文章内容缓存
@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class Article {
    @Id
    private Long id;
    private String title;
    private String content;
    private String author;
    private LocalDateTime publishTime;
  
    // 已发布的文章内容不变,适合只读缓存
}
2.3.3 用户权限系统
// 角色权限缓存
@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Role {
    @Id
    private Long id;
    private String roleName;
    @OneToMany(mappedBy = "role")
    private Set<Permission> permissions;
  
    // 角色权限变化不频繁,适合缓存
}

3. 重难点分析

3.1 技术难点

3.1.1 缓存一致性

难点: 保证缓存与数据库数据的一致性
解决方案:

// 缓存一致性策略配置
@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class User {
    @Id
    private Long id;
    private String username;
    private String email;
  
    // 使用READ_WRITE策略保证一致性
    // 读操作从缓存获取
    // 写操作同时更新缓存和数据库
}

// 缓存更新策略
@Service
public class UserService {
  
    @Transactional
    public User updateUser(User user) {
        // 1. 更新数据库
        User updatedUser = userRepository.save(user);
      
        // 2. 清除相关缓存
        evictUserCache(user.getId());
      
        // 3. 重新加载到缓存
        loadUserToCache(updatedUser.getId());
      
        return updatedUser;
    }
  
    private void evictUserCache(Long userId) {
        // 清除用户相关缓存
        cacheManager.getCache("userCache").evict(userId);
    }
}
3.1.2 缓存穿透

难点: 查询不存在的数据导致缓存失效
解决方案:

@Service
public class UserService {
  
    @Cacheable(value = "userCache", key = "#id")
    public User findById(Long id) {
        User user = userRepository.findById(id).orElse(null);
      
        if (user == null) {
            // 缓存空值,防止缓存穿透
            return new User(); // 返回空对象
        }
      
        return user;
    }
  
    // 布隆过滤器防止缓存穿透
    @Autowired
    private BloomFilter<Long> userBloomFilter;
  
    public User findByIdWithBloomFilter(Long id) {
        // 先检查布隆过滤器
        if (!userBloomFilter.mightContain(id)) {
            return null; // 确定不存在
        }
      
        return findById(id);
    }
}
3.1.3 缓存雪崩

难点: 大量缓存同时失效导致数据库压力
解决方案:

@Service
public class UserService {
  
    @Cacheable(value = "userCache", key = "#id")
    public User findById(Long id) {
        // 添加随机过期时间,避免同时失效
        return userRepository.findById(id).orElse(null);
    }
  
    // 缓存预热
    @PostConstruct
    public void warmUpCache() {
        List<User> users = userRepository.findAll();
        users.forEach(user -> {
            cacheManager.getCache("userCache").put(user.getId(), user);
        });
    }
  
    // 熔断机制
    @HystrixCommand(fallbackMethod = "getUserFallback")
    public User getUserWithCircuitBreaker(Long id) {
        return findById(id);
    }
  
    public User getUserFallback(Long id) {
        // 降级处理
        return new User();
    }
}

3.2 性能难点

3.2.1 内存管理

难点: 缓存占用大量内存
解决方案:

// EHCache配置
@Configuration
public class CacheConfig {
  
    @Bean
    public CacheManager cacheManager() {
        EhCacheManagerFactoryBean factory = new EhCacheManagerFactoryBean();
        factory.setConfigLocation(new ClassPathResource("ehcache.xml"));
        factory.setShared(true);
      
        EhCacheCacheManager cacheManager = new EhCacheCacheManager();
        cacheManager.setCacheManager(factory.getObject());
      
        return cacheManager;
    }
}

// ehcache.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
    <cache name="userCache"
           maxEntriesLocalHeap="1000"
           maxEntriesLocalDisk="10000"
           eternal="false"
           timeToIdleSeconds="300"
           timeToLiveSeconds="600"
           memoryStoreEvictionPolicy="LRU"
           diskPersistent="false"
           diskExpiryThreadIntervalSeconds="120"/>
</ehcache>
3.2.2 序列化性能

难点: 对象序列化影响性能
解决方案:

// 使用高效的序列化方式
@Configuration
public class RedisConfig {
  
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
      
        // 使用Jackson序列化
        Jackson2JsonRedisSerializer<Object> serializer = 
            new Jackson2JsonRedisSerializer<>(Object.class);
      
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);
      
        return template;
    }
}

// 实体类优化
@Entity
@Cacheable
public class User implements Serializable {
    private static final long serialVersionUID = 1L;
  
    @Id
    private Long id;
    private String username;
    private String email;
  
    // 避免循环引用
    @JsonIgnore
    @OneToMany(mappedBy = "user")
    private Set<Order> orders;
}

3.3 业务难点

3.3.1 缓存策略选择

难点: 选择合适的缓存策略
解决方案:

// 不同场景的缓存策略
@Entity
@Cacheable
public class Product {
    @Id
    private Long id;
    private String name;
    private BigDecimal price;
  
    // 根据业务特点选择策略
    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
    private String description; // 可读写
  
    @Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
    private String category; // 只读
  
    @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
    private Integer stock; // 非严格读写
}
3.3.2 缓存更新策略

难点: 确定何时更新缓存
解决方案:

@Service
public class ProductService {
  
    // 主动更新策略
    @Transactional
    public Product updateProduct(Product product) {
        Product updated = productRepository.save(product);
      
        // 立即更新缓存
        cacheManager.getCache("productCache").put(product.getId(), updated);
      
        return updated;
    }
  
    // 延迟更新策略
    @Transactional
    public Product updateProductLazy(Product product) {
        Product updated = productRepository.save(product);
      
        // 延迟更新缓存
        CompletableFuture.runAsync(() -> {
            cacheManager.getCache("productCache").put(product.getId(), updated);
        });
      
        return updated;
    }
  
    // 版本控制策略
    @Entity
    @Cacheable
    public class Product {
        @Id
        private Long id;
        private String name;
      
        @Version
        private Long version; // 乐观锁版本
      
        // 版本变化时自动清除缓存
    }
}

4. 结合SpringBoot使用

4.1 环境准备

4.1.1 依赖配置
<!-- pom.xml -->
<dependencies>
    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
  
    <!-- Spring Boot Data JPA -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
  
    <!-- Spring Boot Cache -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
  
    <!-- Hibernate Core -->
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
    </dependency>
  
    <!-- EHCache -->
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-ehcache</artifactId>
    </dependency>
  
    <!-- Redis -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
</dependencies>
4.1.2 配置文件
# application.yml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/testdb
    username: root
    password: password
    driver-class-name: com.mysql.cj.jdbc.Driver
  
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    properties:
      hibernate:
        cache:
          use_second_level_cache: true
          use_query_cache: true
          region:
            factory_class: org.hibernate.cache.ehcache.EhCacheRegionFactory
        format_sql: true
  
  cache:
    type: ehcache
    ehcache:
      config: classpath:ehcache.xml
  
  redis:
    host: localhost
    port: 6379
    password: 
    timeout: 2000ms
    lettuce:
      pool:
        max-active: 8
        max-wait: -1ms
        max-idle: 8
        min-idle: 0

4.2 基础配置

4.2.1 缓存配置类
@Configuration
@EnableCaching
public class CacheConfig {
  
    // EHCache配置
    @Bean
    public CacheManager ehCacheManager() {
        EhCacheManagerFactoryBean factory = new EhCacheManagerFactoryBean();
        factory.setConfigLocation(new ClassPathResource("ehcache.xml"));
        factory.setShared(true);
      
        EhCacheCacheManager cacheManager = new EhCacheCacheManager();
        cacheManager.setCacheManager(factory.getObject());
      
        return cacheManager;
    }
  
    // Redis配置
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
      
        // 序列化配置
        Jackson2JsonRedisSerializer<Object> serializer = 
            new Jackson2JsonRedisSerializer<>(Object.class);
      
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);
      
        return template;
    }
  
    // 缓存键生成器
    @Bean
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }
}
4.2.2 EHCache配置文件
<!-- ehcache.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
  
    <!-- 默认缓存配置 -->
    <defaultCache
            maxEntriesLocalHeap="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxEntriesLocalDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </defaultCache>
  
    <!-- 用户缓存 -->
    <cache name="userCache"
           maxEntriesLocalHeap="1000"
           eternal="false"
           timeToIdleSeconds="300"
           timeToLiveSeconds="600"
           memoryStoreEvictionPolicy="LRU"
           diskPersistent="false"/>
  
    <!-- 商品缓存 -->
    <cache name="productCache"
           maxEntriesLocalHeap="5000"
           eternal="false"
           timeToIdleSeconds="600"
           timeToLiveSeconds="1200"
           memoryStoreEvictionPolicy="LRU"
           diskPersistent="false"/>
  
    <!-- 查询缓存 -->
    <cache name="queryCache"
           maxEntriesLocalHeap="10000"
           eternal="false"
           timeToIdleSeconds="300"
           timeToLiveSeconds="600"
           memoryStoreEvictionPolicy="LRU"
           diskPersistent="false"/>
</ehcache>

4.3 实体类配置

4.3.1 基础实体类
@Entity
@Table(name = "users")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class User {
  
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
  
    @Column(unique = true, nullable = false)
    private String username;
  
    @Column(unique = true, nullable = false)
    private String email;
  
    @Column(name = "created_at")
    private LocalDateTime createdAt;
  
    @Column(name = "updated_at")
    private LocalDateTime updatedAt;
  
    // 关联实体不缓存,避免循环引用
    @OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
    @JsonIgnore
    private Set<Order> orders = new HashSet<>();
  
    // 构造方法
    public User() {}
  
    public User(String username, String email) {
        this.username = username;
        this.email = email;
        this.createdAt = LocalDateTime.now();
        this.updatedAt = LocalDateTime.now();
    }
  
    // Getter和Setter方法
    // ...
}
4.3.2 商品实体类
@Entity
@Table(name = "products")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Product {
  
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
  
    @Column(nullable = false)
    private String name;
  
    @Column(columnDefinition = "TEXT")
    private String description;
  
    @Column(precision = 10, scale = 2)
    private BigDecimal price;
  
    @Column(name = "category_id")
    private Long categoryId;
  
    @Column(name = "stock_quantity")
    private Integer stockQuantity;
  
    @Column(name = "created_at")
    private LocalDateTime createdAt;
  
    @Column(name = "updated_at")
    private LocalDateTime updatedAt;
  
    // 构造方法
    public Product() {}
  
    public Product(String name, String description, BigDecimal price, Long categoryId) {
        this.name = name;
        this.description = description;
        this.price = price;
        this.categoryId = categoryId;
        this.stockQuantity = 0;
        this.createdAt = LocalDateTime.now();
        this.updatedAt = LocalDateTime.now();
    }
  
    // Getter和Setter方法
    // ...
}

4.4 服务层实现

4.4.1 用户服务
@Service
@Transactional
public class UserService {
  
    @Autowired
    private UserRepository userRepository;
  
    @Autowired
    private CacheManager cacheManager;
  
    // 根据ID查找用户(使用缓存)
    @Cacheable(value = "userCache", key = "#id")
    public User findById(Long id) {
        return userRepository.findById(id)
                .orElseThrow(() -> new EntityNotFoundException("User not found with id: " + id));
    }
  
    // 根据用户名查找用户
    @Cacheable(value = "userCache", key = "#username")
    public User findByUsername(String username) {
        return userRepository.findByUsername(username)
                .orElseThrow(() -> new EntityNotFoundException("User not found with username: " + username));
    }
  
    // 保存用户(清除相关缓存)
    @CacheEvict(value = "userCache", allEntries = true)
    public User save(User user) {
        user.setUpdatedAt(LocalDateTime.now());
        return userRepository.save(user);
    }
  
    // 更新用户(清除相关缓存)
    @CacheEvict(value = "userCache", key = "#user.id")
    public User update(User user) {
        user.setUpdatedAt(LocalDateTime.now());
        return userRepository.save(user);
    }
  
    // 删除用户(清除相关缓存)
    @CacheEvict(value = "userCache", key = "#id")
    public void deleteById(Long id) {
        userRepository.deleteById(id);
    }
  
    // 批量清除缓存
    @CacheEvict(value = "userCache", allEntries = true)
    public void clearUserCache() {
        // 清除所有用户缓存
    }
  
    // 手动缓存管理
    public void evictUserCache(Long userId) {
        Cache cache = cacheManager.getCache("userCache");
        if (cache != null) {
            cache.evict(userId);
        }
    }
  
    public void putUserToCache(User user) {
        Cache cache = cacheManager.getCache("userCache");
        if (cache != null) {
            cache.put(user.getId(), user);
        }
    }
}
4.4.2 商品服务
@Service
@Transactional
public class ProductService {
  
    @Autowired
    private ProductRepository productRepository;
  
    @Autowired
    private CacheManager cacheManager;
  
    // 根据ID查找商品
    @Cacheable(value = "productCache", key = "#id")
    public Product findById(Long id) {
        return productRepository.findById(id)
                .orElseThrow(() -> new EntityNotFoundException("Product not found with id: " + id));
    }
  
    // 根据分类查找商品
    @Cacheable(value = "productCache", key = "'category:' + #categoryId")
    public List<Product> findByCategoryId(Long categoryId) {
        return productRepository.findByCategoryId(categoryId);
    }
  
    // 搜索商品
    @Cacheable(value = "productCache", key = "'search:' + #keyword")
    public List<Product> searchProducts(String keyword) {
        return productRepository.findByNameContainingIgnoreCase(keyword);
    }
  
    // 保存商品
    @CacheEvict(value = "productCache", allEntries = true)
    public Product save(Product product) {
        product.setUpdatedAt(LocalDateTime.now());
        return productRepository.save(product);
    }
  
    // 更新商品
    @CacheEvict(value = "productCache", key = "#product.id")
    public Product update(Product product) {
        product.setUpdatedAt(LocalDateTime.now());
        return productRepository.save(product);
    }
  
    // 更新库存
    @CacheEvict(value = "productCache", key = "#productId")
    public Product updateStock(Long productId, Integer quantity) {
        Product product = findById(productId);
        product.setStockQuantity(quantity);
        product.setUpdatedAt(LocalDateTime.now());
        return productRepository.save(product);
    }
  
    // 条件更新缓存
    @CacheEvict(value = "productCache", condition = "#product.price > 100")
    public Product updateExpensiveProduct(Product product) {
        return productRepository.save(product);
    }
}

4.5 控制器层

4.5.1 用户控制器
@RestController
@RequestMapping("/api/users")
public class UserController {
  
    @Autowired
    private UserService userService;
  
    // 获取用户信息
    @GetMapping("/{id}")
    public ResponseEntity<User> getUser(@PathVariable Long id) {
        try {
            User user = userService.findById(id);
            return ResponseEntity.ok(user);
        } catch (EntityNotFoundException e) {
            return ResponseEntity.notFound().build();
        }
    }
  
    // 创建用户
    @PostMapping
    public ResponseEntity<User> createUser(@RequestBody User user) {
        User savedUser = userService.save(user);
        return ResponseEntity.status(HttpStatus.CREATED).body(savedUser);
    }
  
    // 更新用户
    @PutMapping("/{id}")
    public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) {
        try {
            user.setId(id);
            User updatedUser = userService.update(user);
            return ResponseEntity.ok(updatedUser);
        } catch (EntityNotFoundException e) {
            return ResponseEntity.notFound().build();
        }
    }
  
    // 删除用户
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        try {
            userService.deleteById(id);
            return ResponseEntity.noContent().build();
        } catch (EntityNotFoundException e) {
            return ResponseEntity.notFound().build();
        }
    }
  
    // 清除用户缓存
    @PostMapping("/cache/clear")
    public ResponseEntity<String> clearUserCache() {
        userService.clearUserCache();
        return ResponseEntity.ok("User cache cleared successfully");
    }
}
4.5.2 商品控制器
@RestController
@RequestMapping("/api/products")
public class ProductController {
  
    @Autowired
    private ProductService productService;
  
    // 获取商品信息
    @GetMapping("/{id}")
    public ResponseEntity<Product> getProduct(@PathVariable Long id) {
        try {
            Product product = productService.findById(id);
            return ResponseEntity.ok(product);
        } catch (EntityNotFoundException e) {
            return ResponseEntity.notFound().build();
        }
    }
  
    // 根据分类获取商品
    @GetMapping("/category/{categoryId}")
    public ResponseEntity<List<Product>> getProductsByCategory(@PathVariable Long categoryId) {
        List<Product> products = productService.findByCategoryId(categoryId);
        return ResponseEntity.ok(products);
    }
  
    // 搜索商品
    @GetMapping("/search")
    public ResponseEntity<List<Product>> searchProducts(@RequestParam String keyword) {
        List<Product> products = productService.searchProducts(keyword);
        return ResponseEntity.ok(products);
    }
  
    // 创建商品
    @PostMapping
    public ResponseEntity<Product> createProduct(@RequestBody Product product) {
        Product savedProduct = productService.save(product);
        return ResponseEntity.status(HttpStatus.CREATED).body(savedProduct);
    }
  
    // 更新商品
    @PutMapping("/{id}")
    public ResponseEntity<Product> updateProduct(@PathVariable Long id, @RequestBody Product product) {
        try {
            product.setId(id);
            Product updatedProduct = productService.update(product);
            return ResponseEntity.ok(updatedProduct);
        } catch (EntityNotFoundException e) {
            return ResponseEntity.notFound().build();
        }
    }
  
    // 更新库存
    @PutMapping("/{id}/stock")
    public ResponseEntity<Product> updateStock(@PathVariable Long id, @RequestParam Integer quantity) {
        try {
            Product product = productService.updateStock(id, quantity);
            return ResponseEntity.ok(product);
        } catch (EntityNotFoundException e) {
            return ResponseEntity.notFound().build();
        }
    }
}

4.6 查询缓存

4.6.1 查询缓存配置
@Repository
public class UserRepository extends JpaRepository<User, Long> {
  
    // 启用查询缓存
    @QueryHints(@QueryHint(name = "org.hibernate.cacheable", value = "true"))
    @Query("SELECT u FROM User u WHERE u.username = :username")
    Optional<User> findByUsername(@Param("username") String username);
  
    // 复杂查询缓存
    @QueryHints(@QueryHint(name = "org.hibernate.cacheable", value = "true"))
    @Query("SELECT u FROM User u WHERE u.createdAt >= :startDate AND u.createdAt <= :endDate")
    List<User> findUsersByDateRange(@Param("startDate") LocalDateTime startDate, 
                                   @Param("endDate") LocalDateTime endDate);
  
    // 统计查询缓存
    @QueryHints(@QueryHint(name = "org.hibernate.cacheable", value = "true"))
    @Query("SELECT COUNT(u) FROM User u WHERE u.createdAt >= :startDate")
    Long countUsersSince(@Param("startDate") LocalDateTime startDate);
}
4.6.2 查询缓存服务
@Service
public class UserQueryService {
  
    @Autowired
    private UserRepository userRepository;
  
    // 缓存查询结果
    @Cacheable(value = "queryCache", key = "'users_by_date:' + #startDate + '_' + #endDate")
    public List<User> findUsersByDateRange(LocalDateTime startDate, LocalDateTime endDate) {
        return userRepository.findUsersByDateRange(startDate, endDate);
    }
  
    // 缓存统计结果
    @Cacheable(value = "queryCache", key = "'user_count_since:' + #startDate")
    public Long countUsersSince(LocalDateTime startDate) {
        return userRepository.countUsersSince(startDate);
    }
  
    // 清除查询缓存
    @CacheEvict(value = "queryCache", allEntries = true)
    public void clearQueryCache() {
        // 清除所有查询缓存
    }
}

5. 最佳实践与案例

5.1 缓存策略最佳实践

5.1.1 缓存粒度控制
// 细粒度缓存 - 单个实体
@Cacheable(value = "userCache", key = "#id")
public User findById(Long id) {
    return userRepository.findById(id).orElse(null);
}

// 粗粒度缓存 - 列表数据
@Cacheable(value = "userListCache", key = "'all_users'")
public List<User> findAllUsers() {
    return userRepository.findAll();
}

// 条件缓存 - 根据条件决定是否缓存
@Cacheable(value = "userCache", key = "#id", condition = "#id > 0")
public User findByIdConditional(Long id) {
    return userRepository.findById(id).orElse(null);
}
5.1.2 缓存更新策略
@Service
public class CacheUpdateService {
  
    @Autowired
    private CacheManager cacheManager;
  
    // 立即更新策略
    @CachePut(value = "userCache", key = "#user.id")
    public User updateUserImmediate(User user) {
        return userRepository.save(user);
    }
  
    // 延迟更新策略
    @Async
    @CacheEvict(value = "userCache", key = "#user.id")
    public void updateUserLazy(User user) {
        userRepository.save(user);
        // 异步更新缓存
        CompletableFuture.runAsync(() -> {
            putUserToCache(user);
        });
    }
  
    // 批量更新策略
    @CacheEvict(value = "userCache", allEntries = true)
    public void batchUpdateUsers(List<User> users) {
        userRepository.saveAll(users);
    }
}

5.2 性能优化案例

5.2.1 缓存预热
@Component
public class CacheWarmupService {
  
    @Autowired
    private UserService userService;
  
    @Autowired
    private ProductService productService;
  
    @EventListener(ApplicationReadyEvent.class)
    public void warmupCache() {
        // 预热用户缓存
        warmupUserCache();
      
        // 预热商品缓存
        warmupProductCache();
    }
  
    private void warmupUserCache() {
        List<User> users = userService.findAll();
        users.forEach(user -> {
            userService.findById(user.getId()); // 触发缓存
        });
    }
  
    private void warmupProductCache() {
        List<Product> products = productService.findAll();
        products.forEach(product -> {
            productService.findById(product.getId()); // 触发缓存
        });
    }
}
5.2.2 缓存监控
@Component
public class CacheMonitorService {
  
    @Autowired
    private CacheManager cacheManager;
  
    // 获取缓存统计信息
    public Map<String, Object> getCacheStats() {
        Map<String, Object> stats = new HashMap<>();
      
        cacheManager.getCacheNames().forEach(cacheName -> {
            Cache cache = cacheManager.getCache(cacheName);
            if (cache instanceof EhCache) {
                EhCache ehCache = (EhCache) cache;
                Map<String, Object> cacheStats = new HashMap<>();
                cacheStats.put("hitCount", ehCache.getStatistics().getCacheHits());
                cacheStats.put("missCount", ehCache.getStatistics().getCacheMisses());
                cacheStats.put("hitRate", ehCache.getStatistics().getCacheHitPercentage());
                stats.put(cacheName, cacheStats);
            }
        });
      
        return stats;
    }
  
    // 清理过期缓存
    @Scheduled(fixedRate = 300000) // 每5分钟执行一次
    public void cleanExpiredCache() {
        cacheManager.getCacheNames().forEach(cacheName -> {
            Cache cache = cacheManager.getCache(cacheName);
            if (cache instanceof EhCache) {
                ((EhCache) cache).getNativeCache().evictExpiredElements();
            }
        });
    }
}

5.3 实际应用案例

5.3.1 电商系统缓存方案
// 商品详情页缓存策略
@Service
public class ProductDetailService {
  
    @Autowired
    private ProductService productService;
  
    @Autowired
    private ProductReviewService reviewService;
  
    // 商品详情页数据聚合
    @Cacheable(value = "productDetailCache", key = "#productId")
    public ProductDetailVO getProductDetail(Long productId) {
        Product product = productService.findById(productId);
        List<ProductReview> reviews = reviewService.findByProductId(productId);
        ProductStatistics stats = productService.getProductStatistics(productId);
      
        return ProductDetailVO.builder()
                .product(product)
                .reviews(reviews)
                .statistics(stats)
                .build();
    }
  
    // 商品列表页缓存
    @Cacheable(value = "productListCache", key = "#categoryId + '_' + #page + '_' + #size")
    public Page<Product> getProductList(Long categoryId, int page, int size) {
        return productService.findByCategoryId(categoryId, PageRequest.of(page, size));
    }
}
5.3.2 用户权限缓存方案
// 用户权限缓存
@Service
public class UserPermissionService {
  
    @Autowired
    private UserRepository userRepository;
  
    @Autowired
    private RoleRepository roleRepository;
  
    // 用户权限缓存
    @Cacheable(value = "userPermissionCache", key = "#userId")
    public UserPermissionVO getUserPermissions(Long userId) {
        User user = userRepository.findById(userId).orElse(null);
        if (user == null) {
            return null;
        }
      
        Set<Role> roles = user.getRoles();
        Set<Permission> permissions = new HashSet<>();
      
        roles.forEach(role -> {
            permissions.addAll(role.getPermissions());
        });
      
        return UserPermissionVO.builder()
                .userId(userId)
                .roles(roles)
                .permissions(permissions)
                .build();
    }
  
    // 权限检查
    public boolean hasPermission(Long userId, String permission) {
        UserPermissionVO userPermissions = getUserPermissions(userId);
        if (userPermissions == null) {
            return false;
        }
      
        return userPermissions.getPermissions().stream()
                .anyMatch(p -> p.getName().equals(permission));
    }
}

6. 总结

6.1 二级缓存优势

  1. 性能提升: 减少数据库访问,提高响应速度
  2. 可扩展性: 支持多种缓存提供者,适应不同场景
  3. 透明性: 对业务代码透明,无需修改现有逻辑
  4. 灵活性: 支持细粒度缓存控制,可配置缓存策略

6.2 使用建议

  1. 合理选择缓存策略: 根据数据特点选择合适的并发策略
  2. 注意缓存一致性: 确保缓存与数据库数据的一致性
  3. 监控缓存性能: 定期监控缓存命中率和性能指标
  4. 合理设置过期时间: 根据业务需求设置合适的缓存过期时间
  5. 避免缓存雪崩: 使用随机过期时间,避免同时失效

6.3 注意事项

  1. 内存管理: 注意缓存占用内存,合理配置缓存大小
  2. 序列化性能: 选择高效的序列化方式
  3. 缓存穿透: 防止查询不存在数据导致缓存失效
  4. 缓存雪崩: 避免大量缓存同时失效
  5. 数据一致性: 确保缓存与数据库数据的一致性

二级缓存是提升应用性能的重要手段,合理使用可以显著提高系统响应速度和用户体验。在实际应用中,需要根据具体业务场景选择合适的缓存策略和配置参数。


网站公告

今日签到

点亮在社区的每一天
去签到