Java缓存同步案例

wen java案例 3

本文目录导读:

Java缓存同步案例

  1. 基础缓存工具类
  2. 数据库与缓存同步服务
  3. 分布式缓存同步(使用Redis)
  4. 缓存同步监听器
  5. 定时缓存刷新
  6. 测试用例
  7. 缓存同步关键策略

我来为您提供一个完整的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 (旁路缓存)

  • 读:先读缓存 → 未命中读数据库 → 写入缓存
  • 写:先更新数据库 → 删除缓存

缓存穿透防护

  • 缓存空值
  • 布隆过滤器

缓存击穿防护

  • 互斥锁
  • 双重检查

缓存雪崩防护

  • 设置随机过期时间
  • 多级缓存

这个案例展示了在实际项目中如何实现缓存同步,包括本地缓存、分布式缓存、事件驱动同步等常用模式。

抱歉,评论功能暂时关闭!