Redis应用(3)——Redis的项目应用(二):抢购图书 ---> Redis高并发的问题 分布式锁Redission的使用

news2024/11/25 12:43:31

目录

  • 引出
  • Redis的高并发问题
    • redis的高并发问题
    • Redisson中间件
      • 引入Redisson
      • Redisson配置
      • Redisson应用
        • 报错:java.lang.NoClassDefFoundErro
  • Redis的项目应用(二):抢购图书
    • 1.0版本,Java代码:数据不安全
    • 测试方法
      • 1.用client方法测试
      • 2.用JMeter进行高并发测试
    • 问题:redis出现了数据不安全的情况
  • 2.0版本,改进:加锁分布式锁Redission,保证原子性
    • Redisson中间件
    • 1.导包+配置类
    • 2.进行加锁
    • 3.高并发测试
  • 总结

引出


1.Redis是线程安全的,但是高并发时出现数据不安全的问题;
2.解决办法,加锁,Redission分布式锁的使用;
3.Redis项目应用,图书的抢购,不加锁,数据不安全;
4.加了Redission中间件,保证原子性,数据安全;

Redis的高并发问题

在这里插入图片描述

redis的高并发问题

在这里插入图片描述

Redisson中间件

Redisson是一个基于Redis的Java驻留内存数据网格(In-Memory Data Grid)。它封装了Redis客户端API,并提供了一个分布式锁、分布式集合、分布式对象、分布式Map等常用的数据结构和服务。

引入Redisson

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.21.3</version>
</dependency>

Redisson配置

@Configuration
public class RedissionConfig {
    @Value("${spring.redis.host}")
    private String host;
   // @Bean
    public RedissonClient redissonClient(){
        Config config = new Config();
        config.setTransportMode(TransportMode.EPOLL);
        config.useSingleServer().setAddress("redis://192.168.198.130:6379");
        return Redisson.create(config);
    }
}

Redisson应用

@Autowired
private RedissonClient redissonClient;

// 1.获取锁对象
RLock myLock = redissonClient.getLock("myLock");

try{
	// 2.准备锁代码,并进行加锁
	myLock.lock();
    
}finally{
    // 3.解除锁
    myLock.unlock(); // 把锁释放
}

报错:java.lang.NoClassDefFoundErro

java.lang.NoClassDefFoundError: org/springframework/data/redis/connection/zset/Tuple

org.springframework.data.redis.connection.zset.Tuple

在这里插入图片描述

        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.9.1</version>
        </dependency>

Redis的项目应用(二):抢购图书

1.0版本,Java代码:数据不安全

controller层的代码

package com.tianju.redisDemo.controller;

import com.tianju.redisDemo.dto.HttpResp;
import com.tianju.redisDemo.dto.ResultCode;
import com.tianju.redisDemo.service.IBookService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;

@Controller
@RequestMapping("/api/book")
@Slf4j
public class BookController {

    @Autowired
    private IBookService bookService;

    @GetMapping("/rush")
    public HttpResp rushBook(String key){
        Integer rushBuy = bookService.rushBuy(key);
        // 如果为null,说明图书还没进入抢购的队列
        if (rushBuy==null){
            return HttpResp.results(ResultCode.BOOK_RUSH_ERROR,new Date(),"图书还不能秒杀抢购");
        }
        log.debug("》》》》图书剩余库存:"+rushBuy);
        return HttpResp.results(ResultCode.BOOK_RUSH_SUCCESS,new Date(),null);

    }
}

service层的代码

package com.tianju.redisDemo.service.impl;

import com.tianju.redisDemo.service.IBookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class IBookServiceImpl implements IBookService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Integer rushBuy(String key) {
        String numStr = stringRedisTemplate.opsForValue().get(key);
        if (numStr==null){
            return null; // 商品剩余数量还没有进入缓存,不能抢购
        }
        int num = Integer.parseInt(numStr);
        // 如果库存数量大于1;执行-1,去库存操作
        if (num>0){
            num--; // 去库存
            // 更新当前库存数量
            stringRedisTemplate.opsForValue().set(key, String.valueOf(num));
        }
        return num;
    }
}

application.yml配置文件

server:
  port: 9099

spring:
  # redis的相关配置
  redis:
    host: localhost
    port: 6379
    database: 0


# 日志需要配置一下
logging:
  level:
    com.tianju.redisDemo: debug

测试方法

1.用client方法测试

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2.用JMeter进行高并发测试

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

问题:redis出现了数据不安全的情况

在这里插入图片描述

在这里插入图片描述

2.0版本,改进:加锁分布式锁Redission,保证原子性

Redisson中间件

Redisson是一个基于Redis的Java驻留内存数据网格(In-Memory Data Grid)。它封装了Redis客户端API,并提供了一个分布式锁、分布式集合、分布式对象、分布式Map等常用的数据结构和服务。

要点:

  • 获取锁对象;
  • 准备锁代码;
  • 进行加锁;
  • 解除锁,把锁释放;
@Autowired
private RedissonClient redissonClient;

