二級緩存在實際項目中的應用

二級緩存在項目中的應用

目錄

  • 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;包含了該事件發生時的所有相關信…