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

程序员面试资料大全|各种技术书籍等资料-1000G
IDEA开发工具- FREE

一、双写一致性问题本质

在分布式系统中,缓存与数据库双写一致性指当数据被修改时,如何确保缓存(如Redis)和数据库(如MySQL)中的数据保持同步。核心挑战在于处理并发操作系统故障场景下的数据一致性问题。

典型不一致场景

ClientA Cache DB ClientB 更新数据X=1 成功 删除缓存X 读X(缓存未命中) 读X(旧值X=0) 写入缓存X=0 缓存中存储了旧值 ClientA Cache DB ClientB

二、主流解决方案对比

方案适用场景优点缺点一致性强度
Cache-Aside读多写少简单易实现存在不一致时间窗口最终一致
Write-Through写密集型强一致性保证性能损耗大强一致
Write-Behind高吞吐场景高性能数据丢失风险最终一致
双删策略高一致性要求减少不一致窗口实现复杂强一致

三、核心解决方案详解

方案1:Cache-Aside(旁路缓存)

最佳实践:读多写少场景

写操作
更新数据库
删除缓存
读操作
缓存存在?
返回缓存数据
从数据库读取
写入缓存
返回数据

关键实现代码:

public void updateData(Data data) {// 1. 更新数据库dataDao.update(data);// 2. 删除缓存redis.del(data.getId());
}public Data getData(String id) {// 1. 从缓存获取Data data = redis.get(id);if (data != null) {return data;}// 2. 从数据库读取data = dataDao.get(id);// 3. 写入缓存(设置过期时间)redis.setex(id, 300, data);return data;
}

方案2:Write-Through(穿透写入)

最佳实践:强一致性要求场景

Client Cache DB 写请求 同步写入数据 写入结果 操作结果 Client Cache DB

特点:

  • 缓存层作为数据库代理
  • 所有写操作同步更新缓存和数据库
  • 读操作只访问缓存

方案3:Write-Behind(异步回写)

最佳实践:高吞吐场景

写操作
写入缓存
异步队列
批量更新数据库

实现代码示例:

// 使用内存队列实现异步更新
private BlockingQueue<Data> writeQueue = new LinkedBlockingQueue<>();public void updateData(Data data) {// 1. 更新缓存redis.set(data.getId(), data);// 2. 加入异步队列writeQueue.offer(data);
}// 单独的消费者线程
class DbWriter implements Runnable {public void run() {while (true) {Data data = writeQueue.take();dataDao.update(data); // 批量更新优化}}
}

方案4:双删策略(Double Delete)

最佳实践:高一致性要求场景

Client Cache DB 第一次删除缓存 更新数据库 第二次删除缓存(延迟) Client Cache DB

实现代码:

public void updateDataWithDoubleDelete(Data data) {// 1. 首次删除缓存redis.del(data.getId());// 2. 更新数据库dataDao.update(data);// 3. 延迟二次删除executor.schedule(() -> {redis.del(data.getId());}, 500, TimeUnit.MILLISECONDS); // 500ms延迟
}

四、高级一致性保障方案

方案1:分布式事务(强一致)

业务开始
开启分布式事务
更新数据库
更新缓存
操作成功?
提交事务
回滚事务

实现技术:

  • 2PC(两阶段提交)
  • TCC(Try-Confirm-Cancel)
  • Saga事务模式

方案2:基于Binlog的数据同步

Binlog
MySQL
消息队列
缓存更新服务
Redis

实现组件:

  1. Canal监听MySQL Binlog
  2. Kafka/RocketMQ作为消息队列
  3. 消费者服务更新缓存

优点:

  • 完全解耦
  • 保证最终一致性
  • 支持重试机制

五、异常场景处理方案

1. 缓存更新失败

成功
失败
更新数据库
删除缓存
成功?
完成
加入重试队列
定时重试
告警通知

2. 数据库更新失败

