Redis数据库测试和缓存穿透、雪崩、击穿

Redis数据库测试实验

实验要求

1.新建一张user表,在表内插入10000条数据。
2.①通过jdbc查询这10000条数据,记录查询时间。
  ②通过redis查询这10000条数据,记录查询时间。
3.①再次查询这一万条数据,要求根据年龄进行排序,mysql和redis各实现一次。
4.上面排序后的前5人可进行抽奖,每人有一次抽奖机会,抽奖奖品随意设计,抽奖方式通过redis实现。

1.基本准备

先下载好jar包

在根目录下,新建lib文件夹,并将两个jar包移动到lib文件夹中

在IDEA中,右键点击lib,选择“添加为库”

两个jar包显示可展开即为成功。

2.mysql建立用户表user

CREATE TABLE `user` (
  `id` int primary key AUTO_INCREMENT,
  `name` varchar(10) COMMENT '姓名',
  `age` int COMMENT '年龄'
) ;

3.为mysql和redis添加数据

(1)获取数据库连接,并为mysql添加数据

    //获取数据库连接
    public Connection getConnection() {
        System.out.println("获取数据库连接");
        String url = "jdbc:mysql://localhost:3306/homework";
        String username = "root";
        String password = "123456";
        Connection conn = null;

        try {
            conn = DriverManager.getConnection(url, username, password);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }

    //mysql添加数据
    public void addMysql() {
        System.out.println("mysql添加数据");
        Connection conn = null;
        PreparedStatement ps = null;
        conn = getConnection();

        try {
            Random random = new Random();

            for (int i = 0; i < 10000; i++) {
                String name = "Name" + i;
                int age = random.nextInt(100) + 1;

                ps = conn.prepareStatement("INSERT INTO user (name,age) VALUES (?,?)");
                ps.setString(1, name);
                ps.setInt(2, age);
                ps.executeUpdate();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                ps.close();
                conn.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }

(2)将Mysql数据转储到redis中

    // 将Mysql数据库数据转储到Redis
    public void addRedis() {
        System.out.println("redis添加数据");
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        conn = getConnection();
        try {
            ps = conn.prepareStatement("select * from user");
            rs = ps.executeQuery();

            Jedis jedis = new Jedis("localhost", 6379);

            while (rs.next()) {
                String id = String.valueOf(rs.getInt("id"));
                String name = rs.getString("name");
                int age = rs.getInt("age");

                // 使用有序集合存储学生ID和年龄,以便进行排序
                jedis.zadd("UserByAge", age, id);

                // 存储学生数据
                jedis.hset("user:" + id, "name", name);
                jedis.hset("user:" + id, "age", String.valueOf(age));
            }

            jedis.close();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                rs.close();
                ps.close();
                conn.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }

4.实现mysql和redis查询,并比较查询时间

(1)mysql查询

    //mysql查询
    public void queryDataWithJDBC() {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        conn = getConnection();
        try {
            ps = conn.prepareStatement("select * from user");
            rs = ps.executeQuery();
            while (rs.next()) {
//                System.out.println("ID: " + rs.getInt("id") + ", Name: " + rs.getString("name") + ", Age: " + rs.getInt("age"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                rs.close();
                ps.close();
                conn.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }

(2)redis查询

 //redis查询
    public void queryDataWithRedis() {
        Jedis jedis = new Jedis("localhost", 6379);
        Set<String> keys = jedis.keys("user:*");
        for (String key : keys) {
            Map<String, String> user = jedis.hgetAll(key);
//            System.out.println("Key: " + key + ", Value: " + user);
        }
        jedis.close();
    }

(3)记录并比较查询时间

// 比较查询时间
    public void compareTime() {
        // 通过jdbc查询这10000条数据,记录查询时间
        long start = System.currentTimeMillis();
        queryDataWithJDBC();
        long end = System.currentTimeMillis();
        System.out.println("JDBC查询时间: " + (end - start) + "ms");

        // 通过redis查询这10000条数据,记录查询时间
        start = System.currentTimeMillis();
        queryDataWithRedis();
        end = System.currentTimeMillis();
        System.out.println("Redis查询时间: " + (end - start) + "ms");
    }

5.根据年龄进行排序

(1)mysql排序

    //mysql实现排序
    public void queryAndSortDataWithJDBC() {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        conn = getConnection();
        try {
            ps = conn.prepareStatement("SELECT * FROM user ORDER BY age");
            rs = ps.executeQuery();
            System.out.println("mysql实现排序:");
            while (rs.next()) {
                System.out.println("ID: " + rs.getInt("id") + ", Name: " + rs.getString("name") + ", Age: " + rs.getInt("age"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                rs.close();
                ps.close();
                conn.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }

(2)redis排序

    //redis实现排序
    public void queryAndSortDataWithRedis() {
        Jedis jedis = new Jedis("localhost", 6379);
        List<Tuple> users = jedis.zrangeWithScores("UserByAge", 0, -1);
        System.out.println("redis实现排序:");
        for (Tuple user : users) {
            String id = user.getElement();
            double age = user.getScore();
            String name = jedis.hget("user:" + id, "name");
            System.out.println("ID: " + id + ", Name: " + name + ", Age: " + (int) age);
        }
        jedis.close();
    }

6.抽奖功能

    //抽奖
    public void lottery() {
        Jedis jedis = new Jedis("localhost", 6379);

        // 添加奖品
        String[] prizes = {"锅", "碗", "瓢", "盆", "金元宝"};
        for (String prize : prizes) {
            jedis.sadd("prizes", prize);
        }

        // 年龄最小的前5人
        System.out.println("年龄最小的前5人:");
        List<Tuple> youngestUsers = jedis.zrangeWithScores("UserByAge", 0, 4);
        for (Tuple user : youngestUsers) {
            String id = user.getElement();
            double age = user.getScore();
            String name = jedis.hget("user:" + id, "name");
            String prize = jedis.srandmember("prizes");
            System.out.println("恭喜 " + name + " 获得了抽奖机会!奖品是:" + prize);
        }

        jedis.close();
    }

7.主函数

    public static void main(String[] args) throws SQLException {

        JedisHomework jedisHomework = new JedisHomework();
        jedisHomework.addMysql();
        jedisHomework.addRedis();
        jedisHomework.compareTime();
        jedisHomework.queryAndSortDataWithJDBC();
        jedisHomework.queryAndSortDataWithRedis();
        jedisHomework.lottery();

    }

Redis中的缓存穿透、雪崩、击穿的原因以及解决方案

1.缓存击穿

(1)产生原因

        在高并发访问下,某个热点key在缓存中过期后,大量并发请求同时查询数据库,导致数据库压力激增的现象。

(2)解决方案

合理的过期时间:将热点数据设置为永远不过期

使用互斥锁:基于redis or zookeeper实现互斥锁,等待第一个请求构建完缓存之后,再释放锁,进而其他请求才能通过该key访问数据。

2.缓存雪崩

(1)产生原因

        由于缓存服务器在同一时间大面积失效或宕机,导致大量请求直接打到数据库,瞬间引发数据库压力激增,甚至导致数据库崩溃。

(2)解决方案

事前:redis 高可用,主从+哨兵,redus cluster,避免全盘崩溃

事中:本地缓存 + hystrix 限流&降级,避免 MySQL被打死。同时设置合理的过期时间。

事后:redis持久化,一旦重启,自动从磁盘上加载数据,快速回复缓存数据。

3.缓存穿透

(1)产生原因

        查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,进而给数据库带来压力。

        缓存穿透很有可能是黑客攻击所为,黑客通过发送大量的高并发的无法响应的请求给服务器,由于请求的资源根本就不存在,DB(数据库)就很容易被打垮了。

(2)解决方案

缓存空对象:对查询结果为空的情况,也将其缓存起来,并设置合理的过期时间。

参数校验:在接收到请求之前进行参数校验,判断请求参数是否合法。

布隆过滤器:判断请求的参数是否存在于缓存或数据库中。

4.三者的异同

相同点:大量的请求在redis上得不到响应,那么就会导致这些请求会直接去访问DB,导致DB的压力瞬间变大而卡死或者宕机。

不同点:缓存击穿是某个热点过期后,导致大量请求访问DB;

               缓存雪崩是多个key过期后,导致大量请求访问DB;

               缓存穿透是不存在的key收到大量请求,每次请求都要到DB查询。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JavaCV 图像边缘检测 之 Canny 算法

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…

Java Agent使用

文章目录 基本使用premain使用场景 agentmain 关于tools.jar https://docs.oracle.com/en/java/javase/20/docs/specs/jvmti.html com.sun的API&#xff0c;如果使用其他厂商的JVM&#xff0c;可能没有这个API了&#xff0c;比如Eclipse的J9 https://www.ibm.com/docs/en/sdk…

解决客服打字慢的快捷回复软件

客服每天都要打字回复咨询&#xff0c;打字慢必然影响回复效率&#xff0c;聊天宝就是一款解决客服打字慢的快捷回复软件 前言 总所周知&#xff0c;客服每天都要打字回复咨询&#xff0c;打字慢必然影响回复效率&#xff0c;所以解决打字速度慢的问题&#xff0c;就是很多客服…

【2024最新版Kotlin教程】Kotlin第一行代码系列第六课-嵌套类,数据类,密封类

【2024最新版Kotlin教程】Kotlin第一行代码系列第六课-嵌套类&#xff0c;数据类&#xff0c;密封类 数据类在之前的课程已经讲了&#xff0c;嵌套类和密封类可以不学&#xff0c;很少用到的。 但为了系统的完整性还是说一下 一、嵌套类&#xff1a; 选学&#xff1a;按我经…

uniapp 开发公众号 h5 页面 错误 “redirect_uri 参数错误“

公众号 h5 支付 调错避坑 1. 一直显示以下错误 ”redirect_uri 失效了“不用管直接 打包上传服务器 2. 直接在”微信开发者工具“ 进行调试&#xff08; 使用线上地址访问 &#xff09;就能看到真实 接口 错误&#xff0c;在进行调试

闪存学习_2:Flash-Aware Computing from Jihong Kim

闪存学习_2&#xff1a;Flash-Aware Computing from Jihong Kim【1】 一、三个闪存可靠性问题二、内存的分类三、NAND 闪存和 NOR 闪存四、HDD和SSD比较Reference 一、三个闪存可靠性问题 耐性&#xff08;即寿命&#xff09;&#xff1a;最多能经受编程和擦除的次数。数据保留…

代码随想录算法训练营Day55 | 图论理论基础、深度优先搜索理论基础、卡玛网 98.所有可达路径、797. 所有可能的路径、广度优先搜索理论基础

目录 图论理论基础 深度优先搜索理论基础 卡玛网 98.所有可达路径 广度优先搜索理论基础 图论理论基础 图论理论基础 | 代码随想录 图的基本概念 图的种类 大体分为有向图和无向图。 图中的边有方向的是有向图&#xff1a; 图中的边没有方向的是无向图&#xff1a; 图…

OpenEuler 使用ffmpeg x11grab捕获屏幕流,rtsp推流,并用vlc播放

环境准备 安装x11grab(用于捕获屏幕流)和libx264(用于编码) # 基础开发环境&x11grab sudo dnf install -y \autoconf \automake \bzip2 \bzip2-devel \cmake \freetype-devel \gcc \gcc-c \git \libtool \make \mercurial \pkgconfig \zlib-devel \libX11-devel \libXext…

【Simulink仿真】混合储能平抑光伏功率波动

摘要 本文基于Simulink仿真平台&#xff0c;提出了一种混合储能系统&#xff08;Hybrid Energy Storage System, HESS&#xff09;来平抑光伏发电中的功率波动。该系统将超级电容与电池相结合&#xff0c;通过双向DC-DC变换器实现能量的动态分配。超级电容响应快&#xff0c;主…

C语言必做30道练习题

C语言练习30题&#xff08;分支循环&#xff0c;数组&#xff0c;函数&#xff0c;递归&#xff0c;操作符&#xff09; 目录 分支循环1.闰年的判断2.阅读代码&#xff0c;计算代码输出的结果3.输入一个1~7的数字&#xff0c;打印对应的星期几4.输入任意一个整数值&#xff0c;…

进程与线程+多线程优势

区别&#xff1a; 1、进程中包含线程&#xff0c;每一个进程都至少一个线程&#xff08;主线程&#xff09; 2、进程是申请系统资源的最小单位 3、进程是CPU调度的最小单位 4、线程之间共享进程申请的系统资源 5、一个线程崩溃了会影响整个进程 进程的组织方式&#xff1…

期权懂|期权策略中两边开卖方实值对冲会有盈利区间吗?

期权小懂每日分享期权知识&#xff0c;帮助期权新手及时有效地掌握即市趋势与新资讯&#xff01; 期权策略中两边开卖方实值对冲会有盈利区间吗&#xff1f; 一、期权策略中两边开卖方实值对冲的盈利区间可以‌核心策略‌分析‌&#xff1a; 期权对冲策略的核心是利用期权的特…

Follow软件的使用入门教程

开篇 看到很多兄弟还不知道怎么用这个当下爆火的浏览器&#xff01;在这里简单给需要入门的小伙伴一些建议&#xff1a; 介绍 简单解释一下&#xff0c;RSS 意思是简易信息聚合&#xff0c;用户可以通过 RSS 阅读器或聚合工具自主订阅并浏览各个平台的内容源&#xff0c;不用…

数字孪生的建构之路:从数据到智能

数字孪生是一种将物理实体系统或产品的数字化表示与其实体对应物相结合的概念&#xff0c;通过这种数字化技术&#xff0c;可以实时监测、预测和优化管理实体系统。实现数字孪生需要经历从数据采集、处理到智能化决策等多个步骤。以下是关于如何实现数字孪生的详细介绍。 1. 数…

【C#】创建一个主菜单和弹出菜单系统

文章目录 1. 创建WinForms项目2. 设计窗体3. 添加MenuStrip4. 配置菜单项5. 添加TextBox6. 编写事件处理代码7. 运行和测试 根据您提供的文件内容&#xff0c;看起来您需要在C# WinForms应用程序中设置一个窗体&#xff0c;其中包含一个文本框和几个菜单项&#xff0c;用于改变…

运维告警策略优化与实践

在运维行业中&#xff0c;告警策略的制定与执行是确保系统稳定性和业务连续性的关键环节。面对日益复杂的IT环境和不断变化的运维需求&#xff0c;如何合理制定并优化告警策略&#xff0c;成为运维团队必须面对的重要课题。本文将结合运维行业的现状、挑战及需求&#xff0c;深…

算法通关(3) -- kmp算法

KMP算法的原理 从题目引出 有两个字符串s1和s2,判断s1字符串是否包含s2字符串&#xff0c;如果包含返回s1包含s2的最左开头位置&#xff0c;不包含返回-1&#xff0c;如果是按照暴力的方法去匹配&#xff0c;以s1的每个字符作为开头&#xff0c;用s2的整体去匹配&#xff0c;…

vue3+vite搭建脚手架项目使用eletron打包成桌面应用+可以热更新

当前Node版本&#xff1a;18.12.0&#xff0c;npm版本&#xff1a;8.19.2 1.搭建脚手架项目 搭建Vue3ViteTs脚手架-CSDN博客 可删掉index.html文件的title标签 2.配置package.json {"name": "my-vite-project","private": true,"versi…

Java学习者的福音:SpringBoot教学辅助平台

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理教学辅助平台的相关信息成为必然。开发合适…

JAVA基础:数组 (习题笔记)

一&#xff0c;编码题 1&#xff0c;数组查找操作&#xff1a;定义一个长度为10 的一维字符串数组&#xff0c;在每一个元素存放一个单词&#xff1b;然后运行时从命令行输入一个单词&#xff0c;程序判断数组是否包含有这个单词&#xff0c;包含这个单词就打印出“Yes”&…