@Override
public Integer rushBuy(String key) {
    // 1.获取锁对象
    RLock myLock = redissonClient.getLock("myLock");
    try {
        // 2.准备锁代码,并进行加锁
        myLock.lock();
        // 进行图书的抢购,去库存 -1
        }
        return num;
    } finally {
        // 3.解除锁
        myLock.unlock(); // 把锁释放

在这里插入图片描述

1.导包+配置类

<!--        分布式锁-->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.9.1</version>
        </dependency>

RedissonConfig.java

package com.tianju.springboot.config;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedissonConfig {
    @Value("${spring.redis.host}")
    private String host;

    @Bean // 别人写的对象,放到spring中
    public RedissonClient redissonClient(){
        Config config = new Config();
        // "redis://192.168.198.130:6379"
        config.useSingleServer().setAddress("redis://"+host+":6379");

        return Redisson.create(config);
    }
}

2.进行加锁

package com.tianju.redisDemo.service.impl;

import com.tianju.redisDemo.service.IBookService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class IBookServiceImpl implements IBookService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Override
    public Integer rushBuy(String key) {
        // 1.获取锁对象
        RLock myLock = redissonClient.getLock("myLock");

        try {
            // 2.准备锁代码,并进行加锁
            myLock.lock();

            // 进行图书的抢购,去库存 -1
            String numStr = stringRedisTemplate.opsForValue().get(key);
            if (numStr==null){
                return null; // 商品剩余数量还没有进入缓存,不能抢购
            }
            int num = Integer.parseInt(numStr);
            // 如果库存数量大于1;执行-1,去库存操作
            if (num>0){
                num--; // 去库存
                // 更新当前库存数量
                stringRedisTemplate.opsForValue().set(key, String.valueOf(num));
            }
            return num;
        } finally {
            // 3.解除锁
            myLock.unlock(); // 把锁释放

        }
    }
}

3.高并发测试

在这里插入图片描述


总结

1.Redis是线程安全的,但是高并发时出现数据不安全的问题;
2.解决办法,加锁,Redission分布式锁的使用;
3.Redis项目应用,图书的抢购,不加锁,数据不安全;
4.加了Redission中间件,保证原子性,数据安全;

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

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

相关文章

Python+Selenium做自动化测试

一.项目介绍 目的 测试某官方网站登录功能模块可以正常使用 用例 1.输入格式正确的用户名和正确的密码&#xff0c;验证是否登录成功&#xff1b; 2.输入格式正确的用户名和不正确的密码&#xff0c;验证是否登录失败&#xff0c;并且提示信息正确&#xff1b; 3.输入格式正…

H 欢乐颂+J 睡美人

H 欢乐颂 登录—专业IT笔试面试备考平台_牛客网 思路&#xff1a;这个题就是题意比较难懂&#xff0c;思路还挺好想的&#xff0c;其实就是每个点都有几个状态&#xff0c;然后只有存在相同状态的点之间才可以连边&#xff0c;假设点i的状态和为a&#xff0c;点j的状态和为b&a…

Packet Tracer – 配置无线路由器上的端口转发

Packet Tracer – 配置无线路由器上的端口转发 地址分配表 设备 接口 IP 地址 子网掩码 LA 互联网 209.165.134.1 255.255.255.252 LAN 192.168.0.1 255.255.255.0 拓扑图 目标 第 1 部分&#xff1a;配置端口转发 第 2 部分&#xff1a;验证 ServerA 远程连接 …

C语言基础:预处理指令的使用

本文结合工作经验&#xff0c;研究C语言中常见的预处理指令的用法。 文章目录 1 预处理指令概念2 常见的预处理指令2.1 #include包含头文件2.2 #define定义宏2.2.1 类对象宏&#xff08;object-like macro&#xff09;2.2.2 类函数宏&#xff08;function-like macro&#xff…

Docker 镜像的创建

Docker 镜像的创建 创建镜像有三种方法&#xff0c;分别为基于已有镜像创建、基于本地模板创建以及基于Dockerfile创建。 1&#xff0e;基于现有镜像创建 基于现有镜像创建 先使用现有镜像创建容器 docker run 再进入容器进行内容的更新 docker exex 最后提交成新的进行 …

C++语法总结

今天给大家带来的不是某个知识点的解说&#xff0c;而是我花了几个小时的时间&#xff0c;写的一个C的思维导图&#xff0c;希望大家能够喜欢 以上是我对C语法的一些理解&#xff0c;有些可能是我遗漏了&#xff0c;有不对的地方希望大家能够指出&#xff0c;最后&#xff0c;子…

【Atcoder】 [ARC151D] Binary Representations and Queries

题目链接 Atcoder方向 Luogu方向 题目解法 首先需要得到一个性质&#xff1a; 当 X i ≠ X j Xi\ne Xj XiXj 时&#xff0c; i , j i,j i,j 操作的先后顺序可以交换 证明&#xff1a; 可以画一张图&#xff0c;只考虑 Y i Y j 0 YiYj0 YiYj0 的情况&#xff0c;其他情…

