废话不多说直接上代码1.依赖dependency groupIdcom.github.ben-manes.caffeine/groupId artifactIdcaffeine/artifactId version2.9.3/version /dependency这里版本java8所以用的2.9.32.配置类过期时间redis30分钟caffeine3分钟package com.hmdp.config; import cn.hutool.json.JSONUtil; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.Scheduler; import com.hmdp.entity.Shop; import com.hmdp.mapper.ShopMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.core.StringRedisTemplate; import javax.annotation.PostConstruct; import java.util.List; import java.util.concurrent.TimeUnit; import static com.hmdp.utils.RedisConstants.CACHE_SHOP_KEY; Configuration Slf4j public class CacheConfig { Autowired private ShopMapper shopMapper; // 使用MyBatis-Plus Mapper Autowired private StringRedisTemplate stringRedisTemplate; //注意stringRedisTemplate要配置序列化器 Autowired private CacheLong, Shop caffeineCache; // 上面定义的Bean Bean public CacheLong, Shop caffeineCache() { return Caffeine.newBuilder() // 初始容量 .initialCapacity(100) // 最大条目数超过会按策略淘汰 .maximumSize(10_000) // 写入后过期时间防雪崩关键1本地TTL较短 .expireAfterWrite(3, TimeUnit.MINUTES) // 可选定时清理过期条目 .scheduler(Scheduler.systemScheduler()) // 记录统计信息用于监控 .recordStats() .build(); } PostConstruct public void preloadHotShops() { log.info(开始预热店铺缓存...); // 一次性查出所有店铺10条不会有性能问题 ListShop shops shopMapper.selectList(null); //我这里数据库中只有十条数据如果数据量过大取hotK就行不然小心内存爆掉 for (Shop shop : shops) { //遍历添加 if (shop ! null) { //如果为空报空指针异常 caffeineCache.put(shop.getId(), shop); String key CACHE_SHOP_KEY shop.getId(); stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), 30, TimeUnit.MINUTES); //注意反编译 } } log.info(缓存预热完成共 {} 家店铺, shops.size()); } }3.使用这里直接写业务层1.先从内存中查//从caffeine中查数据 Shop shop caffeineCache.getIfPresent(id); if (shop ! null) { return Result.ok(shop); }2.再从redis中查查到再回填内存//从redis中查询店铺 shopstr是string类型的json字符串 String shopStr stringRedisTemplate.opsForValue().get(key); //存在直接返回 if (StrUtil.isNotBlank(shopStr)){ caffeineCache.put(id, JSONUtil.toBean(shopStr, Shop.class)); //回填caffeine return Result.ok(JSONUtil.toBean(shopStr, Shop.class)); //转换成对象 }3.都没查到从数据库中查然后回填redis跟caffeine//不存在从数据库中查询 shop getById(id); 从数据库中查询 if (shop null){ //为什么还要判断因为前面布隆过滤器可能误判 本来这个数据就不存在 //缓存穿透添加null值 stringRedisTemplate.opsForValue().set(key,,RedisConstants.CACHE_NULL_TTL,TimeUnit.MINUTES); //数据库中不存在返回错误 return null; } //数据库中存在返回店铺保存到redis中 stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop)); stringRedisTemplate.expire(key, RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES); //保存到caffeine中 caffeineCache.put(shop.getId(), shop); return Result.ok(shop);