redisson solves oversold cases

Keywords: Java Redis Cache

 

1. Import dependent packages of Redisson and StringRedisTemplate

	<!--redisson rely on-->
	<dependency>
		<groupId>org.redisson</groupId>
		<artifactId>redisson</artifactId>
		<version>2.2.13</version>
	</dependency>
	
	<!--StringRedisTemplate rely on-->
    <dependency>
		<groupId>org.springframework.data</groupId>
		<artifactId>spring-data-redis</artifactId>
		<version>2.1.4.RELEASE</version>
	</dependency>
	<dependency>
		<groupId>org.apache.commons</groupId>
		<artifactId>commons-pool2</artifactId>
		<version>2.4.2</version>
	</dependency>
	<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
	<dependency>
		<groupId>redis.clients</groupId>
		<artifactId>jedis</artifactId>
		<version>2.9.0</version>
	</dependency>

2. Using syn lock to secure the sale of stand-alone products

public String deductStockOne(){
       synchronized (this) {
           int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
           if (stock > 0) {
               int realStock = stock - 1;
               stringRedisTemplate.opsForValue().set("stock", realStock + "");
               System.out.println("Goods deducted successfully, remaining inventory" + realStock + "");
           } else {
               System.out.println("Deduction failed, insufficient inventory");
           }
       }
        return "end";
    }

3. The simple version of redis implements distributed locks (for the bug version, see 4 for optimization)

After business superposition, one server can not solve the problem. It is necessary to deploy multiple servers to build a distributed system, and synchronized can only lock the process code blocks in the current JVM virtual machine. The idea of setnx in redis can be used to implement distributed locks to control the real-time monitoring of inventory by different servers during service access.

public String deductStockDustriSimp(){
        String lockKey = "lockKey";
        try {
//            Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "yangguo");
//            stringRedisTemplate.expire(lockKey,10,TimeUnit.SECONDS);

            //Equivalent to redis setnx(key,value)
            Boolean result = stringRedisTemplate.opsForValue()
                                                .setIfAbsent(lockKey, "yangguo", 10, TimeUnit.SECONDS);
            if(!result){
                return "error";
            }
            int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
            if(stock>0){
                int realStock = stock-1;
                stringRedisTemplate.opsForValue().set("stock",realStock+"");
                System.out.println("Goods deducted successfully, remaining inventory"+realStock+"");
            }else{
                System.out.println("Deduction failed, insufficient inventory");
            }
        } finally {
            stringRedisTemplate.delete(lockKey);
        }
        return "end";
    }

  All of the simple version of redis will have bug s, as shown in the figure. Pay attention to several details.

Exception, where are the main bug s? For example, both threads t1 and t2 need to execute for 15s, but the set expiration time is 10s. When t1 has not finished executing, the set expiration time is up, and the Lock is released. t2 obtains the Lock to execute the service, but t1 executes stringRedisTemplate.delete(lockKey); Delete Lock operation. Then the t2 Lock was deleted at this time. It is equivalent to that the distributed Lock has expired in the high concurrency scenario.

  So how to optimize it?
The above problem mainly lies in that the set expiration time is deleted before the thread business runs out. How to solve it? Whether you can start a sub thread in the business code and write a timer to judge whether the lock is still there every few seconds. If so, extend the time until the business is completed and the lock is released.
In essence, it depends on the set expiration time to judge whether the lock exists. If it exists, the time will be extended until the business is successfully executed.
The idea is like this, but if you really want to write a Timer program with a sub thread and a Timer, it will be extra complex. In view of this problem, almost large factories are using redisson to solve it.

4. redis distributed lock implementation (normal version)

Let's take a look at the distributed schematic diagram of redis:

The code is as follows:

 public String deductStockDist (){
        String lockKey = "lockKey";
        RLock lock = redisson.getLock(lockKey);
        try {
            lock.tryLock(30, TimeUnit.SECONDS);
            int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
            if(stock>0){
                int realStock = stock-1;
                stringRedisTemplate.opsForValue().set("stock",realStock+"");
                System.out.println("Goods deducted successfully, remaining inventory"+realStock+"");
            }else{
                System.out.println("Deduction failed, insufficient inventory");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
        return "end";
    }

  Transferred from: Redis is used to implement distributed locks and redistribute_ u013025649 column - CSDN blog

 

Posted by frobak on Sun, 31 Oct 2021 04:35:48 -0700