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

目录

  • 引出
  • 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的高并发问题

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

Redisson中间件

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

引入Redisson


    org.redisson
    redisson-spring-boot-starter
    3.21.3

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

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

        <dependency>
            <groupId>org.redissongroupId>
            <artifactId>redisson-spring-boot-starterartifactId>
            <version>3.9.1version>
        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方法测试

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

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

2.用JMeter进行高并发测试

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

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

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

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

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

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

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(); // 把锁释放

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

1.导包+配置类


        <dependency>
            <groupId>org.redissongroupId>
            <artifactId>redisson-spring-boot-starterartifactId>
            <version>3.9.1version>
        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.高并发测试

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


总结

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

你可能感兴趣的:(#,Redis,redis,分布式,数据库)