电商分布式场景中如何保证数据库与缓存的一致性?实战方案与Java代码详解
文章目录
- 一、缓存一致性问题的本质
- 写后读不一致:更新数据库后,缓存未及时失效
- 并发读写竞争:多个线程同时修改同一数据
- 缓存与数据库事务不同步:部分成功导致数据错乱
- 二、5大核心解决方案与代码实现
- 方案1:延迟双删策略(针对写后读不一致)
- 原理
- 适用场景
- 方案2:订阅数据库Binlog异步更新缓存(最终一致性)
- 原理
- 适用场景
- 方案3:分布式锁保证强一致性(针对并发写)
- 原理
- 适用场景
- 方案4:多级缓存兜底策略(防缓存击穿)
- 原理
- 适用场景
- 方案5:设置合理的缓存过期策略(平衡一致性与性能)
- 推荐策略
- 三、实战总结:如何选择方案?
在电商高并发场景中,缓存是提升系统性能的核心组件。然而,数据库与缓存之间的数据一致性一直是分布式系统的难点——缓存穿透、雪崩、脏数据等问题频繁出现。本文将深入分析6种典型场景的解决方案,并提供可直接落地的Java代码实现。
一、缓存一致性问题的本质
在分布式系统中,数据库与缓存的一致性矛盾来源于以下场景:
- 写后读不一致:更新数据库后,缓存未及时失效
- 并发读写竞争:多个线程同时修改同一数据
- 缓存与数据库事务不同步:部分成功导致数据错乱
写后读不一致:更新数据库后,缓存未及时失效
- 场景描述: 当应用程序更新了数据库中的数据后,如果缓存中的旧数据没有及时被清除或更新,后续的读取操作可能会从缓存中读取到过期的数据,导致“写后读不一致”。
- 举例说明: 假设有一个电商系统,用户A修改了自己的收货地址。系统首先将新地址写入数据库,但此时缓存中仍然保存着旧的收货地址。如果另一个用户B紧接着查询该用户的收货地址,可能会从缓存中读取到旧的地址信息,这就是典型的“写后读不一致”。
并发读写竞争:多个线程同时修改同一数据
- 场景描述: 当多个线程或进程同时对同一数据进行读写操作时,可能会出现并发冲突,导致数据不一致或部分更新丢失。
- 举例说明: 在一个在线论坛系统中,两个用户几乎同时编辑同一篇文章。假设系统先处理了用户A的更新请求,并将新内容写入数据库和缓存。然而,在这之前,用户B也提交了编辑请求。如果系统没有适当的并发控制机制,用户B的更新可能会覆盖用户A的更改,或者两者的结果相互干扰,导致文章内容混乱。
缓存与数据库事务不同步:部分成功导致数据错乱
- 场景描述: 在分布式系统中,缓存和数据库的操作通常不在同一个事务中管理。如果其中一个操作失败,而另一个成功,就会导致数据不一致。
- 举例说明: 考虑一个金融交易系统,用户发起一笔转账操作。系统需要同时更新数据库中的账户余额,并更新缓存中的用户余额信息。如果在更新数据库成功后,更新缓存时发生网络故障或其他异常,缓存中的余额将不会反映最新的转账结果。此时,用户再次查询余额时,可能会看到错误的金额。
二、5大核心解决方案与代码实现
方案1:延迟双删策略(针对写后读不一致)
原理
- 第一次删除:更新数据库前删除缓存
- 第二次延迟删除:数据库提交后,延迟再次删除缓存(确保并发读请求的旧缓存被清理)
// 商品服务 - 更新商品价格
public void updateProductPrice(Long productId, BigDecimal price) {// 1. 首次删除缓存redisTemplate.delete("product:" + productId);// 2. 更新数据库productDao.updatePrice(productId, price);// 3. 提交事务后,延迟二次删除(建议异步执行),这里也可以使用@Scheduled(fixedDelay = 2, timeUnit = TimeUnit.SECONDS)注解实现CompletableFuture.runAsync(() -> {try {Thread.sleep(2000); // 延迟2s(根据业务调整)redisTemplate.delete("product:" + productId);} catch (InterruptedException e) {Thread.currentThread().interrupt();}});
}
适用场景
- 对一致性要求较高的写操作(如商品价格修改)
方案2:订阅数据库Binlog异步更新缓存(最终一致性)
原理
- 通过Canal监听MySQL的Binlog
- 解析变更日志后,异步更新或删除缓存
流程:
MySQL → Canal → Kafka → 消费者 → 更新Redis
Java消费者代码片段:
@KafkaListener(topics = "canal.product.update")
public void handleProductUpdate(ProductUpdateEvent event) {// 根据操作类型处理缓存if (event.getType() == OperationType.UPDATE) {redisTemplate.delete("product:" + event.getProductId());// 或重新加载最新数据Product product = productDao.getById(event.getProductId());redisTemplate.opsForValue().set("product:" + product.getId(), product, 30, TimeUnit.MINUTES);}
}
适用场景
- 对实时性要求不高的数据(如商品描述信息)
方案3:分布式锁保证强一致性(针对并发写)
原理
- 使用Redisson分布式锁,确保同一时刻只有一个线程操作关键资源
public void deductStock(Long productId, int quantity) {RLock lock = redissonClient.getLock("product:lock:" + productId);try {// 尝试加锁,最多等待100ms,锁自动释放时间30sif (lock.tryLock(100, 30000, TimeUnit.MILLISECONDS)) {// 1. 查询数据库库存int stock = productDao.getStock(productId);if (stock < quantity) {throw new BusinessException("库存不足");}// 2. 扣减数据库库存productDao.deductStock(productId, quantity);// 3. 更新缓存库存(可设置较短的过期时间)redisTemplate.opsForValue().set("product:stock:" + productId, stock - quantity, 10, TimeUnit.SECONDS);}} finally {lock.unlock();}
}
适用场景
- 秒杀、抢购等高并发写操作
方案4:多级缓存兜底策略(防缓存击穿)
原理
- L1:本地缓存(Caffeine)
- L2:分布式缓存(Redis)
- L3:数据库
// 使用Caffeine+Redis双缓存
public Product getProductWithMultiCache(Long productId) {// 1. 查询本地缓存Product product = caffeineCache.get(productId, key -> {// 2. 本地缓存未命中,查询RedisString redisKey = "product:" + productId;Product p = redisTemplate.opsForValue().get(redisKey);if (p == null) {// 3. Redis未命中,查询数据库并回填p = productDao.getById(productId);redisTemplate.opsForValue().set(redisKey, p, 30, TimeUnit.MINUTES);}return p;});return product;
}
适用场景
- 高频访问的热点数据(如首页商品信息)
方案5:设置合理的缓存过期策略(平衡一致性与性能)
推荐策略
缓存类型 | 过期时间 | 更新策略 |
---|---|---|
商品基础信息 | 30分钟 | 被动失效 + Binlog更新 |
库存数据 | 10秒 | 延迟双删 |
订单状态 | 不缓存 | 直接读库 |
用户购物车 | 2小时 | 写操作同步更新 |
三、实战总结:如何选择方案?
- 强一致性场景(如金融账户):
- 分布式锁 + 同步写缓存
- 最终一致性场景(如商品详情):
- Binlog异步更新 + 合理过期时间
- 超高并发场景(如秒杀库存):
- 本地缓存 + Redis原子操作 + 限流降级
注意事项:
- 所有缓存操作必须添加降级开关(如Sentinel配置)
- 监控缓存命中率与数据库慢查询
- 避免“先更新数据库再删缓存”的陷阱(失败需重试)
通过以上方案组合,可有效解决电商场景下的缓存一致性问题。实际开发中需根据业务特点灵活选择,并配合监控告警体系持续优化。
相关文章:
电商分布式场景中如何保证数据库与缓存的一致性?实战方案与Java代码详解
文章目录 一、缓存一致性问题的本质写后读不一致:更新数据库后,缓存未及时失效并发读写竞争:多个线程同时修改同一数据缓存与数据库事务不同步:部分成功导致数据错乱 二、5大核心解决方案与代码实现方案1:延迟双删策略…...
DeepSeek-R1 大模型本地部署指南
文章目录 一、系统要求硬件要求软件环境 二、部署流程1. 环境准备2. 模型获取3. 推理代码配置4. 启动推理服务 三、优化方案1. 显存优化技术2. 性能加速方案 四、部署验证健康检查脚本预期输出特征 五、常见问题解决1. CUDA内存不足2. 分词器警告处理3. 多GPU部署 六、安全合规…...
【数据结构】 栈和队列
在计算机科学的世界里,数据结构是构建高效算法的基础。栈(Stack)和队列(Queue)作为两种基本且重要的数据结构,在软件开发、算法设计等众多领域都有着广泛的应用。今天,我们就来深入探讨一下栈和…...
用Python构建Mad Libs经典文字游戏
前言 Mad Libs 是一种经典的文字游戏,其中一名玩家向其他玩家询问各种词汇,如名词、动词、形容词等,而不提供任何上下文。然后将这些提示词插入到一个充满空白的故事模板中,从而创造出一个搞笑或荒谬的故事,供玩家大声朗读以获取乐趣。 自1950年代发明以来,Mad Libs 一…...
ReactiveSwift模拟登录功能
通过使用ReactiveSwift模拟一个简单的登录功能,该功能如下要求: 账号不能为空密码必须大于6位 登录按钮方可点击 LoginViewModel: import ReactiveSwiftclass LoginViewModel {// 创建两个信号let userName MutableProperty<String&g…...
亲测有效!使用Ollama本地部署DeepSeekR1模型,指定目录安装并实现可视化聊天与接口调用
文章目录 一、引言二、准备工作(Ollama 工具介绍与下载)2.1 Ollama介绍2.2 Ollama安装 三、指定目录安装 DeepSeek R1四、Chatbox 可视化聊天搭建4.1 Chatbox下载安装4.2 关联 DeepSeek R1 与 Chatbox 的步骤 五、使用 Ollama 调用 DeepSeek 接口5.1 请求…...
【第11章:生成式AI与创意应用—11.3 AI艺术创作的实现与案例分析:DeepArt、GANBreeder等】
凌晨三点的画室里,数字艺术家小美盯着屏幕上的GANBreeder界面——她将梵高的《星月夜》与显微镜下的癌细胞切片图进行混合,生成的新图像在柏林电子艺术展上引发轰动。这场由算法驱动的艺术革命,正在重写人类对创造力的定义。 一、机器视觉的觉醒之路 1.1 数字艺术的三次浪…...
MySQL的基本使用
MySQL 是一个强大且广泛使用的开源关系型数据库管理系统,适用于各种规模的应用程序。无论是初学者还是经验丰富的开发者,掌握 MySQL 的基本操作都是至关重要的。本文将带你了解 MySQL 的基础概念,并通过实例介绍如何执行一些常见的数据库操作…...
WEB安全--SQL注入--PDO与绕过
一、PDO介绍: 1.1、原理: PDO支持使用预处理语句(Prepared Statements),这可以有效防止SQL注入攻击。预处理语句将SQL语句与数据分开处理,使得用户输入的数据始终作为参数传递给数据库,而不会直…...
微信小程序image组件mode属性详解
今天学习微信小程序开发的image组件,mode属性的属性值不少,一开始有点整不明白。后来从网上下载了一张图片,把每个属性都试验了一番,总算明白了。现总结归纳如下: 1.使用scaleToFill。这是mode的默认值,sc…...
大模型炼丹基础--GPU内存计算
一、摘要 选择合适的GPU对成本和效率都至关重要,合理分析GPU 二、硬件计算基础 1 个字节可以表示零(00000000)和 255(11111111)之间的数字 模型参数常用的数据类型如下: float(32 位浮点&a…...
istio入门篇(一)
一、背景 一直以来“微服务”都是一个热门的词汇,在各种技术文章、大会上,关于微服务的讨论和主题都很多。对于基于 Dubbo、SpringCloud 技术体系的微服务架构,已经相当成熟并被大家所知晓,但伴随着互联网场景的复杂度提升、业务…...
Ubuntu 24.04.1 LTS 本地部署 DeepSeek 私有化知识库
文章目录 前言工具介绍与作用工具的关联与协同工作必要性分析 1、DeepSeek 简介1.1、DeepSeek-R1 硬件要求 2、Linux 环境说明2.1、最小部署(Ollama DeepSeek)2.1.1、扩展(非必须) - Ollama 后台运行、开机自启: 2.2、…...
沃德校园助手系统php+uniapp
一款基于FastAdminThinkPHPUniapp开发的为校园团队提供全套的技术系统及运营的方案(目前仅适配微信小程序),可以更好的帮助你打造自己的线上助手平台。成本低,见效快。各种场景都可以自主选择服务。 更新日志 V1.2.1小程序需要更…...
Visual Studio Code使用ai大模型编成
1、在Visual Studio Code搜索安装roo code 2、去https://openrouter.ai/settings/keys官网申请个免费的配置使用...
工业软件测试方案
一、方案概述 本测试方案致力于全面、系统地评估工业仿真软件的综合性能,涵盖性能表现、功能完整性以及用户体验层面的易用性。同时,将其与行业内广泛应用的MATLAB进行深入的对比分析,旨在为用户提供极具价值的参考依据,助力其在…...
红队视角出发的k8s敏感信息收集——Kubernetes API 扩展与未授权访问
针对 Kubernetes API 扩展与未授权访问 的详细攻击视角分析,聚焦 Custom Resource Definitions (CRD) 和 Aggregated API Servers 的潜在攻击面及利用方法: 攻击链示例 1. 攻击者通过 ServiceAccount Token 访问集群 → 2. 枚举 CRD 发现数据库配…...
一种 SQL Server 数据库恢复方案:解密、恢复并导出 MDF/NDF/BAK文件
方案特色 本方案可以轻松恢复和导出SQL数据库:MDF、NDF 和 BAK 文件。 恢复和导出SQL数据库:主(MDF),辅助(NDF)和备份(BAK)文件分析 SQL Server LOG 数据库事务日志将 …...
Pygame中自定义事件处理的方法2-1
1 Pygame事件处理流程 Pygame中的事件处理流程如图1所示。 图1 Pygame中事件处理流程 系统事件包括鼠标事件和键盘事件等,当用户点击了鼠标或者键盘时,这些事件会自动被放入系统的事件队列中。用户自定义事件需要通过代码才能被放入事件队列中。Pygame…...
langchain学习笔记之消息存储在内存中的实现方法
langchain学习笔记之消息存储在内存中的实现方法 引言背景消息存储在内存的实现方法消息完整存储:完整代码 引言 本节将介绍 langchain \text{langchain} langchain将历史消息存储在内存中的实现方法。 背景 在与大模型交互过程中,经常出现消息管理方…...
HarmonyOS组件之Tabs
Tabs 1.1概念 Tabs 视图切换容器,通过相适应的页签进行视图页面的切换的容器组件每一个页签对应一个内容视图Tabs拥有一种唯一的子集元素TabContent 1.2子组件 不支持自定义组件为子组件,仅可包含子组件TabContent,以及渲染控制类型 if/e…...
【C++】基础入门(详解)
🌟 Hello,我是egoist2023! 🌍 种一棵树最好是十年前,其次是现在! 目录 输入&输出 缺省参数(默认参数) 函数重载 引用 概念及定义 特性及使用 const引用 与指针的关系 内联inline和nullptr in…...
bps是什么意思
本文来自DeepSeek "bps" 是 "bits per second" 的缩写,表示每秒传输的比特数,用于衡量数据传输速率。1 bps 即每秒传输 1 比特。 常见单位 bps:比特每秒 Kbps:千比特每秒(1 Kbps 1,000 bps&am…...
OceanBase使用ob-loader-dumper导出表报ORA-00600
执行下面的语句导出表报错,同样的语句之前都没有报错。 ob-loader-dumper-4.2.8-RELEASE/bin/obdumper -h xxx.xxx.xxx.xxx -P 2883 -p 密码 --column-splitter| --no-sys-t gzuat_ss#ob8(集群) -D 数据库名 --cut --table teacher --no-ne…...
JUC并发总结一
大纲 1.Java集合包源码 2.Thread源码分析 3.volatile关键字的原理 4.Java内存模型JMM 5.JMM如何处理并发中的原子性可见性有序性 6.volatile如何保证可见性 7.volatile的原理(Lock前缀指令 + 内存屏障) 8.双重检查单例模式的volatile优化 9.synchronized关键字的原理 …...
hive:分区>>静态分区,动态分区,混合分区
分区表 使用场景:数据量庞大且经常用来做查询的表 特点:将数据分别存储到不同的目录里 优点:避免全盘扫描,提高查询效率 分区的类型 它们的默认值分别是: false, strict, 要求至少有一个静态分区列,而 nonstr…...
深入解析PID控制算法:从理论到实践的完整指南
前言 大家好,今天我们介绍一下经典控制理论中的PID控制算法,并着重讲解该算法的编码实现,为实现后续的倒立摆样例内容做准备。 众所周知,掌握了 PID ,就相当于进入了控制工程的大门,也能为更高阶的控制理论…...
linux--关于GCC、动态库静态库
gcc和g的异同 他们是不同的编译器, 在linux中,生成可执行文件不像和windows一样。 linux中是以**.out作为可执行文件**的 无论是什么系统,生成可执行文件分为4步: 预处理–>编译–>汇编–>链接。 从.c/.cpp–>.i文件…...
matlab汽车动力学半车垂向振动模型
1、内容简介 matlab141-半车垂向振动模型 可以交流、咨询、答疑 2、内容说明 略 3、仿真分析 略 4、参考论文 略...
Pygame中自定义事件处理的方法2-2
在《Pygame中自定义事件处理的方法2-1》中提到了处理自定义事件的方法。通过处理自定义事件,可以实现动画等效果。 1 弹跳小球程序 通过处理自定义事件,可以实现弹跳小球程序,如图1所示。 图1 弹跳小球程序 2 弹跳小球程序原理 实现弹跳小…...
B. Longest Divisors Interval
time limit per test 2 seconds memory limit per test 256 megabytes Given a positive integer nn, find the maximum size of an interval [l,r][l,r] of positive integers such that, for every ii in the interval (i.e., l≤i≤rl≤i≤r), nn is a multiple of ii. …...
什么是服务的雪崩、熔断、降级的解释以及Hystrix和Sentinel服务熔断器的解释、比较
1.什么是服务雪崩? 定义:在微服务中,假如一个或者多个服务出现故障,如果这时候,依赖的服务还在不断发起请求,或者重试,那么这些请求的压力会不断在下游堆积,导致下游服务的负载急剧…...
从驾驶员到智能驾驶:汽车智能化进程中的控制与仿真技术
在汽车技术持续演进的历程中,人类驾驶员始终是一个极具研究价值的智能控制系统“原型”。驾驶员通过视觉感知、行为决策与操作执行的闭环控制,将复杂的驾驶任务转化为车辆的实际动作,同时动态适应道路环境的变化。这一过程不仅体现了高度的自…...
mysql和minio
在现代应用架构中,Word 文档、PPT 等文件通常存储在对象存储服务(如 MinIO)中,而不是直接存储在关系型数据库(如 MySQL)中。以下是具体的分工和原因: 为什么选择对象存储(如 MinIO&a…...
java练习(24)
PS:练习来自力扣 合并两个有序数组 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。 注意&am…...
Android的Activity生命周期知识点总结,详情
一. Activity生命周期 1.1 返回栈知识点 二. Activity状态 2.1 启动状态 2.2 运行状态 2.3 暂停状态 2.4 停止状态 2.5 销毁状态 三. Activity生存期 3.1 回调方法 3.2 生存期 四. 体验Activity的生命周期 五. Activity被回收办法 引言: 掌握Acti…...
STM32——HAL库开发笔记19(串口中断接收实验)(参考来源:b站铁头山羊)
本实验,我们以中断的方式使得串口发送数据控制LED的闪烁速度,发送1,慢闪;发送2,速度正常;发送3,快闪。 一、电路连接图 二、实现思路&CubeMx配置 1、实现控制LED的闪烁速度 uint32_t bli…...
基于腾讯云TI-ONE 训练平台快速部署和体验 DeepSeek 系列模型
引言 在前两篇文章中,我们通过腾讯云的HAI部署了DeepSeek-R1,并基于此进行了一系列实践。 腾讯云HAI DeepSeek 腾讯云AI代码助手 :零门槛打造AI代码审计环境 基于腾讯云HAI DeepSeek 快速开发中医辅助问诊系统 这些尝试不仅帮助我们理解…...
python的类装饰器
装饰器不仅可以用于函数,还能作用于类。将装饰器应用于类时,其核心原理与作用于函数类似,都是通过接收一个类作为输入,然后返回一个新的类或者修改后的原类,以此来为类添加额外的功能 简单的类装饰器 def add_method…...
C++17中的LegacyContiguousIterator(连续迭代器)
文章目录 特点内存连续性与指针的兼容性更高的性能 适用场景与C接口交互高性能计算 支持连续迭代器的容器示例代码性能优势缓存局部性指针算术优化 注意事项总结 在C17标准里,LegacyContiguousIterator(连续迭代器)是一类特殊的迭代器。它不仅…...
Linux-文件IO
1.open函数 【1】基本概念和使用 #include <fcntl.h> int open(const char *pathname,int flags); int open(const char *pathname,int flags,mode_t mode); 功能: 打开或创建文件 参数: pathname //打开的文件名 f…...
DeepSeek-R1 + Cherry Studio 本地部署打造个人 AI 知识库
ChatGPT 爆火的时候,我心里就燃起了一个想法:打造一个专属于自己的AI知识库,它就像我的第二大脑一样,能记住我生活里的点点滴滴。 我随口一问“去年5月我做了什么”,它不仅能精准找到记录,还能帮我回忆起那…...
《红色警戒:兵临城下》 游戏软件安装步骤与百度网盘链接
软件简介: 《红色警戒:兵临城下》(Command & Conquer: Red Alert)是一款经典的即时战略游戏,由Westwood Studios开发,于1996年首次发行。它是《命令与征服》系列的衍生作品,以其独特的世界…...
25/2/16 <算法笔记> DirectPose
DirectPose 是一种直接从图像中预测物体的 6DoF(位姿:6 Degrees of Freedom)姿态 的方法,包括平移和平面旋转。它在目标检测、机器人视觉、增强现实(AR)和自动驾驶等领域中具有广泛应用。相比于传统的位姿估…...
第32周:文献阅读
目录 摘要 Abstract 文献阅读 问题引入 研究问题 研究意义 研究方法 集成方法 随机森林(RF) 支持向量机(SVM) 简单循环神经网络(SimpleRNN) 长短期记忆网络(LSTM) 创…...
Ollama 开发指南
文章来源:开发指南 - Ollama中文文档|Ollama官方文档 安装先决条件: GOC/C 编译器,例如 macOS 上的 Clang、TDM-GCC (Windows amd64) 或 llvm-mingw (Windows arm64)、Linux 上的 GCC/Clang。…...
【deepseek与chatGPT辩论】辩论题: “人工智能是否应当具备自主决策能力?”
探讨辩论题 这个提案涉及创建一个精确的辩论题目,旨在测试deepseek的应答能力。 创建辩论题目 提议设计一个辩论题目以测试deepseek的应答能力。希望这个题目具有挑战性并能够测量其回应质量。 好的,来一道适合深度学习的辩论题: 辩论题&…...
神经网络常见激活函数 9-CELU函数
文章目录 CELU函数导函数函数和导函数图像优缺点pytorch中的CELU函数tensorflow 中的CELU函数 CELU 连续可微指数线性单元:CELU(Continuously Differentiable Exponential Linear Unit),是一种连续可导的激活函数,结合了 ELU 和 …...
JavaScript系列(74)--反射API详解
JavaScript反射API详解 🔍 JavaScript的反射API提供了强大的运行时检查和操作对象的能力。本文将深入探讨Reflect API的原理、应用场景和最佳实践。 反射基础 🌟 💡 小知识:反射是指程序在运行时能够检查、修改自身结构和行为的…...
轻量级分组加密算法RECTANGLE
轻量级分组加密算法RECTANGLE RECTANGLE轻量级分组密码算法是Wentao Zhang,Zhenzhen Bao,Dongdai Lin等学者于2014年提出的,该算法是SPN结构的,采用了线性移位的置换层以及44bit的S盒。RECTANGLE是一个迭代分组密码,分组长度为64…...