28个案例问题分析---01---redis没有及时更新问题--Redis

news2025/1/16 4:49:01

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内数据
在这里插入图片描述
更新完之后数据库内数据
在这里插入图片描述
这时候,问题就暴露出来了,缓存数据与数据库数据不一致。并且缓存的数据还未过期。导致过期前查询的数据都不准确。
所以在使用缓存的时候,如果数据库内数据改了,一定要及时的
清空缓存!!!!清空缓存!!!!清空缓存!!!!

四:总结

这个问题的出现的根本原因是。不同的开发人员没有做好沟通,这两个业务是不同的人员编写的,没有考虑的业务的相关性,导致了这种问题的出现!!
所以不仅仅要实现功能也要考虑业务相关性!!!

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

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

相关文章

【机器学习面试】百面机器学习笔记和问题总结+扩展面试题

第1章 特征工程 1、为什么需要对数值类型的特征做归一化&#xff1f; &#xff08;1&#xff09;消除量纲&#xff0c;将所有特征统一到一个大致相同的区间范围&#xff0c;使不同指标之间具由可比性&#xff1b; &#xff08;2&#xff09;可以加快梯度下降收敛的速度&#…

powershell-dns-txt远程加载

2022-10-30 参考原文&#xff1a; 远程下载的通用替代方案 &#xff5c; 红队攻防 https://mp.weixin.qq.com/s/9MAKZZfNB5YFT7jgln5lXQ实现过程 dns环境&#xff1a;kali bind9&#xff08;docker版&#xff09;&#xff0c;ip&#xff1a;192.168.161.128 靶机&#xff…

AD封装转Allego Cadence

AD封装转Allego CadenceAD封装转Allego Cadence软件版本转换步骤导出AD文件在PADS导入AD在cadence导入PADS在cadence导出library修改焊盘替换焊盘AD封装转Allego Cadence 有时候在网上下载的封装是AD格式的&#xff0c;但内容实在太多&#xff0c;为了快速便捷获得cadence格式…

梯度提升算法决策过程的逐步可视化

梯度提升算法是最常用的集成机器学习技术之一&#xff0c;该模型使用弱决策树序列来构建强学习器。这也是XGBoost和LightGBM模型的理论基础&#xff0c;所以在这篇文章中&#xff0c;我们将从头开始构建一个梯度增强模型并将其可视化。 梯度提升算法介绍 梯度提升算法&#x…

【VC 7/8】vCenter Server 基于文件的备份和还原Ⅰ——基于文件的备份和还原的注意事项和限制

目录1.1 协议1.2 还原后配置说明1.3 Storage DRS1.4 分布式电源管理1.5 分布式虚拟交换机1.6 内容库1.7 虚拟机生命周期操作1.8 vSphere High Availability1.9 基于存储策略的管理1.10 其它注意事项虚拟存储区域网络修补关联博文[图片来源]&#xff1a;https://www.vmignite.co…

ARM uboot 源码分析9 - uboot的硬件驱动部分

一、uboot 与 linux 驱动 1、uboot 本身是裸机程序 (1) 裸机本来是没有驱动的概念的&#xff08;狭义的驱动的概念就是&#xff0c;操作系统中用来具体操控硬件的那部分代码叫驱动&#xff09; (2) 裸机程序中是直接操控硬件的&#xff0c;操作系统中必须通过驱动来操控硬件…

Java 8 新特性之Stream流(二)关键

继续探索流API的高级功能之前&#xff0c;我们先从接口级别全面了解一下流API&#xff0c;这个对于我们来说是至关重要的。下面是一张流API关键知识点的UML图。 流API UML 流API定义的几个接口&#xff0c;都是在java.util.stream包中的.其中上图中的BaseStream接口是最基础的…

每日记录自己的Android项目(二)—Viewbinding,WebView,Navigation

今日想法今天是想把做一个跳转页面的时候调到H5页面去&#xff0c;但是这个页面我用app来承载&#xff0c;不要调到浏览器去。所以用到了下方三个东西。Viewbindingbuild.gradle配置首先在app模块的build.gradle里添加一下代码默认情况下&#xff0c;每一个布局xml文件都会生成…

【Linux学习】基础IO——理解缓冲区 | 理解文件系统

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《Linux学习》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 基础IO☕理解缓冲区&#x1f9c3;缓冲区的共识&#x1f9c3;缓冲区的位置&#x1f9c3;缓冲区的刷…

