高并发下扣减库存及DB数据库更新数据到redis

高并发下扣减库存及DB数据库更新数据到redis

直接上代码,里面有详细注释哦~
1.pom.xml中引入redis和redission相关依赖

<!--redis-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--redisson分布式锁-->
<dependency>
	<groupId>org.redisson</groupId>
	<artifactId>redisson</artifactId>
	<version>3.11.5</version>
</dependency>

2.application.yml中redis配置参数

spring:
  redis:
    #数据库索引
    host: 127.0.0.1
    port: 6379
    #  password: 123456
    # 连接超时时间(毫秒)
    timeout: 10000
    jedis:
      pool:
        # 连接池中的最大空闲连接
        max-idle: 8
        # 连接池中的最小空闲连接
        min-idle: 10
        # 连接池最大连接数(使用负值表示没有限制)
        max-active: 100
        # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1

3.加入Redission的实例Bean

package com.example.demo.study.HighConcurrency.config;

import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Description: Redission分布式锁配置文件
 * @ClassName: RedissonConfig
 * @Author zhaomx
 * @Version 1.0
 * @Date 2021-06-29 17:29
 */
@Configuration
public class RedissonConfig {

    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private String port;

	//@Value("${spring.redis.password}")
    private String password = "";

    @Bean(destroyMethod = "shutdown")
    public RedissonClient redissonClient() {
        Config config = new Config();
        SingleServerConfig singleServerConfig = config.useSingleServer().setAddress("redis://" + host + ":" + port);
        if (StringUtils.isNotBlank(password)) {
            singleServerConfig.setPassword(password);
        }
        System.out.println("------------- redisson -----------------------");
        System.out.println(config.getTransportMode());
        ;
        //适用于linux操作系统下
		//config.setTransportMode(TransportMode.EPOLL);
        return Redisson.create(config);
    }
}

4.测试类

package com.example.demo.redis;

import com.example.demo.study.HighConcurrency.service.CreateOrderService;
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.RedisTemplate;

import java.io.IOException;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @Description: 创建订单测试
 * @ClassName: CreateOrderTest
 * @Author zhaomx
 * @Version 1.0
 * @Date 2021-06-29 22:12
 */
@SpringBootTest
public class CreateOrderTest {

    @Autowired
    CreateOrderService createOrderService;

    @Autowired
    RedisTemplate redisTemplate;

    @Test
    public void createOrder() throws IOException {

        String productId = "12003";
        redisTemplate.delete(productId);

        Object stock = redisTemplate.opsForHash().get(productId, "stock");
//        redisTemplate.opsForHash().put(productId, "stock", 10000);
        //核心线程数16,最大线程20,使用无界阻塞队列(注意内存溢出)的有界阻塞队列
        ThreadPoolExecutor executor = new ThreadPoolExecutor(16, 20, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(20000));
        for (int i = 1; i < 10002; i++) {
            int finalI = i;
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    createOrderService.createOrder(String.valueOf(finalI), 1, productId);
                }
            });
        }
        executor.shutdown();

        //阻塞主线程
        System.in.read();
    }

}

5.具体实现类方法

package com.example.demo.study.HighConcurrency.service;

import com.example.demo.study.HighConcurrency.center.BadCreateOrderException;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * @Description: 模拟下单方法
 * @ClassName: CreateOrderService
 * @Author zhaomx
 * @Version 1.0
 * @Date 2021-06-29 18:40
 */
@Slf4j
@Service
public class CreateOrderService {

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void createOrder(String orderSn, int number, String productId) {

        //log.info("订单编号:{}", orderSn);
        //创建订单
        //扣减库存
        deductionStock(productId, number);

    }

    /**
     * 扣减库存
     * 使用redis的原子自增方法保证扣减库存正确
     * 使用redisson分布式锁保证更新redis时 只有一个线程先去从数据库取数据更新到redis中
     * Rlock中再次判断当前商品是否已加入redis缓存
     *
     * @param productId
     * @param number
     */
    public void deductionStock(String productId, int number) {

        if (redisTemplate.opsForHash().get(productId, "stock") != null) {
            Long stock = redisTemplate.opsForHash().increment(productId, "stock", ~(number - 1));
            if (stock < 0) {
                redisTemplate.opsForHash().increment(productId, "stock", number);
                log.info("库存不足 stock:{}", stock);
                throw new BadCreateOrderException("库存不足");
            }
            if (stock == 0) {
                log.info("扣减库存成功 扣减数量:{} , 剩余数量:{}", number, stock);
            }

        } else {
            RLock rLock = redissonClient.getLock("stock_key_" + productId);
            try {
                rLock.lock();
                log.info("商品加锁 商品id:{}", productId);
                // 这里使用类似双重检测机制,比如同一时刻有5个线程要获取锁,4个等待,一个拿到了
                // 取到锁的线程执行完之后释放锁,其余等待线程会依次取到锁,为了避免重复从DB数据库查询更新缓存,这里就再判断一次
                if (redisTemplate.opsForHash().get(productId, "stock") == null) {
                    redisTemplate.opsForHash().put(productId, "stock", 10000);
                    log.info("更新redis 商品id:{} 更新后内容:{}", productId, redisTemplate.opsForHash().get(productId, "stock"));
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                rLock.unlock();
                log.info("释放锁");
            }
            deductionStock(productId, number);
        }
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值