二級緩存在項目中的應用
目錄
- 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 二級緩存優勢
- 性能提升: 減少數據庫訪問,提高響應速度
- 可擴展性: 支持多種緩存提供者,適應不同場景
- 透明性: 對業務代碼透明,無需修改現有邏輯
- 靈活性: 支持細粒度緩存控制,可配置緩存策略
6.2 使用建議
- 合理選擇緩存策略: 根據數據特點選擇合適的并發策略
- 注意緩存一致性: 確保緩存與數據庫數據的一致性
- 監控緩存性能: 定期監控緩存命中率和性能指標
- 合理設置過期時間: 根據業務需求設置合適的緩存過期時間
- 避免緩存雪崩: 使用隨機過期時間,避免同時失效
6.3 注意事項
- 內存管理: 注意緩存占用內存,合理配置緩存大小
- 序列化性能: 選擇高效的序列化方式
- 緩存穿透: 防止查詢不存在數據導致緩存失效
- 緩存雪崩: 避免大量緩存同時失效
- 數據一致性: 確保緩存與數據庫數據的一致性
二級緩存是提升應用性能的重要手段,合理使用可以顯著提高系統響應速度和用戶體驗。在實際應用中,需要根據具體業務場景選擇合適的緩存策略和配置參數。