• 【Redis(8)】Spring Boot整合Redis和Guava,解决缓存穿透、缓存击穿、缓存雪崩等缓存问题


    缓存技术的挑战及设计方案我们介绍了使用缓存技术可能会遇到的一些问题,那么如何解决这些问题呢?

    在构建缓存系统时,Spring Boot和Redis的结合提供了强大的支持,而Guava的LoadingCache则为缓存管理带来了便捷的解决方案。下面我将介绍如何通过整合Spring Boot、Redis和Guava来实现一个解决缓存穿透、缓存击穿、缓存雪崩、缓存污染和缓存数据一致性问题的缓存方案。

    一、整合Spring Boot与Redis

    首先,我们需要在Spring Boot项目中整合原生Redis客户端。这可以通过添加Spring Boot Redis依赖来实现。

    二、引入Guava

    Guava的LoadingCache是一个高级缓存工具,它支持自动加载、缓存数据的自动刷新和监听器通知。

    三、工具类

    下面是一个三高缓存工具类的实现,它整合了Spring Boot、Redis和Guava的LoadingCache。这个工具类旨在解决缓存穿透、缓存击穿、缓存雪崩、缓存污染和缓存数据一致性问题。

    1. import com.google.common.cache.CacheBuilder;
    2. import com.google.common.cache.CacheLoader;
    3. import com.google.common.cache.LoadingCache;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.data.redis.core.StringRedisTemplate;
    6. import org.springframework.data.redis.core.ValueOperations;
    7. import java.util.concurrent.Callable;
    8. import java.util.concurrent.ExecutionException;
    9. import java.util.concurrent.TimeUnit;
    10. public class CacheUtilextends CacheUtil.DataLoaderInterface> {
    11. private final StringRedisTemplate stringRedisTemplate;
    12. private final ValueOperations valueOperations;
    13. private final LoadingCache loadingCache;
    14. private final DataLoader dataLoader;
    15. @Autowired
    16. public CacheUtil(
    17. StringRedisTemplate stringRedisTemplate, DataLoader dataLoader) {
    18. this.stringRedisTemplate = stringRedisTemplate;
    19. this.valueOperations = stringRedisTemplate.opsForValue();
    20. this.dataLoader = dataLoader;
    21. // 初始化Guava LoadingCache
    22. // 设置最大容量,避免缓存污染
    23. // 设置写入后过期时间,避免缓存雪崩
    24. // 使用锁机制,避免缓存击穿
    25. this.loadingCache = CacheBuilder.newBuilder()
    26. .maximumSize(10000)
    27. .expireAfterWrite(10, TimeUnit.MINUTES)
    28. .build(new CacheLoader() {
    29. @Override
    30. public String load(String key) throws Exception {
    31. // 当缓存未命中时,调用数据加载器的方法从数据库加载数据
    32. return dataLoader.loadDataFromDatabase(key);
    33. }
    34. });
    35. }
    36. public String get(String key) {
    37. try {
    38. // 通过Guava LoadingCache获取数据
    39. // 自动处理缓存穿透和击穿
    40. return loadingCache.get(key);
    41. } catch (ExecutionException e) {
    42. // 异常处理,返回null
    43. return null;
    44. }
    45. }
    46. public void set(String key, String value) {
    47. // 同时更新Redis和Guava LoadingCache
    48. // 保持数据一致性
    49. valueOperations.set(key, value);
    50. loadingCache.put(key, value);
    51. }
    52. public void update(String key, String value) {
    53. // 更新缓存数据,解决数据一致性问题
    54. set(key, value);
    55. }
    56. public void insert(String key, String value) {
    57. // 插入前检查缓存,避免缓存污染
    58. if (get(key) == null) {
    59. set(key, value);
    60. }
    61. }
    62. public void delete(String key) {
    63. // 删除Redis和Guava LoadingCache中的数据
    64. // 保持数据一致性
    65. valueOperations.delete(key);
    66. loadingCache.invalidate(key); // 使缓存项失效
    67. }
    68. // 用于在数据库更新后刷新缓存
    69. public void refreshCache(String key) {
    70. loadingCache.invalidate(key);
    71. }
    72. // 数据加载器接口,调用者需要实现该接口以提供数据加载逻辑
    73. public interface DataLoaderInterface {
    74. String loadDataFromDatabase(String key);
    75. }
    76. }

    四、使用示例

    • 实现数据加载器接口:创建一个类实现CacheUtil.DataLoaderInterface接口,提供具体的数据加载逻辑。
    1. public class UserCacheDataLoader implements CacheUtil.DataLoaderInterface {
    2. private final UserService userService; // 假设这是您的UserService
    3. @Autowired
    4. public UserCacheDataLoader(UserService userService) {
    5. this.userService = userService;
    6. }
    7. @Override
    8. public String loadDataFromDatabase(String key) {
    9. // 根据键(例如用户ID)从数据库加载数据
    10. User user = userService.findById(key);
    11. if (user != null) {
    12. return user.toString(); // 将用户信息转换为字符串
    13. }
    14. return null; // 用户不存在
    15. }
    16. }
    • 配置Spring Bean:在Spring配置中注册CacheUtil Bean。
    1. @Configuration
    2. public class CacheConfig {
    3. @Bean
    4. public CacheUtil userCacheUtil(
    5. UserCacheDataLoader userCacheDataLoader) {
    6. // 假设已经配置好
    7. StringRedisTemplate stringRedisTemplate = stringRedisTemplate();
    8. return new CacheUtil<>(stringRedisTemplate, userCacheDataLoader);
    9. }
    10. // 其他配置...
    11. }
    • 在应用中使用:在需要缓存的地方注入CacheUtil并使用它。
    1. @RestController
    2. @RequestMapping("/users")
    3. public class UserController {
    4. private final CacheUtil userCacheUtil;
    5. @Autowired
    6. public UserController(CacheUtil userCacheUtil) {
    7. this.userCacheUtil = userCacheUtil;
    8. }
    9. @GetMapping("/{userId}")
    10. public String getUserDetails(@PathVariable String userId) {
    11. // 使用CacheUtil的get方法来获取缓存数据
    12. return userCacheUtil.get(userId);
    13. }
    14. // 更新缓存数据
    15. userCacheUtil.update(key, value);
    16. // 插入缓存数据
    17. userCacheUtil.insert(key, value);
    18. // 删除缓存数据
    19. userCacheUtil.delete(key);
    20. // 数据库更新后刷新缓存
    21. userCacheUtil.refreshCache(key);
    22. }

     

    注意:

    • 请确保StringRedisTemplate和数据加载器(如UserCacheDataLoader)已经正确配置并注入到CacheUtil中。
    • 根据业务逻辑的复杂性,loadDataFromDatabase方法可能需要合理的超时和重试策略。
    • 在实际部署前,进行充分的测试,确保缓存加载逻辑在各种情况下都能正常工作。

     

  • 相关阅读:
    【C++】静态成员函数 ( 静态成员函数概念 | 静态成员函数声明 | 静态成员函数访问 | 静态成员函数只能访问静态成员 )
    Android学习笔记5 - 初学Activity(二)多个、生命周期、启动模式
    金仓数据库KingbaseES客户端编程开发框架-SQLAlchemy(2. 概述)
    用了CDN就一定比不用更快吗?
    Python+大数据-Spark技术栈(二)SparkBase&Core
    NX/UG二次开发—UF_MODL_ask_bounding_box_exact浅析
    KO88 冲销内部订单
    LeetCode 121. 买卖股票的最佳时机
    Mysql进阶1
    Chapter6:线性离散系统的分析
  • 原文地址:https://blog.csdn.net/weixin_37519752/article/details/138043646