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

文章讲述了在Redis处理高并发场景时可能出现的数据不安全问题,并介绍了如何通过引入Redisson中间件实现分布式锁来确保数据的原子性和安全性。在图书抢购的示例中,未加锁的版本存在数据不一致风险,而采用Redisson加锁后,解决了这一问题。
摘要由CSDN通过智能技术生成

引出


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中间件,保证原子性,数据安全;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Arya's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值