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

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

3.为mysql和redis添加数据

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

java 复制代码
    //获取数据库连接
    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;
    }
java 复制代码
    //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中

java 复制代码
    // 将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查询

java 复制代码
    //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查询

java 复制代码
 //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)记录并比较查询时间

java 复制代码
// 比较查询时间
    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排序

java 复制代码
    //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排序

java 复制代码
    //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.抽奖功能

java 复制代码
    //抽奖
    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.主函数

java 复制代码
    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查询。

相关推荐
极限实验室3 小时前
IK 字段级别词典的升级之路
数据库
曾几何时`4 小时前
MySQL(配置)——MariaDB使用
数据库·mysql
努力学习java的哈吉米大王4 小时前
MySQL——MVCC
数据库·mysql
数据要素X4 小时前
【数据架构10】数字政府架构篇
大数据·运维·数据库·人工智能·架构
lixzest5 小时前
Redis实现数据传输简介
数据库·redis·缓存
搬砖的小熊猫5 小时前
MySQL常见面试题
数据库·mysql
lang201509285 小时前
如何使用 Apache Ignite 作为 Spring 框架的缓存(Spring Cache)后端
spring·缓存·apache·ignite
Linux技术支持工程师5 小时前
二十八、【Linux系统域名解析】DNS安装、子域授权、缓存DNS、分离解析、多域名解析
linux·运维·服务器·缓存·centos
weixin_419658315 小时前
MySQL的JDBC编程
数据库·mysql
JavaLearnerZGQ5 小时前
Docker部署Nacos
数据库·docker·容器