如何保证缓存和数据库的双写一致性


一、一致性分级与适用场景

级别 延迟范围 适用场景 代表方案
强一致性 0ms 金融账户、库存扣减 分布式锁+同步双写
最终一致性 10ms-10s 商品详情、用户信息 延迟双删、消息队列
弱一致性 >10s 文章阅读量、排行榜 定时全量刷新

二、经典解决方案详解

1. Cache-Aside Pattern(旁路缓存)

流程
App Cache DB 读请求 返回数据 查询数据 返回数据 写入缓存 alt [缓存命中] [未命中] 写数据 删除缓存 App Cache DB

问题 :先写库后删缓存的间隙可能读到旧数据
优化:异步延迟双删(解决并发读写脏数据)


2. Write-Through(穿透写)

架构
应用 Cache Write Handler DB

  • 所有写请求先到Cache,由Cache同步写DB
  • 优点:确保缓存永不过期
  • 缺点:写延迟高(需等待DB完成)

3. Write-Behind(异步写)

流程
App Cache DB 写数据 立即响应 批量写回 loop [异步批处理] App Cache DB

特点

  • 高性能(写操作≈内存速度)
  • 数据可能丢失(宕机时缓存未刷盘)
  • 需配合WAL日志(如Redis AOF)

三、高并发场景下的精控方案

4. 分布式锁强一致
java 复制代码
// Redisson实现示例
public void updateData(Data newData) {
    RLock lock = redisson.getLock("DATA_LOCK:" + newData.id);
    try {
        lock.lock();  // 获取分布式锁
        db.update(newData);   // 更新数据库
        cache.delete(newData.id); // 删除缓存
    } finally {
        lock.unlock();
    }
}

缺陷:性能下降(吞吐量≈DB单点写入能力)


5. 版本号控制(乐观锁)

操作步骤

  1. 数据中增加版本号字段 version

  2. 更新时校验版本号:

    sql 复制代码
    UPDATE table SET value=#{value}, version=version+1 
    WHERE id=#{id} AND version=#{oldVersion}
  3. 若更新失败,重试或回滚缓存


6. Binlog监听同步(最终一致)

Binlog 消息 MySQL Canal RocketMQ 缓存更新Worker Redis

优势

  • 彻底解耦应用层
  • 支持多级缓存同步
    时延:100ms-2s(取决于MQ堆积)

四、特殊问题解决方案

7. 缓存删除失败补偿机制

失败 成功 失败 删除缓存 记录删除失败Key 定时任务 读取失败队列 重试删除 移除队列 报警+人工介入

关键参数

  • 首次重试:1s后
  • 指数退避:最大重试5次
  • 死信处理:NACK后持久化存储

8. 热点Key并发重建

问题 :缓存失效瞬间大量请求击穿DB
解法

java 复制代码
// 使用Redis SETNX 互斥锁
public Data getData(String key) {
    Data data = cache.get(key);
    if (data == null) {
        String lockKey = "LOCK:" + key;
        if (redis.setnx(lockKey, 1)) { // 获取锁
            redis.expire(lockKey, 3);  // 避免死锁
            data = db.query(key);      // 查库
            cache.set(key, data, 30);  // 写缓存
            redis.del(lockKey);        // 释放锁
        } else {
            Thread.sleep(50);          // 等待后重试
            return getData(key);       // 递归调用
        }
    }
    return data;
}

五、方案选型决策树

graph TD A[业务要求强一致?] -->|是| B[分布式锁] A -->|否| C{写频率} C -->|高频写| D[Write-Behind+WAL] C -->|低频写| E[Cache-Aside+延迟双删] C -->|批量写| F[Binlog监听]

六、工业级配置参数参考

组件 关键配置项 推荐值 作用
Redis maxmemory-policy volatile-lfu 内存淘汰策略
Canal canal.mq.retries 5 MQ发送重试次数
Redisson lockWatchdogTimeout 30000 看门狗超时(ms)
本地缓存 Caffeine.expireAfterWrite 5s 防雪崩时间窗口

七、容灾设计

  1. 降级开关

    java 复制代码
    // 配置中心动态开关
    if (ConfigService.getBool("DISABLE_CACHE")) {
        return db.query(key); // 直接读库
    }
  2. 缓存污染防护

    • 空值缓存:SET key null 5s 防穿透
    • 布隆过滤器:拦截非法Key请求
  3. 多级熔断

    指标 阈值 动作
    DB QPS >5000 关闭缓存更新
    缓存Miss率 >40% 开启本地缓存
    平均响应时间 >200ms 跳过缓存直读DB

你想要的我全都有:https://pan.q删掉憨子uark.cn/s/75a5a07b45a2