问题: redis-高并发场景下如何保证缓存数据与数据库的最终一致性

在高并发场景下,Redis 通常用作缓存层,与数据库结合使用以提高系统的性能。为了保证缓存数据与数据库的最终一致性,通常采用的有双写机制、缓存失效机制,基于双写机制、缓存失效机制又衍生出来了消息队列、事件驱动架构等

常见机制

常见的机制如下,个人理解无非是先后或各种手段操作数据库、redis,代码ai给写的示列只需看懂即可。

  1. 双写机制
    在更新数据库的同时,同步更新缓存。
    适用于写操作较少的场景
 public class CacheService {private final JdbcTemplate jdbcTemplate;private final RedisTemplate<String, Object> redisTemplate;public void updateData(String key, String value) {// 更新数据库jdbcTemplate.update("UPDATE table SET value = ? WHERE key = ?", value, key);// 更新缓存redisTemplate.opsForValue().set(key, value);}
  1. 缓存失效机制
    在更新数据库后,删除缓存中的旧数据,读取数据时候时写入缓存
    适用于写操作频繁的场景。
 public class CacheService {private final JdbcTemplate jdbcTemplate;private final RedisTemplate<String, Object> redisTemplate;public void updateData(String key, String value) {// 更新数据库jdbcTemplate.update("UPDATE table SET value = ? WHERE key = ?", value, key);// 删除缓存redisTemplate.delete(key);}public String getData(String key) {// 从缓存中获取数据String value = (String) redisTemplate.opsForValue().get(key);if (value == null) {// 缓存未命中,从数据库中获取数据value = jdbcTemplate.queryForObject("SELECT value FROM table WHERE key = ?", new Object[]{key}, String.class);if (value != null) {// 将数据写入缓存redisTemplate.opsForValue().set(key, value);}}return value;}}
  1. 消息队列机制
    使用消息队列异步更新redis,确保数据的一致性。
    适用于高并发写操作的场景。
  import com.rabbitmq.client.Channel;import com.rabbitmq.client.Connection;import com.rabbitmq.client.ConnectionFactory;public class CacheService {private final JdbcTemplate jdbcTemplate;private final RedisTemplate<String, Object> redisTemplate;public void updateData(String key, String value) {// 更新数据库jdbcTemplate.update("UPDATE table SET value = ? WHERE key = ?", value, key);// 发送消息到消息队列sendUpdateMessage(key, value);}private void sendUpdateMessage(String key, String value) {ConnectionFactory factory = new ConnectionFactory();factory.setHost("localhost");try (Connection connection = factory.newConnection();Channel channel = connection.createChannel()) {channel.queueDeclare("cache_update_queue", true, false, false, null);channel.basicPublish("", "cache_update_queue", null, (key + ":" + value).getBytes());} catch (Exception e) {e.printStackTrace();}}public void consumeUpdateMessages() {ConnectionFactory factory = new ConnectionFactory();factory.setHost("localhost");try (Connection connection = factory.newConnection();Channel channel = connection.createChannel()) {channel.queueDeclare("cache_update_queue", true, false, false, null);DeliverCallback deliverCallback = (consumerTag, delivery) -> {String message = new String(delivery.getBody(), "UTF-8");String[] parts = message.split(":");String key = parts[0];String value = parts[1];// 更新缓存redisTemplate.opsForValue().set(key, value);};channel.basicConsume("cache_update_queue", true, deliverCallback, consumerTag -> {});} catch (Exception e) {e.printStackTrace();}}}
  1. 事件驱动机制
    使用事件驱动架构,当数据库数据发生变化时,触发事件,事件处理器负责更新缓存。
    适用于复杂的数据更新逻辑。
   import org.springframework.context.ApplicationEventPublisher;import org.springframework.context.ApplicationEventPublisherAware;import org.springframework.stereotype.Service;@Servicepublic class CacheService implements ApplicationEventPublisherAware {private final JdbcTemplate jdbcTemplate;private final RedisTemplate<String, Object> redisTemplate;private ApplicationEventPublisher eventPublisher;public void updateData(String key, String value) {// 更新数据库jdbcTemplate.update("UPDATE table SET value = ? WHERE key = ?", value, key);// 发布事件eventPublisher.publishEvent(new DataUpdatedEvent(this, key, value));}@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.eventPublisher = applicationEventPublisher;}@Servicepublic class EventListener {private final RedisTemplate<String, Object> redisTemplate;@org.springframework.context.event.EventListenerpublic void handleDataUpdatedEvent(DataUpdatedEvent event) {// 更新缓存redisTemplate.opsForValue().set(event.getKey(), event.getValue());}}}public class DataUpdatedEvent extends ApplicationEvent {private final String key;private final String value;public DataUpdatedEvent(Object source, String key, String value) {super(source);this.key = key;this.value = value;}public String getKey() {return key;}public String getValue() {return value;}}
  1. 定期补偿机制
    定期对缓存和数据库的数据进行校验,发现不一致时进行补偿操作。
    适用于对数据一致性要求较高的场景。
 import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;public class DataConsistencyChecker {private final JdbcTemplate jdbcTemplate;private final RedisTemplate<String, Object> redisTemplate;private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);public void startChecking() {scheduler.scheduleAtFixedRate(() -> {// 从数据库中获取所有数据List<Map<String, Object>> dataFromDB = jdbcTemplate.queryForList("SELECT key, value FROM table");for (Map<String, Object> row : dataFromDB) {String key = (String) row.get("key");String value = (String) row.get("value");// 从缓存中获取数据String cacheValue = (String) redisTemplate.opsForValue().get(key);if (!value.equals(cacheValue)) {// 数据不一致,更新缓存redisTemplate.opsForValue().set(key, value);}}}, 0, 1, TimeUnit.HOURS);}}

废弃缓存与更新缓存的取舍

由上面代码可看出 1和2 最大的区别在于更新数据库时到底是更新缓存还是删除缓存。
【废弃缓存】
优点:
操作简单,只需在更新数据库后删除缓存,下次读取时重新从数据库加载数据,减少了写的操作日数
缺点:
可能短暂不一致:在缓存删除后和新数据写入缓存前,可能会出现短暂的缓存不一致

【更新缓存】
优点:
数据强一致性:更新数据库和缓存同时进行,确保数据的一致性。
减少数据库读压力:缓存始终是最新的,减少了对数据库的读操作。
缺点:
复杂性增加:需要处理缓存更新失败的情况,可能需要回滚操作。
性能影响:每次更新操作都需要同时更新数据库和缓存,增加了操作的复杂性和时间

  • 写操作较少的场景:
    推荐使用更新缓存:因为写操作较少,更新缓存的额外开销相对较小,且可以确保数据的一致性。
  • 写操作频繁的场景:
    推荐使用废弃缓存:因为写操作频繁,更新缓存会增加系统的复杂性和开销,而废弃缓存可以减少缓存的写操作,降低系统负担。
  • 对数据一致性要求极高的场景:
    推荐使用更新缓存:尽管复杂性增加,但可以确保数据的强一致性。
  • 对性能要求较高且可以容忍短暂不一致的场景:
    推荐使用废弃缓存:可以减少数据库的读压力,提高系统的整体性能

淘汰缓存的顺序

https://blog.csdn.net/qq_39033181/article/details/119276120

【 方案一 】先淘汰缓存,再更新数据库
在并发量较大的情况下,会导致数据的不一致。
  1. A线程进行写操作,先成功淘汰缓存,但由于网络或其它原因,还未更新数据库
  2. B线程进行读操作,发现缓存中没有想要的数据,从数据库中读取到的是旧数据,并把旧数据放入缓存。此时数据库与缓存都是旧值,数据没有不一致
  3. A线程将数据库更新完成,数据库中是更新后的新数据,缓存中是更新前的旧数据,造成数据不一致。

【 方案二 】先更新数据库,再淘汰缓存
在并发量较大的情况下,会导致数据的短暂不一致,但是数据会最终一致。
  1. A线程进行写操作,更新数据库,还未淘汰缓存
  2. B线程从缓存中可以读取到旧数据,此时数据不一致
  3. A线程完成淘汰缓存操作,其它线程进行读操作,从数据库中读入最新数据,此时数据一致

延时双删

上述方案二更简单,在高并发场景下也能保证数据的最终一致性,但是如果我就想用方案一呢?

什么是延时双删

先删再更新数据库 过N秒后再删一次缓存,怎么实现放后面spring-cache集成里,大概有 1.延时队列、2.线程池实现延时任务。

小结

  • 这些都是理论,真正写代码,有cache框架,哪有这么烦,很多人喜欢问,那我们就得理,理了总比不理好,写这个就是怕我自己忘,呵
  • 无论怎么样在高并发场景下,我们也只能要求缓存数据与数据库的最终一致性,如果要求强一致性还要缓存干嘛呢?操作直接走DB更香
  • 大多数情况下建议使用淘汰缓存机制,然后先更新数据库,再淘汰缓存,满足大多数的场景了

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

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

相关文章

Matlab 答题卡方案

在现代教育事业的飞速发展中&#xff0c;考试已经成为现代教育事业中最公平的方式方法&#xff0c;而且也是衡量教与学的唯一方法。通过考试成绩的好与坏&#xff0c;老师和家长可以分析出学生掌握的知识多少和学习情况。从而老师可以了解到自己教学中的不足来改进教学的方式方…

丹摩|丹摩助力selenium实现大麦网抢票

丹摩&#xff5c;丹摩助力selenium实现大麦网抢票 声明&#xff1a;非广告&#xff0c;为用户体验 1.引言 在人工智能飞速发展的今天&#xff0c;丹摩智算平台&#xff08;DAMODEL&#xff09;以其卓越的AI算力服务脱颖而出&#xff0c;为开发者提供了一个简化AI开发流程的强…

【生成数据集EXCEL文件】使用生成对抗网络GAN生成数据集:输出生成数据集EXCEL

本文采用MATLAB编程&#xff0c;使用生成对抗网络GAN生成数据集&#xff1a;输出生成数据集EXCEL格式文件&#xff0c;方便大家使用。 实际工程应用中&#xff0c;由于经济成本和人力成本的限制&#xff0c;获取大量典型的有标签的数据变得极具挑战&#xff0c;造成了训练样本…

cocos creator 3.8 一些简单的操作技巧,材质的创建 1

这是一个飞机的3D模型与贴图 导入到cocos中&#xff0c;法线模型文件中已经包含了mesh、material、prefab&#xff0c;也就是模型、材质与预制。界面上创建一个空节点Plane&#xff0c;将模型直接拖入到Plane下。新建材质如图下 Effect属性选择builtin-unlit&#xff0c;不需…

手机领夹麦克风哪个牌子好,哪种领夹麦性价比高,热门麦克风推荐

​在如今这个科技飞速发展的时代&#xff0c;麦克风的选择成了很多人关心的问题&#xff0c;特别是无线麦克风该怎么选呢&#xff1f;向我咨询麦克风选购事宜的人可不在少数。要是你只是想简单自娱自乐一下&#xff0c;其实真没必要大费周章&#xff0c;直接用手机自带的麦克风…

【功能实现】bilibili顶部鼠标跟随效果怎么实现?

我们在电脑端打开b站首页时&#xff0c;总会被顶部【鼠标跟随】的效果所吸引&#xff0c;那他是如何实现的&#xff0c;来研究一下。 b站效果&#xff1a; 分析&#xff1a; 1.监听鼠标的位置&#xff0c;当悬浮到该模块时&#xff0c;图片会随鼠标移动 2.引入图片的样式是动…

WebStorm 安装配置(详细教程)

文章目录 一、简介二、优势三、下载四、安装4.1 开始安装4.2 选择安装路径4.3 安装选项4.4 选择开始菜单文件夹4.5 安装完成 五、常用插件5.1 括号插件&#xff08;Rainbow Brackets&#xff09;5.2 翻译插件&#xff08;Translation&#xff09;5.3 代码缩略图&#xff08;Cod…

[C++]:C++11(三)

1. 可变参数模版 1.1 概念 可变参数模板允许我们定义能接受可变数目模板参数的模板。简单来说&#xff0c;就好比一个函数可以接受任意个数的实际参数一样&#xff0c;可变参数模板能应对不同数量的模板参数情况。比如&#xff0c;我们可以有一个模板类或者模板函数&#xff…

【Nginx从入门到精通】05-安装部署-虚拟机不能上网简单排错

文章目录 总结1、排查步骤 一、排查&#xff1a;Vmware网关二、排查&#xff1a;ipStage 1 &#xff1a;ping 127.0.0.1Stage 2 &#xff1a;ping 宿主机ipStage 3 &#xff1a;ping 网关 失败原因解决方案Stage 4 &#xff1a;ping qq.com 总结 1、排查步骤 Vmware中网关是否…

InstantStyle容器构建指南

一、介绍 InstantStyle 是一个由小红书的 InstantX 团队开发并推出的图像风格迁移框架&#xff0c;它专注于解决图像生成中的风格化问题&#xff0c;旨在生成与参考图像风格一致的图像。以下是关于 InstantStyle 的详细介绍&#xff1a; 1.技术特点 风格与内容的有效分离 &a…

卷积神经网络各层介绍

目录 1 卷积层 2 BN层 3 激活层 3.1 ReLU&#xff08;Rectified Linear Unit&#xff09; 3.2 sigmoid 3.3 tanh&#xff08;双曲正切&#xff09; 3.4 Softmax 4 池化层 5 全连接层 6 模型例子 1 卷积层 卷积是使用一个卷积核&#xff08;滤波器&#xff09;对矩阵进…

Elastic 和 Red Hat:加速公共部门 AI 和机器学习计划

作者&#xff1a;来自 Elastic Michael Smith 随着公共部门组织适应数据的指数级增长&#xff0c;迫切需要强大、适应性强的解决方案来管理和处理大型复杂数据集。人工智能 (Artificial intelligence - AI) 和机器学习 (machine learning - ML) 已成为政府机构将数据转化为可操…

SAP B1 登陆报错解决方案 - 系统架构目录服务器选择

背景 登录时出现如下报错&#xff0c;报错显示为【系统架构目录服务器选择】 强行登录会发现过往账套都不见了 出现原因 出于各种原因在开机时没有把 SAP 所有的服务成功启动&#xff08;上一次启动科学上网后全局代理没关干净之类的&#xff09;。 解决方案 关机几分钟重启…

基于深度卷积神经网络(CNN)模型的图像着色研究与应用系统实现

1.摘要 许多历史照片都是黑白的&#xff0c;通过颜色化可以恢复这些照片的历史感和真实感&#xff0c;使人们更好地理解和感受历史事件。随着深度学习技术的发展&#xff0c;特别是卷积神经网络和自监督学习的兴起&#xff0c;研究人员提出了新的方法来解决这些问题。通过将颜色…

【CVE-2024-9413】SCP-Firmware漏洞:安全通告

安全之安全(security)博客目录导读 目录 一、概述 二、修订历史 三、CVE根因分析 四、问题修复解决 一、概述 在SCP固件中发现了一个漏洞&#xff0c;如果利用该漏洞&#xff0c;可能会允许应用处理器&#xff08;AP&#xff09;在系统控制处理器&#xff08;SCP&#xf…

Oracle 19C 安装RAC磁盘投票失败

ORACLE 19C 安装RAC第二个节点报错&#xff0c;没有找到足够的 voting 文件&#xff08;投票磁盘&#xff09; 1、磁盘投票失败分析 1.1、02节点报错日志 CRS-4123: Starting Oracle High Availability Services-managed resources CRS-2672: Attempting to start ora.mdnsd…

【Maven】IDEA创建Maven项目 Maven配置

文章目录 简介配置环境变量配置仓库测试安装 IDEA创建项目pom.xml 简介 Maven 是一个非常流行的项目管理和构建自动化工具&#xff0c;主要应用于 Java 项目的构建、依赖管理和项目信息管理。它是由 Apache 软件基金会维护的开源项目。Maven 的设计理念是通过一个项目对象模型…

vue3:使用插件递归组件

vue3:使用插件递归组件 首先安装插件 npm i unplugin-vue-define-optionsvite.config.ts 配置插件 // vite.config.ts// 引入 unplugin-vue-define-options import DefineOptions from "unplugin-vue-define-options"; export default defineConfig({// 注册插件 De…

开源TTS语音克隆神器GPT-SoVITS_V2版本地整合包部署与远程使用生成音频

文章目录 前言1.GPT-SoVITS V2下载2.本地运行GPT-SoVITS V23.简单使用演示4.安装内网穿透工具4.1 创建远程连接公网地址 5. 固定远程访问公网地址 前言 本文主要介绍如何在Windows系统电脑使用整合包一键部署开源TTS语音克隆神器GPT-SoVITS&#xff0c;并结合cpolar内网穿透工…

Keil+VSCode优化开发体验

目录 一、引言 二、详细步骤 1、编译器准备 2、安装相应插件 2.1 安装C/C插件 2.2 安装Keil相关插件 3、添加keil环境变量 4、加载keil工程文件 5、VSCode中成功添加工程文件后可能出现的问题 5.1 编码不一致问题 6、在VSCode中进行编译工程以及烧录程序 7、效果展示…