Solve the Redis overselling problem

The first: use synchronized (only applicable to a single tomcat)

@Autowired
private StringRedisTemplate stringRedisTemplate;
@GetMapping("/deduct")
    public String toDeduct(){
    
    
        synchronized (this) {
    
    
            int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
            if (stock > 0) {
    
    
                Long stock1 = stringRedisTemplate.opsForValue().decrement("stock");
                System.out.println("扣减成功,剩余库存:" + stock1);
            }else{
    
    
                System.out.println("扣减失败,库存不足");
            }
            return "end";
        }
    }

The second: use the SETNX command (multiple tomcats)

 @GetMapping("/deduct_stock")
    public String deductStock() throws InterruptedException {
    
    
        String lockKey = "lockKey";
        //设计唯一key值防止锁失效问题
        String clientId = UUID.randomUUID().toString();
        //设计锁超时时间,防止服务器宕机时锁没有释放掉(finally语句没有执行)
        Boolean result = stringRedisTemplate.opsForValue().
                setIfAbsent(lockKey, clientId, 10, TimeUnit.SECONDS);//jedis.setnx(key,value);
        //若键 key 已经存在, 则 SETNX 命令不做任何动作。result==false
        if (!result) {
    
    
            return "正在排队。。。";
        }
        try {
    
    
            int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
            if (stock > 0) {
    
    
                Long realStock = stringRedisTemplate.opsForValue().decrement("stock");
                System.out.println("扣减成功,剩余库存:" + realStock);
            }else{
    
    
                System.out.println("扣减失败,库存不足");
            }
        } finally {
    
    
            //防止锁失效问题,在多线程的情况下,每个线程只释放自己创建的锁,线程之间互不干预。
            if(clientId.equals(stringRedisTemplate.opsForValue().get(lockKey))) {
    
    
                stringRedisTemplate.delete(lockKey);
            }
        }
        return "end";
    }

The third type: use the Redission framework (the principle is the same as the second type)

1. Configure the config class

@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
    public RedissonClient getRedisson(){
    
    
        Config config = new Config();
        config.useSingleServer().setAddress("redis://" + host + ":" + port).setPassword(password);
        //添加主从配置
//      config.useMasterSlaveServers().setMasterAddress("").setPassword("").addSlaveAddress(new String[]{"",""});
        return Redisson.create(config);
    }
}
@GetMapping("/deduct_stock1")
    public String deductStock1(){
    
    
        String lockKey = "lockKey";
        RLock redissonLock = redisson.getLock(lockKey);
        try {
    
    
            redissonLock.lock();
            int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
            if (stock > 0) {
    
    
                Long realStock = stringRedisTemplate.opsForValue().decrement("stock");
                System.out.println("扣减成功,剩余库存:" + realStock);
            }else{
    
    
                System.out.println("扣减失败,库存不足");
            }
        } finally {
    
    
            redissonLock.unlock();
        }
        return "end";
    }

Redission principle:

Redission uses Lua scripts, and the implementation principle is similar to the second one.
When the business code processing time is very long, like mysql slow query, etc., you can check whether the lock is still held every 10 seconds in the try code block. If you hold it, extend the lock time to prevent the business code from being executed "lockKey" "It's the expiration time.
Insert picture description here


Test environment setup

Redis command list

Set the Redis password
Insert picture description here
pom:

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.5.7</version>
        </dependency>

application.properties:

server.port=8090
# redis
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=888888
spring.redis.jedis.pool.max-active=500
spring.redis.jedis.pool.max-idle=1000
spring.redis.jedis.pool.max-wait=6000ms
spring.redis.jedis.pool.min-idle=4

Nginx load balancing:

 upstream redislock{
    
    
	server 127.0.0.1:8080 weight = 1;
	server 127.0.0.1:8090 weight = 1;
    }
    server {
    
    
        listen       80;
        server_name  localhost;
   		location / {
    
    
		root html;
		index index.html index.htm;
		proxy_pass http://redislock;
        }
    }

Jmeter stress test

Insert picture description here
Send http request:
Insert picture description here
set the sending address: it
Insert picture description here
means to send 200 requests within 0 seconds, and then send 200 * 3 times after processing, a total of 200 * 4 requests:
Insert picture description here
result set:
Insert picture description here

Test result:
Insert picture description here
Insert picture description here
no oversold problem

Guess you like

Origin blog.csdn.net/weixin_41699562/article/details/105224835