[Redis] Redis实战

news2025/1/17 4:35:00

✨✨个人主页:沫洺的主页

📚📚系列专栏: 📖 JavaWeb专栏📖 JavaSE专栏 📖 Java基础专栏📖vue3专栏 

                           📖MyBatis专栏📖Spring专栏📖SpringMVC专栏📖SpringBoot专栏

                           📖Docker专栏📖Reids专栏📖MQ专栏📖SpringCloud专栏     

💖💖如果文章对你有所帮助请留下三连✨✨

💌RedisTemplate使用Scan 

@SpringBootTest
public class AppTests_Scan {

    @Resource(name = "redisTemplate")
    private ValueOperations<String, User> valueOperations;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Test
    void test1() {
        for (int i =1 ;i<11;i++){
            User user = User.builder().name(i+"号张").age(18).build();
            valueOperations.set("user."+i,user);
        }
    }
    
}

原生的scan用法

RedisTemplate使用Scan 

@SpringBootTest
public class AppTests_Scan {

    @Resource(name = "redisTemplate")
    private ValueOperations<String, User> valueOperations;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Test
    void test1() {
        for (int i =1 ;i<11;i++){
            User user = User.builder().name(i+"号张").age(18).build();
            valueOperations.set("user."+i,user);
        }
    }
    @Test
    void test2(){
        //迭代扫描,指定模糊匹配,预想数量
        ScanOptions scanOptions = ScanOptions.scanOptions().match("user.*").count(3).build();
        //获取游标
        Cursor<String> cursor = stringRedisTemplate.scan(scanOptions);
        while (cursor.hasNext()){
            //获取游标id
            System.out.println(cursor.getCursorId());
            System.out.println(cursor.next());
        }
        cursor.close();
    }
}

💌模拟多线程并发使用Decrby 

模拟高并发场景线上购物,多个用户(线程)同时选购某产品,提示库存信息

先初始化产品库存

@SpringBootTest
class AppTests_DecrBy {

    @Resource(name = "redisTemplate")
    private ValueOperations<String, Integer> valueOperations;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    //定义一个产品
    private final static String productKey = "product01";

    @Test
    void test1() {
        //设置初始化库存5个
        valueOperations.set(productKey, 5);
    }
}

不考虑并发的情况下

    @Test
    void test2() throws InterruptedException {
        //获取线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        //模拟开启10个线程
        for (int i = 1; i <= 10; i++) {
            //开启线程执行
            executorService.execute(() -> {
                //获取库存
                Integer qty = valueOperations.get(productKey);
                //如果库存充足,产品数量就减一
                if (qty > 0) {
                    qty = qty - 1;
                    //重新设置库存
                    valueOperations.set(productKey, qty);
                    System.out.println(Thread.currentThread().getName() + " 库存充足");
                } else {
                    System.out.println(Thread.currentThread().getName() + " 库存不足");
                }
            });
        }
        //防止主线程停止,导致模拟线程不执行
        Thread.sleep(60 * 1000);
    }

出现问题,明明库存只有5个,10个用户却都提示库存充足

考虑高并发的情况下 

使用decrement来保证原子性,解决高并发问题

    @Test
    void test3() throws InterruptedException {
        //获取线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        //模拟开启10个线程
        for (int i = 1; i <= 10; i++) {
            //开启线程执行
            executorService.execute(() -> {
                //获取库存
                Long qty = valueOperations.decrement(productKey, 1);
                if (qty >= 0) {
                    System.out.println(Thread.currentThread().getName() + " 库存充足");
                } else {
                    System.out.println(Thread.currentThread().getName() + " 库存不足");
                }
            });
        }
        //防止主线程停止,导致模拟线程不执行
        Thread.sleep(60 * 1000);
    }

解决了并发问题但是出现了新的问题,库存量为负数不合理(意思就是某个线程在执行删减库存时,其他线程也在同一时刻去执行,没有保障redis原子性问题)

有一种解决办法就是通过加锁synchronized来解决,但是响应能力就会大幅降低,也就失去了redis高性能的意义

所以最好的解决方式就是通过EVAL命令执行Lua脚本来解决这类问题

💌模拟多线程并发获取分布式锁SetNX

模拟某一个产品的出入库单据只能一个人(线程)操作的场景(分布式锁),当有人操作时,其他人只能等待该人操作结束之后进行操作

