开发者

Redis和数据库双写一致性问题的解决方案

目录
  • 问题解答
    • 解决方案(含故事举例)
  • 1. 先更新数据库,再删除缓存(推荐方案)
    • 2. 延时双删策略
      • 3. 监听数据库变更(最终一致性)
        • 4. 加分布式锁(强一致)
          • 总结对比

            下面我将详细解答Redis与数据库双写一致性问题,结合故事举例说明,最后用思维导图总结核心解决方案。

            问题解答

            核心问题:当同时更新Redis缓存和数据库时,由于网络延迟、操作顺序等问题导致数据不一致。

            解决方案(含故事举例)

            假设电商平台有商品库存系统:

            // 商品服务类
            public class ProductService {
                private DB db = new mysql();  // 数据库
                private Cache cache = 编程客栈new Redis();  // 缓存
            }
            

            1. 先更新数据库,再删除缓存(推荐方案)

            public void updateProduct(Product product) {
                db.update(product);          // 1. 更新数据库
                cache.delete(product.getId());// 2. 删除缓存
            }
            

            故事场景

            管理员修改商品价格(100→80元):

            • 数据库先更新为80元 ✅
            • 删除Redis中该商品的缓存

            用户查询时:

            • 缓存未命中 → 读数据库(80元) ✅
            • 回填缓存(80元) ✅

            优势

            • 避免"更新缓存失败导致永久不一致"
            • 缓存删除失www.devze.com败可重试(通过消息队列)

            风险

            并发时短暂不一致(概率低):

            • 用户A读缓存(空) → 读数据库(100元)
            • 管理员更新数据库(100→80) → 删缓存
            • 用户A写缓存(100元) ❌(旧数据)
            • 解决方案:延时双删(下文说明)

            2. 延时双删策略

            public void updateWithDelay(Product product) {
                cache.delete(product.getId());    // 1. 先删缓存
                db.update(product);               // 2. 更新数据库
                Thread.sleep(500);                // 3. 等待500ms
                cache.delete(product.getId());    // 4. 再删缓存
            }
            

            故事场景

            解决上述并发问题:

            • 首次删除:清空旧缓存
            • 更新数据库 ✅
            • 等待期间可能写入的http://www.devze.com旧缓存被二次删除 ❌→✅

            3. 监听数据库变更(最终一致性)

            // 使用Canal监听MySQL binlog
            canal.subscribe(event -> {
                if (event.isUpdate()) {
                    cache.delete(event.getKey()); // 异步删除缓存
                }
            });
            

            故事场景

            订单系统库存变更:

            • 数据库减库存 ✅
            • Canal捕获变更事件
            • 自动删除Redis库存缓存
            • 下次查询回填最新值 ✅

            4. 加分布式锁(强一致)

            public void safeUpdate(Product product) {
                Lphpock lock = redisson.getLock("PRODUCT_" + product.getId());
                lock.lock();
                try {
                    db.update(product);
                    cache.update(product); // 同时更新缓存
                } finally {
                    lock.unlock();
                }
            }
            

            适用场景

            金融账户余额等强一致性要求:

            • 读写操作串行化
            • 性能较低(非高并发场景)

            总结对比

            方案android致性性能复杂度适用场景
            先DB后删缓存最终★★★★大部分业务
            延时双删最终★★★高并发场景
            监听binlog最终★★★★异构系统同步
            分布式锁强一致★★金融/账户系统

            核心原则

            • 优先保证数据库正确性
            • 缓存操作可失败/重试
            • 强一致性需牺牲性能

            Redis和数据库双写一致性问题的解决方案

            以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程客栈(www.devze.com)。

            0

            上一篇:

            下一篇:

            精彩评论

            暂无评论...
            验证码 换一张
            取 消

            最新数据库

            数据库排行榜