二级缓存在项目中的应用
目录
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 二级缓存优势
- 性能提升: 减少数据库访问,提高响应速度
- 可扩展性: 支持多种缓存提供者,适应不同场景
- 透明性: 对业务代码透明,无需修改现有逻辑
- 灵活性: 支持细粒度缓存控制,可配置缓存策略
6.2 使用建议
- 合理选择缓存策略: 根据数据特点选择合适的并发策略
- 注意缓存一致性: 确保缓存与数据库数据的一致性
- 监控缓存性能: 定期监控缓存命中率和性能指标
- 合理设置过期时间: 根据业务需求设置合适的缓存过期时间
- 避免缓存雪崩: 使用随机过期时间,避免同时失效
6.3 注意事项
- 内存管理: 注意缓存占用内存,合理配置缓存大小
- 序列化性能: 选择高效的序列化方式
- 缓存穿透: 防止查询不存在数据导致缓存失效
- 缓存雪崩: 避免大量缓存同时失效
- 数据一致性: 确保缓存与数据库数据的一致性
二级缓存是提升应用性能的重要手段,合理使用可以显著提高系统响应速度和用户体验。在实际应用中,需要根据具体业务场景选择合适的缓存策略和配置参数。