@SpringBootTest
@Slf4j
class AppTests_SetNx {

    @Resource(name = "redisTemplate")
    private ValueOperations<String, Integer> valueOperations;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    //定义一个产品
    private final static String productKey = "product01";
    //定义锁
    private final static String lockKey = "lock.1";

    //业务
    private void doing(){
        while (true){
            //设置锁
            Boolean b = valueOperations.setIfAbsent(lockKey, 1);
            if(b){
                log.info(Thread.currentThread().getName() + " 获取到分布式锁");
                //业务代码开始
                ThreadUtil.sleep(3000);//模拟执行业务操作用时
                //业务代码结束
                stringRedisTemplate.delete(lockKey);
                log.info(Thread.currentThread().getName() + " 释放分布式锁");
                break;

            } else {
                //log.info(Thread.currentThread().getName() + " 没有获取到分布式锁,开始睡眠");
                ThreadUtil.sleep(2000);
            }
        }
    }

    @Test
    void test1() {
        //设置初始化库存5个
        valueOperations.set(productKey, 0);
    }

    @Test
    void test2() throws InterruptedException {
        //获取线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        //模拟开启10个线程
        for (int i = 1; i <= 10; i++) {
            //开启线程执行
            executorService.execute(this::doing);
        }
        //防止主线程停止,导致模拟线程不执行
        Thread.sleep(60 * 1000);
    }

}

使用分布式锁模拟用户下单操作,防止恶意并发(一般来说用户下单产品操作时间最少也需要几秒的时间,这里预设5秒),也就是说5秒内只允许下单成功一次,也就解决了恶意并发的问题

    @Test
    void test3() throws InterruptedException {
        String user = "用户1";
        //获取线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        //模拟开启10个线程
        for (int i = 1; i <= 10; i++) {
            //开启线程执行
            executorService.execute(() -> {
                Boolean b = valueOperations.setIfAbsent("lock." + user, 1, 5, TimeUnit.SECONDS);
                if (b) {
                    //下单
                    log.info("{} 下单成功", Thread.currentThread().getName());
                } else {
                    log.info("{} 稍后再试", Thread.currentThread().getName());
                }
            });
        }
        //防止主线程停止,导致模拟线程不执行
        Thread.sleep(60 * 1000);
    }

💌模拟多线程并发访问

在网关设置防止恶意并发访问,将用户ip作为key,只允许用户ip在短时间内访问2次

package com.moming;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.thread.ThreadUtil;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

import javax.annotation.Resource;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

@SpringBootTest
@Slf4j
class AppTests_Increment {

    @Resource(name = "redisTemplate")
    private ValueOperations<String, Integer> valueOperations;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Test
    void test4() throws InterruptedException {
        //模拟用户ip
        String userIp = "127.0.0.1";
        //获取线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        //模拟开启10个线程
        for (int i = 1; i <= 10; i++) {
            //开启线程执行
            executorService.execute(() -> {
                String seconds = DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss");

                String key = seconds + userIp;

                Long count = valueOperations.increment(key, 1);

                if(count>2){
                    //下单
                    log.info("{} 超过请求次数",Thread.currentThread().getName());
                } else  {
                    log.info("{} 请求正常",Thread.currentThread().getName());
                }
            });
        }
        //防止主线程停止,导致模拟线程不执行
        Thread.sleep(60 * 1000);
    }

}

💌BitMap模拟在线统计 

模拟某线上活动要举办3天,参与活动的人员可以线上签到,后台通过3天人员的签到情况,统计信息

  1. 3天满勤人数: 三天都签到才算一位
  2. 3天活跃人数: 任何一天签到都算一位
