redis与mysql一致性解决方案代码示例

1,分布式锁

@Service
@RequiredArgsConstructor
public class CacheService {
    private final RedisTemplate<String, Object> redisTemplate;
    private final ProductRepository productRepository;
    private final DistributedLockService lockService;
    
    private static final String PRODUCT_CACHE_PREFIX = "product:";
    private static final String PRODUCT_LOCK_PREFIX = "lock:product:";
    private static final long DEFAULT_LOCK_WAIT_TIME = 1000; // 1秒
    private static final long DEFAULT_LOCK_LEASE_TIME = 5000; // 5秒
    
    /**
     * 获取商品信息(带缓存)
     */
    public Product getProduct(Long productId) {
        String cacheKey = PRODUCT_CACHE_PREFIX + productId;
        String lockKey = PRODUCT_LOCK_PREFIX + productId;
        
        // 1. 先查缓存
        Product product = (Product) redisTemplate.opsForValue().get(cacheKey);
        if (product != null) {
            return product;
        }
        
        // 2. 缓存不存在,加分布式锁查数据库
        return lockService.executeWithLock(lockKey, 
                                        DEFAULT_LOCK_WAIT_TIME, 
                                        DEFAULT_LOCK_LEASE_TIME,
                                        () -> {
            // 双重检查,防止重复查询数据库
            Product cachedProduct = (Product) redisTemplate.opsForValue().get(cacheKey);
            if (cachedProduct != null) {
                return cachedProduct;
            }
            
            // 查询数据库
            Product dbProduct = productRepository.findById(productId)
                .orElseThrow(() -> new RuntimeException("Product not found"));
            
            // 写入缓存,设置过期时间
            redisTemplate.opsForValue().set(
                cacheKey, 
                dbProduct, 
                30, TimeUnit.MINUTES); // 30分钟过期
            
            return dbProduct;
        });
    }
    
    /**
     * 更新商品信息(保证缓存与数据库一致性)
     */
    public Product updateProduct(Product product) {
        String lockKey = PRODUCT_LOCK_PREFIX + product.getId();
        
        return lockService.executeWithLock(lockKey, 
                                        DEFAULT_LOCK_WAIT_TIME, 
                                        DEFAULT_LOCK_LEASE_TIME,
                                        () -> {
            // 1. 更新数据库
            Product updatedProduct = productRepository.save(product);
            
            // 2. 删除缓存
            redisTemplate.delete(PRODUCT_CACHE_PREFIX + product.getId());
            
            // 3. 可选的延迟双删(针对极端情况)
            new Thread(() -> {
                try {
                    Thread.sleep(500); // 延迟500ms
                    redisTemplate.delete(PRODUCT_CACHE_PREFIX + product.getId());
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }).start();
            
            return updatedProduct;
        });
    }
    
    /**
     * 带版本控制的更新(乐观锁)
     */
    public Product updateProductWithVersion(Product product) {
        String lockKey = PRODUCT_LOCK_PREFIX + product.getId();
        
        return lockService.executeWithLock(lockKey, 
                                        DEFAULT_LOCK_WAIT_TIME, 
                                        DEFAULT_LOCK_LEASE_TIME,
                                        () -> {
            // 1. 先获取当前版本
            Product existing = productRepository.findById(product.getId())
                .orElseThrow(() -> new RuntimeException("Product not found"));
            
            // 2. 检查版本
            if (!existing.getVersion().equals(product.getVersion())) {
                throw new RuntimeException("Optimistic lock failed");
            }
            
            // 3. 更新数据库
            Product updatedProduct = productRepository.save(product);
            
            // 4. 删除缓存
            redisTemplate.delete(PRODUCT_CACHE_PREFIX + product.getId());
            
            return updatedProduct;
        });
    }
}

2,基于消息队列的最终一致性

@Service
@RequiredArgsConstructor
public class MQConsistencyService {
    private final RedisTemplate<String, Object> redisTemplate;
    private final ProductRepository productRepository;
    private final RabbitTemplate rabbitTemplate;
    
    private static final String CACHE_UPDATE_QUEUE = "cache.update.queue";
    
    /**
     * 更新数据库并发送缓存更新消息
     */
    public void updateProductWithMQ(Product product) {
        // 1. 更新数据库
        productRepository.save(product);
        
        // 2. 发送消息到MQ
        rabbitTemplate.convertAndSend(CACHE_UPDATE_QUEUE, 
                                    new CacheUpdateMessage(
                                        product.getId(), 
                                        "DELETE"));
    }
    
    /**
     * 消息处理器
     */
    @RabbitListener(queues = CACHE_UPDATE_QUEUE)
    public void handleCacheUpdate(CacheUpdateMessage message) {
        try {
            if ("DELETE".equals(message.getAction())) {
                redisTemplate.delete("product:" + message.getProductId());
            }
            // 可以扩展其他操作
        } catch (Exception e) {
            // 加入重试逻辑
        }
    }
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class CacheUpdateMessage {
        private Long productId;
        private String action;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值