本文目录导读:

我来为您提供一个完整的Java缓存同步案例,涵盖常见的缓存同步策略。
基础缓存工具类
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class CacheManager<K, V> {
private final ConcurrentHashMap<K, V> cache = new ConcurrentHashMap<>();
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
// 写入缓存
public void put(K key, V value) {
lock.writeLock().lock();
try {
cache.put(key, value);
System.out.println("缓存写入: " + key + " = " + value);
} finally {
lock.writeLock().unlock();
}
}
// 读取缓存
public V get(K key) {
lock.readLock().lock();
try {
return cache.get(key);
} finally {
lock.readLock().unlock();
}
}
// 删除缓存
public void remove(K key) {
lock.writeLock().lock();
try {
cache.remove(key);
System.out.println("缓存删除: " + key);
} finally {
lock.writeLock().unlock();
}
}
// 清空缓存
public void clear() {
lock.writeLock().lock();
try {
cache.clear();
System.out.println("缓存清空");
} finally {
lock.writeLock().unlock();
}
}
}
数据库与缓存同步服务
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class CacheSyncService {
@Autowired
private CacheManager<String, Object> cacheManager;
@Autowired
private UserRepository userRepository;
// 读取数据(先读缓存,再读数据库)
public User getUserById(String userId) {
// 1. 先查询缓存
User user = (User) cacheManager.get("user:" + userId);
if (user != null) {
System.out.println("从缓存获取用户: " + userId);
return user;
}
// 2. 缓存未命中,查询数据库
user = userRepository.findById(userId).orElse(null);
if (user != null) {
// 3. 将数据写入缓存
cacheManager.put("user:" + userId, user);
System.out.println("从数据库获取用户并写入缓存: " + userId);
}
return user;
}
// 更新数据(先更新数据库,再删除缓存)
@Transactional
public User updateUser(User user) {
// 1. 更新数据库
User updatedUser = userRepository.save(user);
System.out.println("数据库更新用户: " + user.getId());
// 2. 删除缓存(或更新缓存)
cacheManager.remove("user:" + user.getId());
System.out.println("删除用户缓存: " + user.getId());
return updatedUser;
}
// 删除数据(先删除数据库,再删除缓存)
@Transactional
public void deleteUser(String userId) {
// 1. 删除数据库
userRepository.deleteById(userId);
System.out.println("数据库删除用户: " + userId);
// 2. 删除缓存
cacheManager.remove("user:" + userId);
System.out.println("删除用户缓存: " + userId);
}
}
分布式缓存同步(使用Redis)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class DistributedCacheSyncService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private ProductRepository productRepository;
private static final String PRODUCT_CACHE_KEY = "product:";
private static final long CACHE_TTL = 30; // 缓存过期时间(分钟)
// 缓存穿透保护:查询商品
public Product getProductById(String productId) {
String cacheKey = PRODUCT_CACHE_KEY + productId;
// 1. 查询缓存
Product product = (Product) redisTemplate.opsForValue().get(cacheKey);
if (product != null) {
System.out.println("从Redis缓存获取商品: " + productId);
return product;
}
// 2. 缓存未命中,加锁防止缓存击穿
synchronized (this) {
// 双重检查
product = (Product) redisTemplate.opsForValue().get(cacheKey);
if (product != null) {
return product;
}
// 查询数据库
product = productRepository.findById(productId).orElse(null);
if (product != null) {
// 设置缓存,带过期时间
redisTemplate.opsForValue().set(cacheKey, product, CACHE_TTL, TimeUnit.MINUTES);
System.out.println("从数据库获取商品并写入Redis: " + productId);
} else {
// 缓存空值,防止缓存穿透
redisTemplate.opsForValue().set(cacheKey, new EmptyProduct(), 5, TimeUnit.MINUTES);
System.out.println("商品不存在,缓存空值: " + productId);
}
}
return product;
}
// 更新商品(使用分布式锁)
@Transactional
public Product updateProduct(Product product) {
String lockKey = "lock:product:" + product.getId();
String cacheKey = PRODUCT_CACHE_KEY + product.getId();
// 获取分布式锁
boolean lockAcquired = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "locked", 10, TimeUnit.SECONDS);
if (!lockAcquired) {
throw new RuntimeException("获取分布式锁失败,请稍后重试");
}
try {
// 1. 更新数据库
Product updatedProduct = productRepository.save(product);
System.out.println("数据库更新商品: " + product.getId());
// 2. 删除缓存
redisTemplate.delete(cacheKey);
System.out.println("删除商品缓存: " + product.getId());
// 3. 发送消息通知其他节点
sendCacheInvalidationMessage(product.getId());
return updatedProduct;
} finally {
// 释放分布式锁
redisTemplate.delete(lockKey);
}
}
// 发送缓存失效消息
private void sendCacheInvalidationMessage(String productId) {
// 使用消息队列通知其他服务节点
System.out.println("发送缓存失效消息: " + productId);
}
// 消息队列监听器
public void handleCacheInvalidationMessage(String productId) {
String cacheKey = PRODUCT_CACHE_KEY + productId;
redisTemplate.delete(cacheKey);
System.out.println("接收消息并删除缓存: " + productId);
}
}
缓存同步监听器
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class CacheSyncListener {
@Autowired
private CacheManager<String, Object> cacheManager;
// 监听数据变更事件
@EventListener
public void handleDataChangeEvent(DataChangeEvent event) {
String cacheKey = event.getCacheKey();
switch (event.getChangeType()) {
case UPDATE:
case DELETE:
cacheManager.remove(cacheKey);
System.out.println("事件驱动缓存删除: " + cacheKey);
break;
case CREATE:
// 可选择立即加载或延迟加载
break;
}
}
}
// 数据变更事件
class DataChangeEvent {
private String cacheKey;
private ChangeType changeType;
public DataChangeEvent(String cacheKey, ChangeType changeType) {
this.cacheKey = cacheKey;
this.changeType = changeType;
}
public String getCacheKey() { return cacheKey; }
public ChangeType getChangeType() { return changeType; }
}
enum ChangeType {
CREATE, UPDATE, DELETE
}
定时缓存刷新
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.Set;
@Component
public class CacheRefreshScheduler {
@Autowired
private CacheManager<String, Object> cacheManager;
@Autowired
private ProductRepository productRepository;
// 每小时刷新热门商品缓存
@Scheduled(cron = "0 0 * * * ?") // 每小时执行一次
public void refreshHotProductsCache() {
System.out.println("开始刷新热门商品缓存");
// 获取所有热门商品ID
Set<String> hotProductIds = getHotProductIds();
for (String productId : hotProductIds) {
String cacheKey = "hot:product:" + productId;
// 删除旧缓存
cacheManager.remove(cacheKey);
// 从数据库加载最新数据
Product product = productRepository.findById(productId).orElse(null);
if (product != null) {
cacheManager.put(cacheKey, product);
System.out.println("刷新商品缓存: " + productId);
}
}
}
private Set<String> getHotProductIds() {
// 模拟获取热点商品ID
return Set.of("001", "002", "003");
}
}
测试用例
public class CacheSyncTest {
public static void main(String[] args) {
CacheSyncService cacheService = new CacheSyncService();
// 模拟测试
System.out.println("=== 缓存同步测试 ===");
// 1. 第一次查询 - 缓存未命中
User user = cacheService.getUserById("001");
System.out.println("第一次查询结果: " + user);
// 2. 第二次查询 - 缓存命中
user = cacheService.getUserById("001");
System.out.println("第二次查询结果: " + user);
// 3. 更新数据
user.setName("新名称");
cacheService.updateUser(user);
// 4. 更新后查询 - 缓存已删除,重新加载
user = cacheService.getUserById("001");
System.out.println("更新后查询结果: " + user);
}
}
缓存同步关键策略
Cache-Aside Pattern (旁路缓存)
- 读:先读缓存 → 未命中读数据库 → 写入缓存
- 写:先更新数据库 → 删除缓存
缓存穿透防护
- 缓存空值
- 布隆过滤器
缓存击穿防护
- 互斥锁
- 双重检查
缓存雪崩防护
- 设置随机过期时间
- 多级缓存
这个案例展示了在实际项目中如何实现缓存同步,包括本地缓存、分布式缓存、事件驱动同步等常用模式。