@SpringBootTest
class AppTests_BitMap {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Test
    public void test2() {
        //2022111 20221112 20221113 三天活动
        //张三 1, 李四 2, 王五:3
        //满勤人数,1人
        //活跃人数,3人
        stringRedisTemplate.opsForValue().setBit("20221111",1,true);
        stringRedisTemplate.opsForValue().setBit("20221112",2,true);
        stringRedisTemplate.opsForValue().setBit("20221112",2,true);
        stringRedisTemplate.opsForValue().setBit("20221111",3,true);
        stringRedisTemplate.opsForValue().setBit("20221112",3,true);
        stringRedisTemplate.opsForValue().setBit("20221113",3,true);
        //RedisStringCommands.BitOperation.AND 满勤人数
        //RedisStringCommands.BitOperation.OR 活跃人数
        RedisCallback<Long> callback1 = connection -> {
            connection.bitOp(RedisStringCommands.BitOperation.OR,
                    "人数统计".getBytes(StandardCharsets.UTF_8),
                    "20221111".getBytes(StandardCharsets.UTF_8),
                    "20221112".getBytes(StandardCharsets.UTF_8),
                    "20221113".getBytes(StandardCharsets.UTF_8)
            );
            Long count = connection.bitCount("人数统计".getBytes(StandardCharsets.UTF_8));
            return count;
        };
        Long count1 = stringRedisTemplate.execute(callback1);
        System.out.println(count1);
    }
}

RedisStringCommands.BitOperation.AND 满勤人数

RedisStringCommands.BitOperation.OR 活跃人数

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

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

相关文章

[附源码]java毕业设计实践教学管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Echarts:好玩的timeline

Echarts是一个开源的可视化图表库&#xff0c;支持丰富的图表&#xff0c;官网中还有大量示例可以选择使用、参考。 其中比较有趣的一个特性是可以把数据随时间变化而变化&#xff0c;其效果与一些视频中比较不同国家的国力随时间变化的排名变化的效果相似。 接下啦我们就实现…

linux下golang环境安装教程(学习笔记)

linux下golang环境安装教程&#xff08;学习笔记&#xff09; SSH远程登录linux服务器 安装 mercurial包 [rootlocalhost ~]# yum install mercurial 安装git包 [rootlocalhost ~]# yum install git 安装gcc【一般自带安装好了的】 [rootlocalhost ~]# yum install gcc …

黑*头条_第3章_文章详情前后端成形记

黑*头条_第3章_文章详情前后端成形记 文章目录黑*头条_第3章_文章详情前后端成形记文章详情前后端成形记1 分布式主键封装1.1 依赖导入1.2 配置文件1.3 枚举封装1.4 序列封装1.5 Client封装1.6 Config封装1.7 Sequences封装1.8 使用案例1.9 扩展自增表2 App文章详情2.1 功能需求…

Spring IOC源码:registerBeanPostProcessors 详解

前言 上篇文章介绍了后置处理器BeanFactoryPostProcessor的注册、实例化及执行操作&#xff0c;这节介绍一下另外一个后置处理器BeanPostProcessor。前者是针对BeanFactory工厂对象进行增上改查操作&#xff0c;在bean实例化之前&#xff0c;我们可以修改其定义。后者是对实例…

电脑重装系统蓝屏是什么原因

​电脑蓝屏是由于系统故障、致命的系统错误或系统崩溃而导致的现象&#xff0c;要想修复电脑蓝屏&#xff0c;关键是找出原因所在。为此&#xff0c;下面小编就给大家整理电脑蓝屏是什么原因。 原因1、虚拟内存不足造成系统多任务运算错误&#xff1a; 虚拟内存是Windows系统所…

(免费分享)基于springboot健康运动-带论文

源码获取&#xff1a;关注文末gongzhonghao&#xff0c;输入013领取下载链接 ​开发工具&#xff1a;IDEA, mysql5.7 技术&#xff1a;springbootmybatis-plus 健康管理包括&#xff1a;健康体检、健康评估、健康促进和健康服务四大部分。具体来说健康管理就是由健康管理顾问…

13.4 GAS与攻击

目录1. 由GA砍出的第一刀2. 挥剑时的命中检测3. 完善&#xff1a;UI显示当前血量参考&#xff1a;1. 由GA砍出的第一刀 有了前面章节的经验&#xff0c;我们可以很容易创建一个专用于攻击的GA&#xff1a; 其中PlayMontageAndWait任务节点负责攻击动画及相应回调的绑定。 但是…

向毕业妥协系列之深度学习笔记(二)深层神经网络

目录 一.深层神经网络 二.前向和反向传播 三.深层网络中的前向传播 四.核对矩阵的维数 五.为什么使用深层表示 六.参数VS超参数 一.深层神经网络 就是好多层。 二.前向和反向传播 三.深层网络中的前向传播 四.核对矩阵的维数 略 五.为什么使用深层表示 我们都知道深度…

