本文目录导读:

- 基础缓存刷新案例
- 使用缓存框架(Caffeine)的刷新机制
- 使用Redis的缓存刷新
- 使用Spring Cache的刷新机制
- 实际业务场景:商品库存缓存刷新
- 生产环境使用的缓存监控和刷新工具类
- 测试类
- 最佳实践建议
我来通过一个完整的实操案例,演示Java缓存刷新的实现方法。
基础缓存刷新案例
场景:用户信息缓存
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class UserCacheService {
// 使用ConcurrentHashMap存储用户缓存
private static final ConcurrentHashMap<Long, UserInfo> userCache = new ConcurrentHashMap<>();
// 定时任务执行器
private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
// 缓存过期时间(毫秒)
private static final long CACHE_EXPIRE_TIME = 5 * 60 * 1000; // 5分钟
// 存储每个缓存的过期时间
private static final ConcurrentHashMap<Long, Long> expireTimeMap = new ConcurrentHashMap<>();
/**
* 获取用户信息(带缓存)
*/
public UserInfo getUserInfo(Long userId) {
// 检查缓存是否存在
UserInfo cachedUser = userCache.get(userId);
if (cachedUser != null) {
// 检查是否过期
Long expireTime = expireTimeMap.get(userId);
if (System.currentTimeMillis() < expireTime) {
log.info("从缓存获取用户信息: {}", userId);
return cachedUser;
} else {
// 缓存过期,移除
log.info("缓存已过期,移除: {}", userId);
userCache.remove(userId);
expireTimeMap.remove(userId);
}
}
// 从数据库获取
UserInfo user = queryFromDatabase(userId);
// 放入缓存
if (user != null) {
userCache.put(userId, user);
expireTimeMap.put(userId, System.currentTimeMillis() + CACHE_EXPIRE_TIME);
}
return user;
}
/**
* 主动刷新缓存
*/
public void refreshCache(Long userId) {
log.info("主动刷新缓存: {}", userId);
userCache.remove(userId);
expireTimeMap.remove(userId);
getOneUserInfo(userId);
}
/**
* 批量刷新缓存
*/
public void batchRefreshCache() {
log.info("批量刷新所有缓存");
// 获取所有缓存的用户ID
userCache.keySet().forEach(userId -> {
refreshCache(userId);
});
}
/**
* 定时刷新缓存任务
*/
public void startScheduledRefresh() {
scheduler.scheduleAtFixedRate(() -> {
log.info("执行定时缓存刷新任务");
batchRefreshCache();
}, 0, CACHE_EXPIRE_TIME, TimeUnit.MILLISECONDS);
}
private UserInfo queryFromDatabase(Long userId) {
// 模拟数据库查询
return new UserInfo(userId, "用户" + userId, "user" + userId + "@example.com");
}
private UserInfo getOneUserInfo(Long userId) {
UserInfo user = queryFromDatabase(userId);
if (user != null) {
userCache.put(userId, user);
expireTimeMap.put(userId, System.currentTimeMillis() + CACHE_EXPIRE_TIME);
}
return user;
}
// 用户信息实体类
@Data
@AllArgsConstructor
public static class UserInfo {
private Long id;
private String name;
private String email;
}
}
使用缓存框架(Caffeine)的刷新机制
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.github.benmanes.caffeine.cache.Scheduler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.concurrent.TimeUnit;
@Slf4j
@Service
public class CaffeineCacheService {
private LoadingCache<String, String> refreshCache;
@PostConstruct
public void init() {
refreshCache = Caffeine.newBuilder()
// 缓存写入后10秒过期
.expireAfterWrite(10, TimeUnit.SECONDS)
// 缓存访问后10秒过期
.expireAfterAccess(10, TimeUnit.SECONDS)
// 开启缓存刷新功能
.refreshAfterWrite(5, TimeUnit.SECONDS)
// 设置缓存容量
.maximumSize(1000)
// 记录统计信息
.recordStats()
// 设置调度器
.scheduler(Scheduler.systemScheduler())
.build(key -> {
// 缓存失效后的加载方法
log.info("加载缓存数据: {}", key);
return loadDataFromDatabase(key);
});
}
/**
* 获取缓存数据
*/
public String getData(String key) {
return refreshCache.get(key);
}
/**
* 主动刷新特定缓存
*/
public void refreshData(String key) {
log.info("主动刷新缓存: {}", key);
// 使缓存失效
refreshCache.invalidate(key);
// 重新加载
refreshCache.get(key);
}
/**
* 批量刷新缓存
*/
public void refreshAllData() {
log.info("刷新所有缓存");
refreshCache.invalidateAll();
refreshCache.refresh("defaultKey");
}
/**
* 获取缓存统计信息
*/
public CacheStats getCacheStats() {
return refreshCache.stats();
}
private String loadDataFromDatabase(String key) {
// 模拟数据库查询
try {
Thread.sleep(100); // 模拟耗时
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "数据 " + key + " - " + System.currentTimeMillis();
}
}
使用Redis的缓存刷新
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class RedisCacheService {
private final JedisPool jedisPool;
// 缓存过期时间
private static final int CACHE_EXPIRE_SECONDS = 300; // 5分钟
public RedisCacheService() {
this.jedisPool = new JedisPool("localhost", 6379);
}
/**
* 获取缓存数据
*/
public String getCacheData(String key) {
try (Jedis jedis = jedisPool.getResource()) {
String data = jedis.get(key);
if (data != null) {
log.info("从Redis获取缓存: {}", key);
// 更新过期时间(滑动窗口策略)
jedis.expire(key, CACHE_EXPIRE_SECONDS);
return data;
}
// 从数据库加载
String value = loadFromDatabase(key);
if (value != null) {
jedis.setex(key, CACHE_EXPIRE_SECONDS, value);
}
return value;
}
}
/**
* 主动刷新缓存
*/
public void refreshCache(String key) {
log.info("刷新Redis缓存: {}", key);
try (Jedis jedis = jedisPool.getResource()) {
// 删除旧缓存
jedis.del(key);
// 重新加载
String value = loadFromDatabase(key);
if (value != null) {
jedis.setex(key, CACHE_EXPIRE_SECONDS, value);
}
}
}
/**
* 批量刷新缓存(使用管道)
*/
public void batchRefreshCache(String... keys) {
log.info("批量刷新Redis缓存");
try (Jedis jedis = jedisPool.getResource()) {
// 使用管道批量操作
var pipeline = jedis.pipelined();
for (String key : keys) {
pipeline.del(key);
String value = loadFromDatabase(key);
if (value != null) {
pipeline.setex(key, CACHE_EXPIRE_SECONDS, value);
}
}
pipeline.sync();
}
}
private String loadFromDatabase(String key) {
// 模拟数据库查询
return "数据库数据 " + key;
}
}
使用Spring Cache的刷新机制
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class SpringCacheService {
private final CacheManager cacheManager;
public SpringCacheService(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
/**
* 获取数据(带缓存)
*/
@Cacheable(value = "userCache", key = "#userId")
public UserInfo getUserInfo(Long userId) {
log.info("从数据库获取用户信息: {}", userId);
return queryUserFromDatabase(userId);
}
/**
* 更新并刷新缓存
*/
@CachePut(value = "userCache", key = "#userId")
public UserInfo updateUserInfo(Long userId, UserInfo userInfo) {
log.info("更新用户信息并刷新缓存: {}", userId);
// 更新数据库
updateUserInDatabase(userId, userInfo);
return userInfo;
}
/**
* 清除缓存
*/
@CacheEvict(value = "userCache", key = "#userId")
public void deleteUserCache(Long userId) {
log.info("清除用户缓存: {}", userId);
}
/**
* 清除所有缓存
*/
@CacheEvict(value = "userCache", allEntries = true)
public void clearAllUserCache() {
log.info("清除所有用户缓存");
}
/**
* 组合操作:先清除再更新
*/
@Caching(
evict = @CacheEvict(value = "userCache", key = "#userId"),
put = @CachePut(value = "userCache", key = "#userId")
)
public UserInfo refreshCacheAndUpdate(Long userId, UserInfo userInfo) {
log.info("刷新并更新缓存: {}", userId);
updateUserInDatabase(userId, userInfo);
return userInfo;
}
}
实际业务场景:商品库存缓存刷新
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.concurrent.TimeUnit;
@Slf4j
@Service
public class ProductStockCacheService {
private LoadingCache<String, Integer> stockCache;
@PostConstruct
public void init() {
stockCache = CacheBuilder.newBuilder()
.maximumSize(10000) // 最大缓存数量
.expireAfterWrite(30, TimeUnit.SECONDS) // 写入30秒后过期
.refreshAfterWrite(10, TimeUnit.SECONDS) // 写入10秒后刷新
.recordStats()
.removalListener(notification -> {
log.info("缓存被移除: {} - 原因: {}",
notification.getKey(),
notification.getCause());
})
.build(new CacheLoader<String, Integer>() {
@Override
public Integer load(String key) {
return loadStockFromDatabase(key);
}
@Override
public Integer reload(String key, Integer oldValue) {
log.info("刷新缓存: {},旧值: {}", key, oldValue);
return loadStockFromDatabase(key);
}
});
}
/**
* 获取商品库存
*/
public Integer getStock(String productId) {
try {
return stockCache.get(productId);
} catch (Exception e) {
log.error("获取库存失败: {}", productId, e);
return 0;
}
}
/**
* 扣减库存并刷新缓存
*/
public Integer deductStock(String productId, Integer quantity) {
log.info("扣减库存: {} - {}", productId, quantity);
// 从数据库扣减
Integer currentStock = deductStockFromDatabase(productId, quantity);
// 主动刷新缓存
stockCache.refresh(productId);
return currentStock;
}
/**
* 批量刷新商品库存
*/
public void batchRefreshStock(List<String> productIds) {
log.info("批量刷新商品库存");
productIds.parallelStream().forEach(productId -> {
try {
// 从数据库加载最新库存
Integer stock = loadStockFromDatabase(productId);
// 放入缓存
stockCache.put(productId, stock);
log.info("刷新商品库存成功: {} -> {}", productId, stock);
} catch (Exception e) {
log.error("刷新商品库存失败: {}", productId, e);
}
});
}
private Integer loadStockFromDatabase(String productId) {
// 模拟从数据库加载库存
return 100; // 示例返回
}
private Integer deductStockFromDatabase(String productId, Integer quantity) {
// 模拟数据库库存扣减
return 98; // 示例返回
}
}
生产环境使用的缓存监控和刷新工具类
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class CacheManagementTool {
@Autowired
private CacheManager cacheManager;
@Autowired
private CaffeineCacheService caffeineCacheService;
/**
* 每5分钟检查缓存健康状态
*/
@Scheduled(fixedDelay = 5 * 60 * 1000)
public void checkCacheHealth() {
log.info("开始检查缓存健康状态");
// 获取缓存统计信息
CacheStats stats = caffeineCacheService.getCacheStats();
log.info("缓存统计信息:");
log.info("命中率: {}", stats.hitRate());
log.info("命中次数: {}", stats.hitCount());
log.info("未命中次数: {}", stats.missCount());
log.info("请求总数: {}", stats.requestCount());
// 如果命中率低于阈值,自动刷新缓存
if (stats.hitRate() < 0.8) {
log.warn("缓存命中率过低: {},准备刷新缓存", stats.hitRate());
caffeineCacheService.refreshAllData();
}
}
/**
* 手动刷新指定缓存
*/
public void manualRefresh(String cacheName, String key) {
log.info("手动刷新缓存: {} - {}", cacheName, key);
Cache cache = cacheManager.getCache(cacheName);
if (cache != null) {
cache.evict(key); // 移除缓存
log.info("缓存已清除: {} - {}", cacheName, key);
}
}
/**
* 查看所有缓存的内容
*/
public void viewAllCacheEntries() {
log.info("查看所有缓存条目");
for (String cacheName : cacheManager.getCacheNames()) {
Cache cache = cacheManager.getCache(cacheName);
log.info("缓存名称: {}, 类型: {}", cacheName, cache.getClass().getSimpleName());
}
}
}
测试类
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class CacheRefreshExampleTests {
@Autowired
private UserCacheService userCacheService;
@Autowired
private SpringCacheService springCacheService;
@Test
void testBasicCacheRefresh() throws InterruptedException {
// 测试基础缓存刷新
Long userId = 1L;
// 第一次获取(从数据库加载)
UserInfo user1 = userCacheService.getUserInfo(userId);
System.out.println("第一次获取: " + user1);
// 第二次获取(从缓存)
UserInfo user2 = userCacheService.getUserInfo(userId);
System.out.println("第二次获取: " + user2);
// 等待缓存过期
Thread.sleep(6000); // 等待6秒
// 缓存过期后重新获取
UserInfo user3 = userCacheService.getUserInfo(userId);
System.out.println("过期后获取: " + user3);
}
@Test
void testSpringCacheRefresh() {
// 测试Spring Cache刷新
Long userId = 1L;
UserInfo userInfo = new UserInfo(userId, "张三", "zhangsan@example.com");
// 更新用户信息并刷新缓存
UserInfo updatedUser = springCacheService.updateUserInfo(userId, userInfo);
System.out.println("更新结果: " + updatedUser);
// 清除缓存
springCacheService.deleteUserCache(userId);
System.out.println("缓存已清除");
}
}
最佳实践建议
-
选择合适的刷新策略:
- 定时刷新:适合数据变化不频繁的场景
- 事件驱动刷新:数据变化时立即刷新
- 懒加载刷新:访问时检查并刷新
-
注意缓存一致性:
- 写操作时主动刷新或删除缓存
- 使用版本号或时间戳避免脏数据
-
缓存穿透防护:
- 空值缓存
- 布隆过滤器
- 限流保护
-
监控告警:
- 监控缓存命中率
- 监控缓存大小
- 设置告警阈值
这个实操案例包含了从基础到高级的缓存刷新实现,你可以根据实际需求选择合适的方案。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播