28个案例问题分析---01---redis没有及时更新问题--Redis
redis没有及时更新问题
- 一:背景介绍
- 二:前期准备
- pom依赖
- 连接Redis工具类
- 连接mysql工具类
- 三:过程
- 使用redis缓存,缓存用户年龄
- 业务对应流程图
- 使用redis缓存用户年龄对应代码
- 四:总结
一:背景介绍

业务中使用redis做缓存,来减少对数据库的操作,提升速度。使用的过程是
去redis内查询数据。如果有的话直接返回redis内的数据,如果没有的话去数据库查询数据,并将其存储到redis中,那么下次的查询就是在redis内查询的,省去了我们多次查询数据库的操作。
但是我们这个业务里,redis内存的数据可能与数据库的不一致。导致这种现象的原因是。在另外的业务中更改了数据库内的数据后,没有去修改redis内的数据,于是造成了redis数据与数据库数据不一致的问题。下面我就用一个小例子,演示上述问题,以及解决方案
二:前期准备
此实例是一个普通的maven项目。使用前需要准备好mysql数据,redis。具体方式大家可以上网查询。
pom依赖
项目连接mysql和redis,需要在maven内引入以下两个依赖.
<dependencies><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.3.0</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.46</version></dependency></dependencies>
连接Redis工具类
编写一个连接Redis的工具类,这里直接使用的Jedis进行的连接。
/*** @BelongsProject: redis* @BelongsPackage: org.example.utils* @Author:hlzs1* @Description: redis的工具类* @CreateTime: 2023-03-03 20:42* @Version: 1.0*/
public class RedisUtils {public static Jedis jedis ;static {jedis = new Jedis("ip地址",6379,10000);//没有设置密码可以不填jedis.auth("000415");}/*** @description: 在redis取出数据* @author: haolizhuo* @date: 2023/3/4* @param: [key]* @return: java.lang.Object**/public static Object redisGet(String key){return RedisUtils.jedis.get(key);}/*** @description:向redis存储数据* @author: haolizhuo* @date: 2023/3/4* @param: [key, value, seconds]* @return: void**/public static void redisSet(String key, String value, @Nullable Integer seconds){RedisUtils.jedis.set(key,value);if(seconds !=null){RedisUtils.jedis.expire(key,seconds);}}
}
连接mysql工具类
连接mysql需要对应的配置文件,新建一个jdbc.properties文件将此文件放到resources目录下即可
userName=root
password=deng123~
url=jdbc:mysql://ip地址/数据库名?useSSL=false&useUnicode=true&characterEncoding=utf8
driverClass=com.mysql.cj.jdbc.Driver
连接数据库的工具类
/*** @author : [haolizhuo]* @version : [v1.0]* @className : JDBCUtils* @description : [描述说明该类的功能]* @createTime : [2022/11/30 11:24]* @updateUser : [haolizhuo]* @updateTime : [2022/11/30 11:24]* @updateRemark : [描述说明本次修改内容]*/
public class JDBCUtils {private static String url;private static String userName;private static String password;private static String driverClass;private static Connection connection;static {try {Properties properties = new Properties();InputStream inputStream = JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");properties.load(inputStream);driverClass = properties.getProperty("driverClass");url = properties.getProperty("url");userName = properties.getProperty("userName");password = properties.getProperty("password");} catch (Exception e) {e.printStackTrace();}}/** @version V1.0* Title: getConnection* @author haolizhuo* @description 获取数据库连接* @createTime 2022/11/30 11:36* @param []* @return java.sql.Connection*/public static Connection getConnection() {try {connection = DriverManager.getConnection(url, userName, password);} catch (Exception e) {e.printStackTrace();}return connection;}/** @version V1.0* Title: close* @author haolizhuo* @description 释放资源* @createTime 2022/11/30 11:39* @param [connection, statement, resultSet]* @return void*/public static void close(Connection connection, Statement statement, ResultSet resultSet) {if (connection != null) {try {connection.close();} catch (SQLException e) {e.printStackTrace();}}if (statement != null) {try {statement.close();} catch (SQLException e) {e.printStackTrace();}}if (resultSet != null) {try {resultSet.close();} catch (SQLException e) {e.printStackTrace();}}}
项目结构

到这里我们真个环境就准备好了,下面我们就复现背景里提到的,使用缓存的情况
三:过程
使用redis缓存,缓存用户年龄
这里是一个普通的使用redis的场景,我使用redis缓存了用户的年龄。key值是用户的id,value值是用户的年龄。
业务对应流程图

使用redis缓存用户年龄对应代码
public static void main(String[] args) {//使用redis缓存,缓存某个id的用户的年龄,这里以1做示例String userId = "1";Object redisUserInfo = RedisUtils.redisGet(userId);//如果缓存查到信息,直接返回,并且结束if(null != redisUserInfo){System.out.println("redis获取到了用户"+userId+"的年龄,年龄为:" + redisUserInfo);return;}//缓存里没有就去数据库进行查询String sql ="select * from user_info where id = " + userId;Map<String, Object> userInfo = getUserInfo(sql);//如果查询到了信息,更新到缓存内if(userInfo != null){System.out.println("mysql获取到了用户"+userId+"的年龄,年龄为:" + userInfo.get("age"));RedisUtils.redisSet(userInfo.get("id").toString(),userInfo.get("age").toString(),60);}}/*** @description: 数据库查询用户数据* @author: haolizhuo* @date: 2023/3/4* @param: [sql]* @return: java.util.Map<java.lang.String,java.lang.Object>**/private static Map<String, Object> getUserInfo(String sql){Connection connection;Statement statement;ResultSet resultSet;Map<String , Object> map = new HashMap<>(16);try {//创建数据库连接connection = JDBCUtils.getConnection();statement = connection.createStatement();resultSet = statement.executeQuery(sql);//将值取出while (resultSet.next()){map.put("id",resultSet.getInt("id"));map.put("name",resultSet.getString("name"));map.put("age",resultSet.getInt("age"));}} catch (SQLException e) {throw new RuntimeException(e);}return map;}
这部分代码是没有问题,通过redis做了对应缓存。如果缓存过期了之后,就去数据库内查询,并且更新到缓存中。但是问题是,假设我们有别的业务更新了我们缓存的age数据,同时redis的数据没有过期,这是后就会造成一个数据不一致的问题。
执行代码,控制台打印

查看redis内数据

查看数据库内数据

现在我们的数据是正确无误,缓存与数据库数据一致。可如果我们的业务更新这个age的话,就会产生不一致的问题
public static void main(String[] args) {String sql = "update user_info set age = 66 where id = 1";updateUserInfo(sql);}/*** @description: 数据库修改用户数据* @author: haolizhuo* @date: 2023/3/4* @param: [sql]* @return: void**/private static void updateUserInfo(String sql){Connection connection;Statement statement;int resultSet;try {//创建数据库连接connection = JDBCUtils.getConnection();statement = connection.createStatement();resultSet = statement.executeUpdate(sql);System.out.println(resultSet);} catch (SQLException e) {throw new RuntimeException(e);}}
更新完之后redis内数据

更新完之后数据库内数据

这时候,问题就暴露出来了,缓存数据与数据库数据不一致。并且缓存的数据还未过期。导致过期前查询的数据都不准确。
所以在使用缓存的时候,如果数据库内数据改了,一定要及时的
清空缓存!!!!清空缓存!!!!清空缓存!!!!
四:总结
这个问题的出现的根本原因是。不同的开发人员没有做好沟通,这两个业务是不同的人员编写的,没有考虑的业务的相关性,导致了这种问题的出现!!
所以不仅仅要实现功能也要考虑业务相关性!!!
相关文章:
28个案例问题分析---01---redis没有及时更新问题--Redis
redis没有及时更新问题一:背景介绍二:前期准备pom依赖连接Redis工具类连接mysql工具类三:过程使用redis缓存,缓存用户年龄业务对应流程图使用redis缓存用户年龄对应代码四:总结一:背景介绍 业务中使用redis…...
[1.3_3]计算机系统概述——系统调用
文章目录第一章 计算机系统概述系统调用(一)什么是系统调用,有何作用(二)系统调用与库函数的区别(三)小例子:为什么系统调用是必须的(四)什么功能要用到系统调…...
Vue基础学习 第一个Vue程序 el挂载点 v-指令(1)
Vue简介 Vue是一个Javascript框架Vue框架可以简化Dom操作响应式数据驱动 : 页面是由数据生成的,当数据出现改动,页面也会即时改变 第一个Vue程序 Vue中文文档官网:https://v2.cn.vuejs.org/v2/guide/ 根据官方文档的说法&#…...
前端页面性能
提升页面性能的方法资源压缩合并,减少HTTP请求非核心代码异步加载异步加载方式?1)动态脚本加载、2)defer、3)async(在加载js的时候在script标签上添加这两个属性,<script src"./test.js" charset"utf-8" defer><…...
2023-03-04 反思
摘要: 当前的时期确实比较特殊,不但是对于一个生命周期的最后的挣扎,更是在经历了各种浮浮沉沉的波澜之后还有更多的波浪。 精神分析-GRY: 非常奇怪的一个跳梁小丑, 不过我个人认为用这个标签是对跳梁小丑的侮辱和上层管理者对于这种人的纵容有很大关系…...
奇思妙想:超链接唤起本地应用
文章目录分析实现参考很多人的博客都有这样的小玩意,点击之后就可以直接与博主进行对话,而且无需添加好友。 先研究一下网页源代码: <a href"tencent://message/?uin88888888&Siteqq&Menuyes">联系我</a>很明…...
初识数据结构——“数据结构与算法”
各位CSDN的uu们你们好呀,今天小雅兰进入一个全新的内容的学习,就是算法和数据结构啦,话不多说,让我们进入数据结构的世界吧 什么是数据结构? 什么是算法? 数据结构和算法的重要性 如何学好数据结构和算…...
华为OD机试Golang解题 - 计算网络信号
华为Od必看系列 华为OD机试 全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典文章目录 华为Od必看系列使用说明本期题目…...
ESP32编译及运行错误记录
1、打印格式不对 一般都是因为日志中某个参数打印格式不匹配造成。 ESP_LOGI(TAG, "[APP] Free memory: %lu bytes", esp_get_free_heap_size());//将之前的%d 改为%lu 2、配置载不对 这里选择了蓝牙模块需要引入蓝牙组件才能编译通过 idf.py menuconfig Component…...
GEE开发之降雨(CHIRPS)数据获取和分析
GEE开发之降雨CHIRPS数据获取和分析1.数据介绍2.初识CHIRPS2.1 代码一2.2 代码二3.逐日数据分析和获取4.逐月数据分析和获取4.1 代码一4.2 代码二(简洁)5.逐年数据分析和获取5.1 代码一5.2 代码二(简洁)前言:主要获取和分析UCSB-CHG/CHIRPS/DAILY的日数据、月数据和…...
TypeScript中面向对象
面向对象 要想面向对象,操作对象,首先便要拥有对象; 要创建对象,必须要先定义类,所谓的类可以理解为对象的模型; 程序中可以根据类创建指定类型的对象; 举例来说: 可以通过Perso…...
Transformer 模型:入门详解(1)
动动发财的小手,点个赞吧! 简介 众所周知,transformer 架构是自然语言处理 (NLP) 领域的一项突破。它克服了 seq-to-seq 模型(如 RNN 等)无法捕获文本中的长期依赖性的局限性。事实证明,transformer 架构是…...
深入理解js中的new关键字
在js中我们经常会使用到new关键字,那我们在使用new关键字的时候,new到底做了什么呢?今天我们就来深入探究一下 1.初步使用 我们先来使用一下,这是一个正常操作 function Person() {this.name "John";}let person new…...
RT-Thread Nano(2) - 线程
参考:RT-Thread API参考手册: 线程管理 线程的分类:动态线程,静态线程 动态线程是系统自动从动态内存堆上分配栈空间的线程句柄(程序运行时再分配空间),静态线程是由用户分配栈空间与线程句柄(可以说是程序编译时已经分配好空间) 1.创建线程 创建一个动态线程 rt_thread_t …...
真香,Grafana开源Loki日志系统取代ELK?
一、Loki是什么? Loki是由Grafana Labs开源的一个水平可扩展、高可用性,多租户的日志聚合系统的日志聚合系统。它的设计初衷是为了解决在大规模分布式系统中,处理海量日志的问题。Loki采用了分布式的架构,并且与Prometheus、Graf…...
机器学习|多变量线性回归 | 吴恩达学习笔记
前文回顾:机器学习 | 线性回归(单变量) 目录 📚多维特征 📚多变量梯度下降 📚梯度下降法实践 🐇特征缩放 🐇学习率 📚特征和多项式回归 📚正规方程 &…...
高并发内存池
按照threadcache,centralcache,pagecache顺序所列 这里还需要一定的前期准备工作 首先是可以设计一个定长内存池 ObjectPool.h #pragma once #include<iostream> #include"Common.h" using std::cout; using std::endl; using std::…...
springboot mybatis-plus 对接 sqlserver 数据库 批处理的问题
问题: 在对接 sqlserver数据库的时候 主子表 保存的时候 子表批量保存 使用的 mybatis-plus提供的saveOrUpdateBatch 这个方法 但是 报错 报错内容为 : com.microsoft.sqlserver.jdbc.SQLServerException: 必须执行该语句才能获得结果。 框架版本 sprin…...
Acwing---843. n-皇后问题——DFS
n-皇后问题1.题目2.基本思想3.代码实现1.题目 n−皇后问题是指将 n 个皇后放在 nn 的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。 现在给定整数 n,请你输出所有的满足条件的棋子摆法。 …...
Android事件分发机制
文章目录Android View事件分发机制:事件分发中的核心方法onTouchListener和onClickListener的优先级事件分发DOWN,MOVE,UP 事件分发CANCEL代码实践requestdisallowIntereptTouchEvent作用Android View事件分发机制: 事件分发中的核心方法 Android中事件…...
Phi-3-mini-4k-instruct-gguf应用落地:教育场景中的作业辅导与知识点提炼
Phi-3-mini-4k-instruct-gguf应用落地:教育场景中的作业辅导与知识点提炼 1. 教育场景中的AI助手需求 想象一下这样的场景:晚上10点,孩子还在为数学作业发愁,家长已经精疲力尽;老师批改着第50份作文,眼睛…...
intv_ai_mk11效果惊艳案例:为初创公司1小时生成完整BP商业计划书框架
intv_ai_mk11效果惊艳案例:为初创公司1小时生成完整BP商业计划书框架 1. 商业计划书生成效果展示 1.1 从零到完整的商业计划书 intv_ai_mk11在商业计划书生成方面展现出惊人的效率和质量。我们实测了一个真实案例:一家智能硬件初创公司需要准备融资用…...
深入解析C++中获取进程模块基址的高效实现方法
1. 为什么需要获取进程模块基址 在Windows系统编程中,获取进程模块基址是一个基础但极其重要的操作。简单来说,模块基址就是某个DLL或EXE文件被加载到内存中的起始地址。这个地址就像是模块在内存中的"门牌号",有了它我们才能找到模…...
nli-distilroberta-base参数解析与调优指南:关键配置项详解
nli-distilroberta-base参数解析与调优指南:关键配置项详解 1. 引言 如果你正在使用nli-distilroberta-base模型进行自然语言推理任务,可能会遇到这样的困惑:为什么同样的模型在不同机器上运行速度差异这么大?为什么有时候推理结…...
Godot-MCP:如何通过双向语义桥梁解决游戏开发中的创意断层问题
Godot-MCP:如何通过双向语义桥梁解决游戏开发中的创意断层问题 【免费下载链接】Godot-MCP An MCP for Godot that lets you create and edit games in the Godot game engine with tools like Claude 项目地址: https://gitcode.com/gh_mirrors/god/Godot-MCP …...
Qwerty Learner字体优化:提升阅读体验的细节处理
Qwerty Learner字体优化:提升阅读体验的细节处理 【免费下载链接】qwerty-learner 为键盘工作者设计的单词记忆与英语肌肉记忆锻炼软件 / Words learning and English muscle memory training software designed for keyboard workers 项目地址: https://gitcode.…...
Pixel Couplet Gen应用场景:微信小程序开发者如何复用像素皇城UI组件
Pixel Couplet Gen应用场景:微信小程序开发者如何复用像素皇城UI组件 1. 项目背景与价值 Pixel Couplet Gen是一款融合传统春节文化与现代像素艺术风格的创新应用。作为微信小程序开发者,您可以直接复用其UI组件库,快速构建具有以下特点的应…...
Windows 11 + CUDA 11.7 环境下,TensorRT 8.5.2.2 保姆级安装与配置避坑指南
Windows 11 CUDA 11.7 环境下 TensorRT 8.5.2.2 终极安装指南 刚接触深度学习推理优化的开发者,往往会在环境配置阶段耗费大量时间。TensorRT作为NVIDIA官方推出的高性能推理库,能够显著提升模型在NVIDIA GPU上的运行效率。本文将手把手带你完成Windows…...
霜儿-汉服-造相Z-Turbo模型推理优化:理解与避免神经网络中的耦合过度
霜儿-汉服-造相Z-Turbo模型推理优化:理解与避免神经网络中的耦合过度 不知道你有没有遇到过这种情况:想让AI画一个穿汉服的女孩,结果出来的图,发型和衣服总是一起“跑偏”。比如,你想生成一个“唐代齐胸襦裙”的造型&…...
Qwen3-1.7B推理模式切换体验:思考模式与非思考模式效果对比
Qwen3-1.7B推理模式切换体验:思考模式与非思考模式效果对比 1. 引言:双模式推理的创新价值 在边缘计算和轻量化AI模型快速发展的今天,Qwen3-1.7B通过独特的动态双模式架构,为用户提供了灵活的推理选择。这款17亿参数的轻量级大语…...