Spring Boot+Vue前后端分离项目练习03之网盘项目文件夹创建及文件查询接口开发

1.集成Swagger 3接口文档 在前后端分离的项目中&#xff0c;接口文档的存在十分重要。swagger 是一个自动生成接口文档的工具&#xff0c;在需求变更十分频繁的情况下&#xff0c;手写接口文档是效率十分低下&#xff0c;这时swagger自动生生文档的的作用就体现出来了&#xf…

【uni-app教程】UniAPP 常用组件和 常用 API 简介# 知心姐姐聊天案例

五、UniAPP 常用组件简介 uni-app 为开发者提供了一系列基础组件&#xff0c;类似 HTML 里的基础标签元素&#xff0c;但 uni-app 的组件与 HTML 不同&#xff0c;而是与小程序相同&#xff0c;更适合手机端使用。 虽然不推荐使用 HTML 标签&#xff0c;但实际上如果开发者写了…

华为机试题:HJ105 记负均正II(python)

文章目录&#xff08;1&#xff09;题目描述&#xff08;2&#xff09;Python3实现&#xff08;3&#xff09;知识点详解1、input()&#xff1a;获取控制台&#xff08;任意形式&#xff09;的输入。输出均为字符串类型。1.1、input() 与 list(input()) 的区别、及其相互转换方…

【Kubernetes】第十七篇 - ECS 服务停机和环境修复

一&#xff0c;前言 上一篇&#xff0c;介绍了 Secret 镜像的使用&#xff1b; 三台服务每天大概 15 块钱的支出&#xff0c;用一个月也是不少钱&#xff1b; 闲时可以停掉&#xff0c;这样每天只有 4 块钱支出&#xff0c;剩下一大笔&#xff1b; ECS 服务停机后公网 IP 会…

移除元素(每日一题)

目录 一、题目描述 二、题目分析 2.1 方法一 2.1.1 思路 2.1.2 代码 2.2 方法二 2.2.1 思路 2.2.2 代码 一、题目描述 题目链接&#xff1a;27. 移除元素 - 力扣&#xff08;LeetCode&#xff09; 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数…

【Maven】P1 Maven 基础知识

Maven 基础知识Maven基础仓库坐标快速坐标生成网站国内镜像仓库前言 本节&#xff1a;Maven第一节内容&#xff0c;记录maven是什么&#xff0c;解决了什么问题&#xff0c;进而推出他的作用&#xff1b;然后介绍maven中两个重要概念&#xff0c;仓库与坐标。 下一节&#xff1…

TIA博途中使用SCL语言实现选择排序算法并封装成FC全局库

TIA博途中使用SCL语言实现选择排序算法并封装成FC全局库 选择排序算法包括升序和降序2种: 升序排列: 第一轮从数据源中找到最小值排在第一位,第二轮从剩下的数据中寻找最小值排在第二位,依次类推,直到所有数据完成遍历;降序排列: 第一轮从数据源中找到最大值排在第一位,…

centOS 编译strongswan

安装编译环境 yum groupinstall "Development Tools" 编译strongswan Download strongSwan: wget https://download.strongswan.org/strongswan-x.x.x.tar.bz2 Unpack the tarball and navigate into the directory: tar xjf strongswan-x.x.x.tar.bz2; cd strong…

Editor.md 的使用方法及图片处理

目录1. 资源下载2. 生成页面2.1 编辑和预览页面2.2 文本渲染页面3. 图片上传3.1 前端配置3.2 后端接口4. 图片粘贴1. 资源下载 官网下载 gitee 下载 2. 生成页面 2.1 编辑和预览页面 将资源&#xff08;精简后 Editor.md 资源1&#xff09;导入项目&#xff1a; 按照官方教…

nvidia Jetson nano Linux内核编译

今天编译了nvidia 的jetson nano的内核。在网上找到的资料都比较老了。现在官网的最新版本是35.1.结合之前看到的博客的内容。关键是内核源码和交叉编译器的下载。找到官方文档后,编译成功!并且官方的文档是有一个编译脚本的。看之前的资料都是给出的命令,不知道这个nvbuild…

库函数qsort的使用以及模拟实现

首先&#xff0c;qsort函数是个库函数 那么就有头文件 #include<stdlib.h>这个函数的实现是利用快速排序的方法实现的 下面是该函数的参数//void qsort(void* base, //指向了待排序数组的第一个元素 // size_t num, //待排序的元素个数 // size_t …