redis:Redis缓存与数据库一致性深度解析

Redis缓存与数据库一致性解析

Redis缓存与数据库一致性深度解析

一、缓存一致性核心问题

1. 数据一致性流程图

先DB
先缓存
数据更新
先操作DB还是缓存?
更新数据库
删除缓存
删除缓存
更新数据库
存在不一致时间窗口
如何最小化不一致?

2. 典型方案对比

方案一致性强度实现复杂度适用场景
缓存删除模式最终一致简单读多写少
双写模式较强中等写多读少
延迟双删较强较高高一致性要求
订阅Binlog强一致复杂金融级系统

二、生产环境解决方案(电商订单系统案例)

1. 延迟双删策略实现

时序图

ClientCacheDBMQ删除缓存更新数据成功发送延迟消息(500ms)二次删除确认删除ClientCacheDBMQ

代码实现

public class OrderService {
    private final RedisTemplate<String, Object> redisTemplate;
    private final RabbitTemplate rabbitTemplate;
    
    @Transactional
    public void updateOrder(Order order) {
        // 第一次删除
        redisTemplate.delete("order:" + order.getId());
        
        // 更新数据库
        orderDao.update(order);
        
        // 发送延迟消息
        rabbitTemplate.convertAndSend(
            "cache.delete.delay.exchange",
            "cache.delete",
            order.getId(),
            message -> {
                message.getMessageProperties()
                    .setDelay(500); // 500ms延迟
                return message;
            });
    }
    
    @RabbitListener(queues = "cache.delete.queue")
    public void handleDelayMessage(Long orderId) {
        // 第二次删除
        redisTemplate.delete("order:" + orderId);
    }
}

2. Binlog监听方案

架构图

Binlog
MQ
MySQL
Canal
消费者服务
删除/更新缓存

关键配置

public class BinlogConsumer {
    @KafkaListener(topics = "canal_topic")
    public void process(ChangeEvent event) {
        if (event.getTable().equals("orders")) {
            String cacheKey = "order:" + event.getPrimaryKey();
            switch (event.getType()) {
                case INSERT:
                case UPDATE:
                    Order order = queryFromDB(event.getPrimaryKey());
                    redisTemplate.opsForValue().set(
                        cacheKey, order, 30, TimeUnit.MINUTES);
                    break;
                case DELETE:
                    redisTemplate.delete(cacheKey);
                    break;
            }
        }
    }
}

三、大厂面试深度追问

追问1:如何解决高并发下的缓存与DB一致性问题?

解决方案(库存系统案例)

  1. 版本号控制机制
public class VersionedCache {
    private final RedisTemplate<String, Object> redisTemplate;
    
    public void updateWithVersion(String key, Object value, long version) {
        while (true) {
            Long currentVersion = (Long) redisTemplate.opsForValue()
                .get(key + ":version");
                
            if (currentVersion == null || version > currentVersion) {
                // 乐观锁控制
                redisTemplate.watch(key + ":version");
                redisTemplate.multi();
                redisTemplate.opsForValue().set(key, value);
                redisTemplate.opsForValue().set(key + ":version", version);
                if (redisTemplate.exec() != null) {
                    break;
                }
            } else {
                // 版本冲突处理
                refreshFromDB(key);
                break;
            }
        }
    }
}
  1. 分布式事务方案
public class XACacheService {
    @Transactional
    public void updateWithXA(String key, Object value) {
        // 1. 记录事务日志
        transactionLogDao.insert(new TransactionLog(key));
        
        // 2. 更新数据库
        dataDao.update(value);
        
        // 3. 提交Redis事务
        redisTemplate.execute(new SessionCallback<>() {
            @Override
            public Object execute(RedisOperations operations) {
                operations.multi();
                operations.opsForValue().set(key, value);
                operations.exec();
                return null;
            }
        });
    }
    
    @Scheduled(fixedRate = 10000)
    public void compensate() {
        // 定时检查未完成事务
        List<TransactionLog> logs = transactionLogDao.findUnfinished();
        logs.forEach(log -> {
            Object value = dataDao.getById(log.getKey());
            redisTemplate.opsForValue().set(log.getKey(), value);
            transactionLogDao.updateStatus(log.getId(), "COMPLETED");
        });
    }
}

追问2:如何处理缓存与DB更新失败的不同步问题?

解决方案(支付系统案例)

  1. 事务状态追踪表
CREATE TABLE cache_sync_status (
    id BIGINT PRIMARY KEY,
    business_key VARCHAR(64) UNIQUE,
    cache_status TINYINT COMMENT '0-未同步 1-已同步',
    retry_count INT DEFAULT 0,
    create_time DATETIME,
    update_time DATETIME
);
  1. 异步修复服务
public class CacheRepairService {
    private final ScheduledExecutorService executor;
    
    public void init() {
        executor.scheduleAtFixedRate(this::repair, 5, 5, TimeUnit.SECONDS);
    }
    
    private void repair() {
        List<CacheSyncStatus> failedItems = dao.selectFailedItems();
        failedItems.forEach(item -> {
            try {
                Object data = dbQuery(item.getBusinessKey());
                redisTemplate.opsForValue().set(
                    item.getBusinessKey(), 
                    data);
                dao.updateStatus(item.getId(), 1);
            } catch (Exception e) {
                dao.incrementRetry(item.getId());
                if (item.getRetryCount() > 5) {
                    alertAdmin(item);
                }
            }
        });
    }
}
  1. 多级修复策略
检测不一致
不一致时间<1s?
立即修复
1s<时间<1m?
异步队列修复
人工介入

四、架构防御最佳实践

  1. 多级一致性保障
保障级别技术方案一致性强度性能影响
基础级删除缓存最终一致<5%
进阶级延迟双删较强一致10-15%
企业级Binlog监听强一致20-30%
金融级分布式事务严格一致50%+
  1. Redis配置建议
# 开启AOF持久化
appendonly yes
appendfsync everysec

# 集群配置
cluster-enabled yes
cluster-node-timeout 5000
  1. 监控指标
  • 缓存不一致率(缓存未命中且DB存在)/总请求
  • 修复延迟数据更新到缓存更新耗时
  • 修复成功率成功修复数/总修复数
  1. 未来演进方向
  • 混合一致性协议:结合RAFT和最终一致性
  • AI预测修复:基于历史模式预测需要修复的数据
  • 持久内存应用:使用PMEM减少同步延迟
  • Serverless架构:自动扩缩容的修复服务
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WeiLai1112

你的鼓励将是我创作的最大动力。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值