在淘宝开店后,如何发布宝贝?从哪发布?

近期&#xff0c;有几位在淘宝新开店的店主&#xff0c;来向我们咨询了一些问题&#xff0c;总结来说可以将问题归为一个&#xff1a;在淘宝开店后&#xff0c;怎样上传宝贝&#xff1f;从哪上传&#xff1f;下面&#xff0c;小编来给大家简单的说一下发布宝贝时要注意什么&…

AD域 - 自动为域颁发证书

自动为域用户颁发数字证书 ———————————— 进入CA证书颁发机构 在“证书颁发机构”窗口中,鼠标右键点击“证书模板”,在弹出的快捷菜单中点击“管理” 在“证书模板控制台”窗口中,鼠标右键点击右侧列表中的“用户”,在弹出的快捷菜单中点击“复制模板

【Redis】Redis数据库的实现原理

在之前的文章我们介绍过&#xff0c;Redis服务器在启动之初&#xff0c;会初始化RedisServer的实例&#xff0c;在这个实例中存在很多重要的属性结构&#xff0c;同理本篇博客中介绍的数据库实现原理也会和其中的某些属性相关&#xff0c;我们继续看一下吧。 1.服务器和客户端…

【设计模式】 - 创建者模式 - 原型模式

目录标题1. 原型模式1.1 概述1.2 结构1.3 实现1.4 浅克隆Demo1&#xff1a;基本类型Demo2&#xff1a;引用类型浅克隆总结&#xff1a;1.5 深克隆实现方式1&#xff1a;浅克隆嵌套1. Address类实现Cloneable接口&#xff0c;重写clone方法;2. 在Customer类的clone方法中调用Add…

[附源码]SSM计算机毕业设计智慧教室预约JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

2019年1+X 证书 Web 前端开发中级理论考试——易错题、陌生但又会考到的题目原题+答案

文章目录 &#x1f3af;关于1X标准 &#x1f3af;关于中级考点 ❗❗❗注意&#xff1a; 理论题题型包括单选题、多选题、判断题。 ❗注意&#xff1a;题目序号没有修改 ❗红色的选项才是正确答案 ❗如果题目后面没有红色的选项&#xff0c;那么括号里面的答案是正确的 …

Unity游戏Mod/插件制作教程01 - BepInEx的安装和使用

前言 本章节为没有使用过BepInEx的同学进行BepInEx的安装和使用方面的介绍&#xff0c;如果你之前已经使用过并了解如何使用&#xff0c;可以直接跳过本章节。 BepInEx下载 BepInEx的Github链接 https://github.com/BepInEx/BepInEx/releases 一共有3种版本&#xff0c;BepIn…

Hive环境搭建

3.1 Hive环境搭建 3.1.1 Hive引擎简介 Hive引擎包括&#xff1a;默认MR、tez、spark Hive on Spark&#xff1a;Hive既作为存储元数据又负责SQL的解析优化&#xff0c;语法是HQL语法&#xff0c;执行引擎变成了Spark&#xff0c;Spark负责采用RDD执行。 Spark on Hive : Hi…

人人开源后台项目maven构建(yyds)

人人开源后台项目maven构建(yyds) npm run serve 和 npm run dev 的区别在日常运行vue 项目中 在终端 运行命令有时用到 npm run serve 有时是 npm run dev。那么&#xff0c;什么时候用到 serve &#xff0c;什么时候用到 dev 呢&#xff1f; 他们的区别是什么&#xff1f;一…

【学习笔记】《Python深度学习》第四章:机器学习基础

文章目录1 机器学习的四个分支1.1 监督学习1.2 无监督学习1.3 自监督学习1.4 强化学习2 评估机器学习模型2.1 训练集、验证集和测试集2.2 注意事项3 数据预处理、特征工程和特征学习3.1 神经网络的数据预处理3.2 特征工程4 过拟合与欠拟合4.1 减小网络大小4.2 添加权重正则化4.…

postgresql安装配置和基本操作

1.安装 linux上安装 最好是centos7.6或者7.8&#xff0c; 参考官网 PGSQL的官方地址&#xff1a;PostgreSQL: The worlds most advanced open source database PGSQL的国内社区&#xff1a;PostgreSQL中文社区:: 世界上功能最强大的开源数据库... 点击download PostgreSQ…