力扣256.翻转二叉树(递归/qBFS) 剑指offer 32 从上到下打印二叉树(q BFS)I II III(三道题)

采用队列 class Solution { public:TreeNode* invertTree(TreeNode* root) {queue<TreeNode*> q;if(rootNULL) return root;q.push(root);int i0;while(!q.empty()){TreeNode *curq.front();swap(cur->left,cur->right);if(cur->left) q.push(cur->left);if…

电商系统架构设计系列(六):电商的「账户系统」设计要特别考虑哪些问题?

上篇文章中&#xff0c;我给你留了一个思考题&#xff1a;电商的账户系统&#xff0c;该如何设计&#xff1f; 今天这篇文章&#xff0c;我们来说一下电商的账户系统。 引言 账户系统负责记录和管理用户账户的余额&#xff0c;这个余额就是每个用户临时存在电商的钱&#xff…

Spring整合Mybatis原理

首先介绍一下Mybatis的工作原理 先简略的放两张图&#xff0c;后面的知识结合这两张图比较好理解 Mybatis的基本工作原理 在 Mybatis 中&#xff0c;我们可以使用⼀个接口去定义要执行sql&#xff0c;简化代码如下&#xff1a; 定义⼀个接口&#xff0c;Select 表示要执行查询…

UE 材质实现让远处物体变小

CameraDepthFade: 根据距离摄像机的距离改变值 Fade Length: 从0到1的过渡距离 Fade Offset&#xff1a;小于该值的地方值为1 UV平铺&#xff1a;值越大&#xff0c;平铺的越少&#xff0c;纹理重复显示的越少&#xff0c;视觉效果纹理变大&#xff0c;值越小&#xff0c;平铺…

[Linux笔记]gcc/g++,动静态库,make/makefile/.PHONY

都是编译器&#xff0c;二者的选项是重叠的 基本上&#xff0c;gcc专门用于编译c&#xff0c;g专门用于编译c gcc/g形成的可执行程序默认是release版的。若要debug版&#xff0c;则使用-g选项。 如&#xff1a;gcc -o mytest test.c -g -stdc99 编译命令格式例&#xff1a; gc…

MYSQL-死锁大集合

为什么会死锁 数据准备 建个表 CREATE TABLE t_order (id int NOT NULL AUTO_INCREMENT,order_no int DEFAULT NULL,create_date datetime DEFAULT NULL,PRIMARY KEY (id),KEY index_order (order_no) USING BTREE ) ENGINEInnoDB ; 存个数据 然后我们分别创建两个事务 事…

【多模态】16、DetCLIP | 构建超大词汇字典来进行开放世界目标检测

论文&#xff1a;DetCLIP: Dictionary-Enriched Visual-Concept Paralleled Pre-training for Open-world Detection 代码&#xff1a;无。。。 出处&#xff1a;NIPS2022 | 华为诺亚方舟 | 中山大学 | 香港科技大学 效果&#xff1a; 在 LVIS 的 1203 个类别上超越了 GLIP…

每月进度总结 7月1日~7月22日

一个月已经过了三分之二了&#xff0c;感觉这个月是在学校学的很多。也是最充实的三个星期。其中也有发呆&#xff0c;也有过懊悔&#xff0c;今天状态为什么这么差&#xff0c;就学了这一点。但是还有学到知识的喜悦。总之是认识到了自己的很多不足&#xff0c;也找到了相对正…

conda在D盘创建虚拟环境

1.安装Anaconda 略 2.修改镜像源 清华的镜像源好像不能用了。交大的镜像源还能使用。 winr 输入&#xff1a; %HOMEPATH% 进入C盘的用户目录。找到.condarc的文件。打开它&#xff0c;把里面的镜像内容修改为以下内容 channels:- https://mirrors.sjtug.sjtu.edu.cn/anaco…

华硕ROG枪神6plus原装Windows11预装系统 工厂模式恢复安装带ASUSRecevory一键还原安装还原方法

华硕ROG枪神6plus原装Windows11预装系统 工厂模式恢复安装带ASUSRecevory一键还原安装还原方法 第一步&#xff1a;需要拥有文件格式为6个底包的文件 第二步&#xff1a;创建系统u盘 第三步&#xff1a;复制文件到u盘之后&#xff0c;启动华硕工厂模式 第四步&#xff1a;按…

python安装第三方包的两种方式

最近研究QQ空间、微博的&#xff08;爬虫&#xff09;模拟登录&#xff0c;发现都涉及RSA算法。于是需要下一个RSA包&#xff08;第三方包&#xff09;。折腾了很久&#xff0c;主要是感觉网上很多文章对具体要在哪里操作写得不清楚。这里做个总结&#xff0c;以免自己哪天又忘…

MES管理系统如何为汽配制造赋能

汽配制造是企业产业链的重要环节之一&#xff0c;其生产质量和效率直接影响到汽车的整体质量和安全性。然而&#xff0c;传统的汽配制造管理模式存在着一些问题&#xff0c;如生产过程不透明、信息传递不畅、生产效率低下等&#xff0c;这些问题成为了制约汽配制造发展的瓶颈。…