  • 事务回滚
  • 补偿机制恢复缓存
public void updateDataWithCompensation(Data data) {try {// 1. 开启事务transaction.begin();// 2. 更新数据库dataDao.update(data);// 3. 删除缓存redis.del(data.getId());// 4. 提交事务transaction.commit();} catch (Exception e) {// 5. 事务回滚transaction.rollback();// 6. 恢复缓存Data oldData = dataDao.get(data.getId());redis.set(data.getId(), oldData);}
}

六、最佳实践选择指南

场景特征推荐方案配置建议
读多写少,容忍短暂不一致Cache-Aside缓存过期时间 5-30分钟
写密集型,强一致性要求Write-Through配合本地缓存减少DB压力
超高吞吐,可接受秒级延迟Write-Behind批量大小100-500条,刷新间隔1s
金融交易类系统分布式事务TCC模式+异步对账
大型电商平台Binlog同步Canal+Kafka+消费者集群

七、性能优化技巧

  1. 批量处理:合并多个缓存操作

    public void batchUpdate(List<Data> dataList) {// 批量更新数据库dataDao.batchUpdate(dataList);// 批量删除缓存List<String> keys = dataList.stream().map(Data::getId).collect(Collectors.toList());redis.del(keys.toArray(new String[0]));
    }
    
  2. 热点数据特殊处理

    // 使用互斥锁防止缓存击穿
    public Data getHotData(String id) {Data data = redis.get(id);if (data == null) {if (redis.setnx("lock:" + id, "1")) {redis.expire("lock:" + id, 10); // 设置锁超时data = dataDao.get(id);redis.set(id, data);redis.del("lock:" + id);} else {// 等待重试Thread.sleep(50);return getHotData(id);}}return data;
    }
    
  3. 多级缓存策略

    客户端
    CDN
    边缘缓存
    Redis集群
    本地缓存
    数据库

八、监控与度量指标

  1. 关键监控项

    • 缓存命中率(Hit Ratio)
    • 缓存更新延迟(Update Latency)
    • 不一致事件计数
    • 重试队列长度
  2. 告警规则

    # Prometheus告警规则示例
    - alert: HighCacheInconsistencyRateexpr: rate(cache_inconsistency_count[5m]) > 0.5for: 10mlabels:severity: criticalannotations:summary: "缓存不一致率过高"- alert: CacheUpdateTimeoutexpr: cache_update_latency_seconds > 1for: 5mlabels:severity: warningannotations:summary: "缓存更新延迟超过阈值"
    

程序员面试资料大全|各种技术书籍等资料-1000G
IDEA开发工具- FREE

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.tpcf.cn/web/87196.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt 5.9 XML文件写入指南

Qt 5.9 XML文件写入指南 在Qt 5.9中&#xff0c;有多种方法可以编写XML文件。下面我将介绍三种主要方法&#xff0c;并提供完整的代码示例和最佳实践。 三种XML写入方法对比 方法优点缺点适用场景QXmlStreamWriter高效、内存占用低无树形结构大型XML文件QDomDocument树形结构…

一些ubuntu命令记录(持续补充)

一、查看代码运行占用的内存 1、使用 top 命令 top 命令是一个实时的系统监控工具&#xff0c;可以显示当前系统中所有进程的资源使用情况。运行以下命令&#xff1a; top 在 top 界面中&#xff0c;可以看到每个进程的内存使用情况&#xff08;%MEM 列&#xff09;。 如何…

今日学习:音视频领域入门文章参考(待完善)

音视频领域概览 入门文章参考 CSDN 雷神 博客园 2022-5-22

.npmrc和.yarnrc配置文件介绍:分别用于 Node.js 中的 npm(Node Package Manager)和 Yarn 包管理工具

.npmrc 和 .yarnrc 是两个配置文件&#xff0c;分别用于 Node.js 中的 npm&#xff08;Node Package Manager&#xff09;和 Yarn 包管理工具。它们存储了与包管理相关的配置选项&#xff0c;允许用户自定义和控制包的安装、版本、缓存等行为。下面是它们的详细说明&#xff1a…

数字人分身 + 矩阵系统聚合:源码搭建,支持OEM

在 AIGC 技术爆发的当下&#xff0c;数字人分身已从概念走向实用&#xff0c;而矩阵系统的聚合能力则让单个数字人分身突破场景限制&#xff0c;实现 “一人多岗” 的规模化应用。无论是企业客服、直播带货&#xff0c;还是教育培训、虚拟社交&#xff0c;数字人分身 矩阵系统…

学习昇腾开发的第12天--安装第三方依赖

第三方依赖安装指导&#xff08;C样例&#xff09; 前置条件 1. 按照官方指导文档完成CANN包安装。 2. CANN版本需要>5.0.4.alpha001&#xff0c;低于此版本请参见昇腾CANN样例仓介绍中的版本说明切换tag并使用发行版。 安装须知 samples仓中的部分c样例使用到opencv&am…

机器人仿真(1)Ubuntu24.04下CLion的ROS2开发环境配置

目录 一、前言二、配置要求安装ROS2安装CLion 三、配置步骤四、后记 一、前言 近日CLion已开放非商用免费使用。相比教程中常用的VSCode&#xff0c;CLion在自动补全、调试和环境变量配置等方面表现更为出色。不过截至本文撰写时&#xff0c;CLion官网仅提供了Windows系统下的…

WPF两种绑定方式的分析

一、两种绑定方式的分析 你提供的代码展示了两种不同的属性绑定实现方式&#xff1a;传统的CLR属性配合INotifyPropertyChanged接口&#xff0c;以及WPF依赖属性(DependencyProperty)系统。 相同点 目的相同&#xff1a;两种方式都是为了实现属性值变化时通知UI更新数据绑定…

【零基础学AI】第14讲:支持向量机实战 - 文本分类系统

本节课你将学到 理解支持向量机的核心思想和几何直觉 掌握SVM的关键参数和核函数选择 学会文本数据预处理和特征提取 完成一个邮件分类项目 对比SVM与其他算法的性能差异 开始之前 环境要求 Python 3.8内存: 建议2GB 需要安装的包 pip install pandas numpy scikit-learn …

美团 mtgsig1.2 最新版分析

声明: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 逆向分析 部分代码 result cp.call…

【实战】CRMEB Pro 企业版安装教程(附 Nginx 反向代理配置 + 常见问题解决)

一、前言 CRMEB Pro 是一款企业级高并发高性能的电商系统&#xff0c;支持 Linux 服务器环境&#xff0c;需要 PHP 8.0 及以上版本&#xff0c;兼容多种 WEB 服务器&#xff08;如 Nginx 和 Apache&#xff09;&#xff0c;并支持 MySQL 数据库。本文将详细介绍如何从零开始安…

解决Linux下根目录磁盘空间不足的问题

ubantu中提示根目录磁盘空间不足 解决办法&#xff1a;对根目录磁盘空间进行扩展。 一、使用lsblk查看磁盘使用情况 命令行输入&#xff1a;lsblk aaaubuntu:~/Desktop$ lsblk可以看到sda5是挂载在根目录上的。所以我们要对sda5进行扩展 二、扩展硬盘空间 1、关闭虚拟机 2、…

【C++】--入门

前面我们学习C语言的时候&#xff0c;我们也有讲过C的部分历史&#xff0c;我们看其名字就知道其和我们的C语言肯定是有密不可分的关系的&#xff0c;我们的C是在C的基础上发展的&#xff0c;其弥补了C语⾔在表达能⼒、可维护性 和可扩展性⽅⾯的不⾜。 下面为C的近年来的几次…

JAVA内存区域划分

根据《JAVA虚拟机规范》的规定&#xff0c;JAVA虚拟机在执行JAVA程序的过程中会把内存划分为不同的数据区域。不同类型的数据会存储在不同的区域&#xff0c;理解JAVA内存区域的工作细节对理解JAVA多线程、线程安全性有着重要意义。 注意&#xff0c;JAVA内存区域的划分与我们…

Navicat 导入 SQL 文件

1. 安装并打开 Navicat 安装 Navicat&#xff08;如 Navicat Premium、Navicat for MySQL&#xff09;&#xff0c;百度或者淘宝就有很多破解版。 打开 Navicat&#xff0c;进入主界面。 2. 新建数据库连接 点击左上角 “连接” 按钮&#xff0c;选择你对应的数据库类型&…

《Go语言高级编程》玩转RPC

《Go语言高级编程》玩转RPC 一、客户端 RPC 实现原理&#xff1a;异步调用机制 Go 的 RPC 客户端支持同步和异步调用&#xff0c;核心在于 Client.Go 方法的实现&#xff1a; 1. 同步调用&#xff08;Client.Call&#xff09;的本质 func (client *Client) Call(serviceMet…

四大核心要素驱动汽车智能化创新与相关芯片竞争格局

作者&#xff1a;北京华兴万邦管理咨询有限公司 翔煜 商瑞 智能汽车时代的加速到来&#xff0c;使车载智能系统面临前所未有的算力需求。随着越来越多车型引入电子电气架构转向中心化、智能驾驶的多传感器融合、智能座舱的多模态交互以及生成式AI驱动的虚拟助手等创新技术&a…

照明新基建:塔能科技如何用数字骨骼支撑智慧城市生长

一、能源管理困局&#xff1a;双碳目标下的市政用电痛点 在双碳背景下&#xff0c;城市照明用电量已引起市政部门的重点关注。据国家统计局统计&#xff1a;我国城市照明用电量已占据全市城市用电量的28%&#xff0c;部分城市的照明用电量已高达35%以上&#xff0c;高压钠灯传统…

让Claude Code像Cursor一样好用

最近折腾AI工具&#xff0c;发现Claude Code真是个宝藏。但说实话&#xff0c;初学者一上手&#xff0c;十有八九会被命令行那一堆黑框框劝退。你以为你用熟了&#xff1f;其实你只解锁了Claude Code不到20%的威力&#xff0c;剩下的80%都藏在命令行背后的“黑魔法”里。00后谁…

ROS 2 中更改从设备(如电机控制器)的运动模式

在 ROS 2 中更改从设备&#xff08;如电机控制器&#xff09;的运动模式&#xff08;例如从位置模式切换到速度模式&#xff09;&#xff0c;需要通过操作模式&#xff08;Mode of Operation&#xff0c;对应对象字典索引0x6060&#xff09; 进行设置。结合你的配置&#xff08…