Redis如何与数据库保持双写一致性

总结

为确保Redis与数据库双写一致性,常用方法包括:先更新数据库再删缓存(Cache-Aside)、延迟双删(更新前后均删缓存并延迟二次删除)、基于消息队列异步同步、监听数据库binlog触发缓存更新,以及设置缓存过期时间兜底。需结合业务场景选择策略,配合重试机制确保最终一致性。

详细解析

保证缓存与数据库的双写一致性是分布式系统设计中的核心挑战,需结合 业务场景 和 技术特性 选择合适策略。以下是主流解决方案及原理分析:


一、核心策略分类

策略核心思想适用场景一致性级别
Cache Aside读时优先读缓存,写时先更新数据库再删除缓存通用场景(读多写少)最终一致性
延迟双删更新数据库后,延迟一段时间再次删除缓存高并发读写、热点数据最终一致性

消息队列

异步同步

通过消息队列异步更新缓存,保证最终一致性高吞吐、允许短暂延迟最终一致性
分布式锁写操作时加锁,确保同一数据串行更新强一致性要求的高并发场景强一致性
TCC 模式

业务层分阶段提交(Try-Confirm-Cancel),保证数据

库和缓存操作的原子性

金融级强一致性场景强一致性

二、主流方案详解

  1. Cache Aside 模式(旁路缓存模式)
    • 操作流程:
    • 读请求:先读缓存 → 缓存未命中则读数据库 → 回填缓存。
    • 写请求:先更新数据库 → 删除缓存(而非更新缓存)。
    • 原理:
    • 删除缓存避免旧值回填(若先更新缓存,其他线程可能读取旧数据库值并覆盖新缓存)。
    • 依赖 读操作重建缓存 的惰性机制。
    • 代码示例:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    // 读操作

    public Data getData(String key) {

       Data data = cache.get(key);

       if (data == null) {

           data = db.query(key);  // 从数据库加载

           cache.put(key, data);  // 回填缓存

       }

       return data;

    }

    // 写操作

    public void updateData(String key, Data newData) {

       db.update(key, newData);  // 先更新数据库

       cache.delete(key);        // 再删除缓存

    }

    • 优点:实现简单,适合大多数读多写少场景。
    • 缺点:存在短暂不一致窗口(如写操作后,新读请求可能命中旧缓存)。
    • 优化:对热点数据采用 读写锁 或 互斥锁(如 Redis 的SETNX)。
  2. 延迟双删策略
    • 操作流程:
    • 第一次删除缓存:更新数据库后立即删除。
    • 延迟删除:通过定时任务或异步线程,延迟一段时间后再次删除缓存。
    • 原理:
    • 解决 主从同步延迟 导致的脏数据问题(如从库读取旧数据回填缓存)。
    • 延迟时间需大于主从同步耗时(通常 100-500ms)。
    • 代码示例:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    // 写操作

    public void updateWithDoubleDelete(String key, Data newData) {

       db.update(key, newData);

       cache.delete(key);  // 第一次删除

       scheduleDelete(key, 300);  // 延迟 300ms 后再次删除

    }

    private void scheduleDelete(String key, long delay) {

       executor.schedule(() -> cache.delete(key), delay, TimeUnit.MILLISECONDS);

    }

    • 优点:降低脏数据概率。
    • 缺点:需维护定时任务,可能增加系统复杂度。
  3. 消息队列异步同步
    • 操作流程:
    • 写数据库成功后,发送消息到 MQ。
    • 消费 MQ 消息,更新或删除缓存。
    • 原理:
    • 通过 MQ 解耦数据库和缓存操作,保证最终一致性。
    • 支持异步重试(如 RabbitMQ 的死信队列)。
    • 代码示例:

    1

    2

    3

    4

    5

    6

    7

    // 写操作

    public void updateWithMQ(String key, Data newData) {

       db.update(key, newData);

       mq.send("cache-update", key);  // 发送删除缓存指令

    }

    // MQ 消费者

    mq.consume("cache-update", key -> cache.delete(key));

    • 优点:高吞吐、解耦系统。
    • 缺点:依赖 MQ 可靠性,需处理消息积压和重复消费。
  4. 分布式锁控制
    • 操作流程:
    • 写操作前获取分布式锁(如 Redis 的 RedLock)。
    • 更新数据库 → 删除缓存 → 释放锁。
    • 原理:
    • 串行化写操作,避免并发冲突。
    • 代码示例:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    public void updateWithLock(String key, Data newData) {

       String lockKey = "lock:" + key;

       if (redis.tryLock(lockKey, 10, TimeUnit.SECONDS)) {

           try {

               db.update(key, newData);

               cache.delete(key);

           finally {

               redis.unlock(lockKey);

           }

       }

    }

    • 优点:强一致性保障。
    • 缺点:性能瓶颈明显,锁粒度控制困难。
  5. TCC 模式(Try-Confirm-Cancel)
    • 操作流程:
    • Try 阶段:预留资源(如冻结账户余额)。
    • Confirm 阶段:确认操作(如扣减余额并更新缓存)。
    • Cancel 阶段:释放预留资源(如回滚余额并恢复缓存)。
    • 原理:
    • 业务层控制事务边界,保证数据库和缓存操作的原子性。
    • 代码示例:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    // Try 阶段

    public boolean tryUpdate(String key, Data newData) {

       // 冻结资源(如预扣库存)

       return db.tryUpdate(key, newData);

    }

    // Confirm 阶段

    public void confirmUpdate(String key) {

       db.commit(key);

       cache.put(key, getFromDb(key));  // 更新缓存

    }

    // Cancel 阶段

    public void cancelUpdate(String key) {

       db.rollback(key);

       cache.delete(key);  // 恢复旧缓存

    }

    • 优点:强一致性,适合金融场景。
    • 缺点:业务侵入性强,需设计补偿逻辑。

三、选型建议

场景推荐方案原因
通用业务(读多写少)Cache Aside实现简单,性能高,适合大多数场景。
高并发写+热点数据延迟双删 + 互斥锁减少脏数据概率,控制重建并发。
异步解耦消息队列削峰填谷,适合最终一致性要求。
强一致性需求TCC 或分布式锁保证操作原子性,适用于金融级场景。

四、总结

没有绝对完美的方案,需根据业务特点选择:
• 强一致性:TCC 或分布式锁(牺牲部分性能)。
• 最终一致性:Cache Aside 或消息队列(平衡性能与可靠性)。
• 特殊场景:延迟双删解决主从同步问题,多级缓存应对高并发。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值