分布式缓存实战:Redis与多级缓存架构的完整指南

分布式缓存实战:Redis与多级缓存架构的完整指南

大家好,我是迪哥。缓存是提升系统性能的关键组件,从本地缓存到分布式缓存,从 Redis 到多级缓存架构,我们经历了多次优化。今天就聊聊分布式缓存的最佳实践。

多级缓存架构

复制代码
┌─────────────────────────────────────────────────────────────┐
│                      多级缓存架构                            │
├─────────────────────────────────────────────────────────────┤
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐    │
│  │  本地缓存    │  │   Redis      │  │   数据库     │    │
│  │  Caffeine   │  │   分布式缓存  │  │   MySQL     │    │
│  └──────┬───────┘  └──────┬───────┘  └──────┬───────┘    │
│         │                 │                 │              │
│         ▼                 ▼                 ▼              │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                   应用层                            │   │
│  └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘

本地缓存:Caffeine

配置示例

java 复制代码
@Configuration
public class CaffeineConfig {
    
    @Bean
    public Cache<String, Object> localCache() {
        return Caffeine.newBuilder()
            .initialCapacity(1000)
            .maximumSize(10000)
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .expireAfterAccess(5, TimeUnit.MINUTES)
            .recordStats()
            .build();
    }
}

使用示例

java 复制代码
@Service
public class UserService {
    @Autowired
    private Cache<String, User> localCache;
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    @Autowired
    private UserMapper userMapper;
    
    public User getUser(Long userId) {
        String key = "user:" + userId;
        
        // 1. 先查本地缓存
        User user = localCache.getIfPresent(key);
        if (user != null) {
            return user;
        }
        
        // 2. 再查 Redis
        String json = redisTemplate.opsForValue().get(key);
        if (json != null) {
            user = JSON.parseObject(json, User.class);
            localCache.put(key, user);
            return user;
        }
        
        // 3. 最后查数据库
        user = userMapper.selectById(userId);
        if (user != null) {
            redisTemplate.opsForValue().set(key, JSON.toJSONString(user), 30, TimeUnit.MINUTES);
            localCache.put(key, user);
        }
        
        return user;
    }
}

Redis 缓存优化

缓存策略

java 复制代码
public enum CacheStrategy {
    READ_THROUGH,    // 读穿透
    WRITE_THROUGH,   // 写穿透
    WRITE_BEHIND,    // 写回
    CACHE_ASIDE;     // 旁路缓存(最常用)
}

缓存更新模式

java 复制代码
@Service
public class CacheService {
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    // 模式1:先更新数据库,再删除缓存
    public void updateUser1(User user) {
        userMapper.updateById(user);
        redisTemplate.delete("user:" + user.getId());
    }
    
    // 模式2:先删除缓存,再更新数据库
    public void updateUser2(User user) {
        redisTemplate.delete("user:" + user.getId());
        userMapper.updateById(user);
    }
    
    // 模式3:延迟双删(解决并发问题)
    @Async
    public void updateUser3(User user) {
        redisTemplate.delete("user:" + user.getId());
        userMapper.updateById(user);
        
        // 延迟一段时间后再次删除
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        redisTemplate.delete("user:" + user.getId());
    }
}

缓存问题解决

问题 1:缓存穿透

java 复制代码
// 使用布隆过滤器
@Component
public class BloomFilterService {
    private final BloomFilter<Long> userFilter;
    
    public BloomFilterService() {
        userFilter = BloomFilter.create(
            Funnels.longFunnel(),
            1000000,  // 预计元素数量
            0.01      // 误判率
        );
        
        // 初始化过滤器
        List<Long> userIds = userMapper.selectAllIds();
        userIds.forEach(userFilter::put);
    }
    
    public boolean mightContain(Long userId) {
        return userFilter.mightContain(userId);
    }
}

问题 2:缓存击穿

java 复制代码
@Service
public class ProductService {
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    private final Map<String, Object> locks = new ConcurrentHashMap<>();
    
    public Product getProduct(Long productId) {
        String key = "product:" + productId;
        
        // 先查缓存
        String json = redisTemplate.opsForValue().get(key);
        if (json != null) {
            return JSON.parseObject(json, Product.class);
        }
        
        // 加锁,防止缓存击穿
        synchronized (locks.computeIfAbsent(key, k -> new Object())) {
            try {
                // 双重检查
                json = redisTemplate.opsForValue().get(key);
                if (json != null) {
                    return JSON.parseObject(json, Product.class);
                }
                
                // 查数据库
                Product product = productMapper.selectById(productId);
                if (product != null) {
                    redisTemplate.opsForValue().set(key, JSON.toJSONString(product), 5, TimeUnit.MINUTES);
                }
                return product;
            } finally {
                locks.remove(key);
            }
        }
    }
}

问题 3:缓存雪崩

java 复制代码
// 解决方案:设置随机过期时间
public void setCacheWithRandomTTL(String key, Object value) {
    int ttl = 30 + ThreadLocalRandom.current().nextInt(30); // 30-60分钟
    redisTemplate.opsForValue().set(key, JSON.toJSONString(value), ttl, TimeUnit.MINUTES);
}

最佳实践清单

维度 最佳实践
多级缓存 本地缓存 + Redis + 数据库
缓存策略 旁路缓存模式
更新模式 先更新数据库,再删除缓存
并发问题 延迟双删 + 分布式锁
监控 监控缓存命中率、热点Key

说到缓存,我家那只叫 Docker 的哈士奇最近学会了"缓存策略"------把喜欢的玩具藏在沙发底下(本地缓存),饿了才去狗粮碗(数据库)找吃的,这缓存命中率比我们的 Redis 还高 😂

我是迪哥,我们下期再见!

相关推荐
墨着染霜华2 小时前
MySQL字符串数字筛选与转换 + Java Integer/Long数值长度避坑指南
java·数据库·mysql
蜡台2 小时前
IDEA 编辑器两个竖线显示位置
java·编辑器·intellij-idea
Devin~Y2 小时前
大厂Java面试实录:Spring Boot/Cloud、JVM、Redis、Kafka、MyBatis 到 RAG/Agent 的三轮连环问(含答案详解)
java·jvm·spring boot·redis·spring cloud·kafka·mybatis
benpaodeDD2 小时前
视频44——Tomcat标准输出流乱码问题
java·tomcat
2401_850491652 小时前
Bootstrap和OpenLayers结合开发的示例
jvm·数据库·python
Royzst2 小时前
集合进阶(Map集合)
java·前端·数据库
happymaker06262 小时前
LeetCodeHot100——1.两数之和(详细解答)
java·数据结构·学习·算法
AI人工智能+电脑小能手2 小时前
【大白话说Java面试题 第60题】【JVM篇】第20题:垃圾收集算法和垃圾收集器有什么区别?
java·jvm·算法·面试
wand codemonkey2 小时前
(三十)web应用+【核心】+【规矩】+【原理】
java·开发语言·前端