二级缓存在项目中的应用

目录

  • 1. 二级缓存简介
  • 2. 应用场景
  • 3. 重难点分析
  • 4. 结合SpringBoot使用
  • 5. 最佳实践与案例
  • 6. 总结

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 {@Idprivate Long id;private String username;private String email;// 用户信息变化不频繁,适合缓存
}
2.1.2 数据变化不频繁
// 字典数据 - 变化不频繁
@Entity
@Cacheable
public class Dictionary {@Idprivate Long id;private String type;private String code;private String value;// 字典数据相对稳定,适合缓存
}
2.1.3 复杂查询结果
// 统计报表数据 - 复杂查询
@Entity
@Cacheable
public class ReportData {@Idprivate Long id;private String reportType;private LocalDate reportDate;private BigDecimal amount;// 报表数据计算复杂,适合缓存
}

2.2 不适用场景

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

2.3 典型应用案例

2.3.1 电商系统
// 商品信息缓存
@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Product {@Idprivate 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 {@Idprivate 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 {@Idprivate 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 {@Idprivate Long id;private String username;private String email;// 使用READ_WRITE策略保证一致性// 读操作从缓存获取// 写操作同时更新缓存和数据库
}// 缓存更新策略
@Service
public class UserService {@Transactionalpublic 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;}// 布隆过滤器防止缓存穿透@Autowiredprivate 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);}// 缓存预热@PostConstructpublic 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 {@Beanpublic 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 {@Beanpublic 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;@Idprivate 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 {@Idprivate 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 {// 主动更新策略@Transactionalpublic Product updateProduct(Product product) {Product updated = productRepository.save(product);// 立即更新缓存cacheManager.getCache("productCache").put(product.getId(), updated);return updated;}// 延迟更新策略@Transactionalpublic Product updateProductLazy(Product product) {Product updated = productRepository.save(product);// 延迟更新缓存CompletableFuture.runAsync(() -> {cacheManager.getCache("productCache").put(product.getId(), updated);});return updated;}// 版本控制策略@Entity@Cacheablepublic class Product {@Idprivate Long id;private String name;@Versionprivate 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/testdbusername: rootpassword: passworddriver-class-name: com.mysql.cj.jdbc.Driverjpa:hibernate:ddl-auto: updateshow-sql: trueproperties:hibernate:cache:use_second_level_cache: trueuse_query_cache: trueregion:factory_class: org.hibernate.cache.ehcache.EhCacheRegionFactoryformat_sql: truecache:type: ehcacheehcache:config: classpath:ehcache.xmlredis:host: localhostport: 6379password: timeout: 2000mslettuce:pool:max-active: 8max-wait: -1msmax-idle: 8min-idle: 0

4.2 基础配置

4.2.1 缓存配置类
@Configuration
@EnableCaching
public class CacheConfig {// EHCache配置@Beanpublic 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配置@Beanpublic 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;}// 缓存键生成器@Beanpublic KeyGenerator keyGenerator() {return new KeyGenerator() {@Overridepublic 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"><!-- 默认缓存配置 --><defaultCachemaxEntriesLocalHeap="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)@JsonIgnoreprivate 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 {@Autowiredprivate UserRepository userRepository;@Autowiredprivate 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 {@Autowiredprivate ProductRepository productRepository;@Autowiredprivate 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 {@Autowiredprivate 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();}}// 创建用户@PostMappingpublic 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 {@Autowiredprivate 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);}// 创建商品@PostMappingpublic 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 {@Autowiredprivate 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 {@Autowiredprivate 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 {@Autowiredprivate UserService userService;@Autowiredprivate 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 {@Autowiredprivate 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 {@Autowiredprivate ProductService productService;@Autowiredprivate 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 {@Autowiredprivate UserRepository userRepository;@Autowiredprivate 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. 数据一致性: 确保缓存与数据库数据的一致性

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/921992.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/921992.shtml
英文地址,请注明出处:http://en.pswp.cn/news/921992.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入浅出CRC校验:从数学原理到单周期硬件实现 (2)CRC数学多项式基础

数学的优雅&#xff1a;剖开CRC的多项式除法核心看似复杂的CRC校验&#xff0c;其核心建立在优雅的数学基础之上。本文将为您揭开CRC算法的数学面纱&#xff0c;让您真正理解多项式除法的精妙之处。模2运算&#xff1a;CRC世界的特殊算术 CRC计算建立在一种特殊的代数系统上——…

软考初级有没有必要考?

对正在学习相关专业的学生或者是行业新人&#xff0c;这篇文章从软考初级的含义、适合哪些人考、考试难度等方面解答&#xff0c;帮助你判断要不要报考。一、软考初级是什么&#xff1f; 软考初级是软考体系里面的基础级别&#xff0c;主要面向在校大学生或是IT行业新人&#x…

11 Prompt 工程进阶:Few-shot 与 Chain-of-Thought

11 Prompt 工程进阶&#xff1a;Few-shot 与 Chain-of-Thought 前10节总结 & 后10节展望 在前 10 节&#xff0c;我们已经完成了 AI 产品经理的入门阶段&#xff1a; 1–3&#xff1a;理解了大模型的基本概念、Token、Prompt 基础&#xff1b;4–5&#xff1a;体验了本地部…

ARM1.(ARM体系结构)

1.基本概念嵌入式:以应用为心&#xff0c;以计算机技术为础&#xff0c;软便件可被的专用计算机系统。计算机系统的软件基本组成: 系统软件、应用软件。计算机系统的硬件基本组成&#xff1a;运算器、控制器、存诸器、输入设备、输出设备日常生活中遇到的专业术语&#xff1a…

Django全栈班v1.01 Python简介与特点 20250910

从零开始的Python编程之旅 “人生苦短&#xff0c;我用Python。”这不仅仅是Python程序员的口头禅&#xff0c;更是对Python强大能力的最好诠释&#xff01;&#xff01;&#xff01; 为什么全世界有超过1500万开发者选择Python&#xff1f; 为什么Python连续多年蝉联最受欢…

【WebApi】什么情况开启如何开启缓存

在 ASP.NET Core WebAPI 中开启缓存是优化性能、减少服务器负载和提升用户体验的非常重要的手段。但并非所有情况都适合开启缓存。 下面我将从 “什么情况下开启” 和 “如何开启” 两个方面为你详细解释。 一、什么情况下应该开启缓存? 总的来说,缓存适用于 “变化不频繁但…

Go语言类型断言全解析

类型断言的基本概念类型断言(Type Assertion)是Go语言中用于检查接口值底层具体类型的机制。它本质上是一种运行时类型检查的操作&#xff0c;允许程序在运行时判断接口变量是否持有特定的类型值&#xff0c;并提取该类型的值。这是Go语言类型系统中的一个重要特性&#xff0c;…

大模型在题目生成中的安全研究:攻击方法与防御机制

大模型在题目生成中的安全研究&#xff1a;攻击方法与防御机制 文章目录大模型在题目生成中的安全研究&#xff1a;攻击方法与防御机制一、引言二、大模型在题目生成中的安全漏洞与攻击方法2.1 大模型在题目生成中的安全漏洞分析2.1.1 训练数据相关漏洞2.1.2 模型架构与特性相关…

跟做springboot尚品甄选项目(二)

登录功能的书写 后端接口的书写 &#xff08;1&#xff09;创建配置文件 粘贴这两个文件&#xff08;E:\project\AllProJect\Shangpin Selection\项目材料素材\资料\资料\03-配置文件&#xff09; 在spzx-manager服务的src/resources目录下创建application.yml、application-…

前后端接口调试提效:Postman + Mock Server 的工作流

前后端接口调试提效&#xff1a;Postman Mock Server 的工作流 &#x1f31f; Hello&#xff0c;我是摘星&#xff01; &#x1f308; 在彩虹般绚烂的技术栈中&#xff0c;我是那个永不停歇的色彩收集者。 &#x1f98b; 每一个优化都是我培育的花朵&#xff0c;每一个特性都是…

大带宽香港云服务器在数据传输速度上有何优势?

为方便站长快速部署网站、优化用户访问体验&#xff0c;当下众多实力强劲的香港数据中心&#xff0c;均推出了大带宽云服务器产品。不过&#xff0c;市面上不少数据中心虽宣称提供 “专属大带宽”&#xff0c;但其线路配置中&#xff0c;国际线路占比高、绕行链路多&#xff0c…

HT862 智能音频功率放大器:为便携音频设备打造高效稳定的音质解决方案

在蓝牙音箱、智能手机、便携式游戏机等设备的设计中&#xff0c;音频功率放大器是决定音质表现、续航能力与使用稳定性的关键部件。一款优质的音频功放&#xff0c;不仅需要输出足够的功率以满足清晰响亮的听觉需求&#xff0c;还需在能效、温控、适配性上达到平衡&#xff0c;…

HarmonyOS-ArkUI Web控件基础铺垫7-HTTP SSL认证图解 及 Charles抓包原理 及您为什么配置对了也抓不到数据

HarmonyOS-ArkUI Web控件基础铺垫6--TCP协议- 流量控制算法与拥塞控制算法 HarmonyOS-ArkUI Web控件基础铺垫5--TCP协议- 动画展示超时重传&#xff0c;滑动窗口&#xff0c;快速重传 HarmonyOS-ArkUI Web控件基础铺垫4--TCP协议- 断联-四次挥手解析 HarmonyOS-ArkUI Web控件…

【qt】通过TCP传输json,json里包含图像

主要是使用协议头 发送方connect(m_pDetectWorker, &DetectionWorker::sig_detectImg, this, [](const QJsonObject &json){// 转换为JSON数据QJsonDocument doc(json);QByteArray jsonData doc.toJson(QJsonDocument::Compact);// 构建增强协议头struct EnhancedHead…

四,基础开发工具(下)

4.5自动构建make/Makefile4.5.1基本使用1示例2进一步解释3实践4最佳实践4.6练习&#xff1a;进度条4.6.1倒计时4.6.2进度条version14.6.2进度条version24.7版本控制器Git4.7.1git操作1操作一次&#xff0c;以后不愁2经典"三件套"3常用4版本回退4.7.2小结4.5自动构建m…

C++基本数据类型的范围

文章目录不同位数的系统下各个类型所占字节数如何存储的我发现我能搜到的相关文章都只讲了这些数据类型的范围是这样的&#xff0c;不说实际的存储情况&#xff0c;当你了解了类型实际是如何存储的&#xff0c;再去记忆这些范围就简单了&#xff0c;所以就有了这篇文章不同位数…

基于社交媒体数据的公众情绪指数构建与重大事件影响分析

一、引言在信息爆炸的时代&#xff0c;社交媒体&#xff08;如微博、Twitter&#xff09;已成为公众表达情绪、讨论热点事件的主要平台。通过分析社交媒体数据&#xff0c;可以构建公众情绪指数&#xff0c;并进一步研究其与股市波动、政策发布等重大事件的关联性。本文将介绍如…

OpenLayers数据源集成 -- 章节七:高德地图集成详解

前言在前面的文章中&#xff0c;我们学习了OpenLayers的瓦片调试&#xff08;VectorTileDebug&#xff09;技术。本文将深入探讨OpenLayers中高德地图的集成方法&#xff0c;这是WebGIS开发中接入商业地图服务的重要技术。高德地图作为国内领先的地图服务提供商&#xff0c;提供…

海外代理IP平台Top3评测:LoongProxy、神龙动态IP、IPIPGO哪家更适合你?

在当今互联网环境中&#xff0c;代理IP服务已成为许多企业和个人用户的刚需。无论是数据采集、市场调研还是账号管理&#xff0c;优质的代理IP都能大幅提升工作效率。本文将针对LoongProxy、神龙海外动态IP和IPIPGO这三家主流代理IP服务商进行横向评测&#xff0c;帮助你根据自…

对浏览器事件机制的理解

浏览器事件是什么&#xff1a; 事件是用户操作网页时发生的交互动作&#xff0c;比如 click/move&#xff0c; 事件除了用户触发的动作外&#xff0c;还可以是文档加载&#xff0c;窗口滚动和大小调整。事件被封装成一个 event 对象&#xff0c;包含了该事件发生时的所有相关信…