Second kill based on redis

Posted by smudge on Tue, 22 Feb 2022 04:28:30 +0100

For single process implementation

   @RequestMapping("/miaosha1")
    @ResponseBody
    public String miaosha1() {

        synchronized (this){
            Map<String,Object> mapListmaps = new HashMap<>();
            //Get inventory value in redis
            int kucun = Integer.valueOf(redisUtil.getStrKV("kucun").toString());
            //If inventory is greater than 0
            if(kucun>0){
                int newkucun = kucun - 1;
                //Store the inventory value in redis (representing the remaining inventory)
                redisUtil.setStrKV("kucun",newkucun);
                System.out.println("Remaining inventory"+newkucun);
            }else{
                System.out.println("Insufficient inventory");
            }
        }
        return "list";
    }

jdk provides synchronized mode, which can be used in stand-alone situations (a single process has built-in lock, which can only control the current process). If the cluster, there will be concurrency problems

For cluster implementation

1,redisTemplate.opsForValue().setIfAbsent mode

@RequestMapping("/miaosha2")
    @ResponseBody
    public String miaosha2() {
        String lockKey = "lockKey";
        String clientId = UUID.randomUUID().toString();
        //If true is returned, it means the save is successful
        Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey,clientId);
        if(!result){
            return  "The current system is busy. Please try again later";
        }
         Map<String,Object> mapListmaps = new HashMap<>();
            //Get inventory value in redis
            int kucun = Integer.valueOf(redisUtil.getStrKV("kucun").toString());
            //If inventory is greater than 0
            if(kucun>0){
                int newkucun = kucun - 1;
                //Store the inventory value in redis (representing the remaining inventory)
                redisUtil.setStrKV("kucun",newkucun);
                System.out.println("Remaining inventory"+newkucun);
            }else{
                System.out.println("Insufficient inventory");
            }
            //After execution, delete the key (let the next one in)
        redisTemplate.delete(lockKey);
        return "list";

    }

redis distributed lock (entry version)
Use redistemplate opsForValue(). setIfAbsent
If the same key value appears, redis will execute the first one by default. If it is found that the second key value is the same as the first key value, the second one will not be executed
>>>Disadvantages: if the redis data decreases, but the subsequent business throws an exception (system downtime), and the rediskey deletion operation cannot be performed, the subsequent operation will be deadlocked

2. Join try{}finally {}, expiration time

@RequestMapping("/miaosha3")
    @ResponseBody
    public String miaosha3() {
        String lockKey = "lockKey";
        //UUID (add a unique identifier for each lock)
        String clientId = UUID.randomUUID().toString();
        //If true is returned, it means the save is successful
        //Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey,clientId);

        //Add timeout
        Boolean result = redisTemplate.opsForValue().setIfPresent(lockKey,clientId,10, TimeUnit.SECONDS);
        if(!result){
            return  "The current system is busy. Please try again later";
        }
        try{
            Map<String,Object> mapListmaps = new HashMap<>();
            //Get inventory value in redis
            int kucun = Integer.valueOf(redisUtil.getStrKV("kucun").toString());
            //If inventory is greater than 0
            if(kucun>0){
                int newkucun = kucun - 1;
                //Store the inventory value in redis (representing the remaining inventory)
                redisUtil.setStrKV("kucun",newkucun);
                System.out.println("Remaining inventory"+newkucun);
            }else{
                System.out.println("Insufficient inventory");
            }
        }finally {
            //Judge whether the current redis is redis under clientId
            if(clientId.equals(redisUtil.getStrKV("lockKey"))){
                //After execution, delete the key (let the next one in)
                redisTemplate.delete(lockKey);
            }

        }
        return "list";

    }

redis distributed lock (entry version)
The previous disadvantage: if the redis data is reduced, but the subsequent business throws an exception and cannot delete the rediskey, the subsequent operation will deadlock
>>>Solution: Join try{}finally {}, and delete the rediskey () regardless of the execution process (add the expiration time to avoid the failure to delete due to system downtime)
>>>Disadvantages: due to the time problem, if the first one takes a long time and the first one times out after 10 seconds, the key will be deleted, so the second one comes in, but the first one finally deletes the second key, which is repeated
>>>Solution: add a unique identifier for each lock (judge whether the current redis is redis under clientId after finally)
>>>Problem: worry about the timeout of 10 second expiration time, and the "lock renewal" can be solved; Set a scheduled task to check whether the lock has been executed. If not, reset 10 seconds to 10 seconds
>>>Problem: distributed lock, problem of releasing lock (atomicity)

3,redissonLock.lock() mode

@RequestMapping("/miaosha4")
    @ResponseBody
    public String miaosha4() {
        String lockKey = "lockKey";

        RLock redissonLock = RedisConfig.redisson().getLock(lockKey);
        try{
            //Lock
            redissonLock.lock();

            //Get inventory value in redis
            int kucun = Integer.valueOf(redisUtil.getStrKV("kucun").toString());
            //If inventory is greater than 0
            if(kucun>0){
                int newkucun = kucun - 1;
                //Store the inventory value in redis (representing the remaining inventory)
                redisUtil.setStrKV("kucun",newkucun);
                System.out.println("Remaining inventory"+newkucun);
            }else{
                System.out.println("Insufficient inventory");
            }
        }finally {
            //Release lock
            redissonLock.unlock();

        }
        return "list";

    }

redis distributed lock (intermediate version) (the company can use enough)
Apply reisson locking (redis upgrade: if the locking is successful, check the "lock renewal" every 10 seconds; and it will add its own identification. If the first lock is not executed, the second one will not come in)
>>>redissonLock.lock() source code:
lua script (the content is the previous step: add key, add logo, add timeout, and a logic for locking and life renewal)
>>>Problem: synchronization between master and slave nodes (when the first redis adds the key value, it will go to the master node first and then to the slave node. But now the master node is hung up, and the first one cannot be completed. At this time, the second one comes in again)

4. Read / write lock (inconsistency between cache and database read / write)

 @RequestMapping("/miaosha5")
    @ResponseBody
    public String miaosha5() {
        String lockKey = "lockKey";
        //Read write lock
        RReadWriteLock rReadWriteLock =  RedisConfig.redisson().getReadWriteLock(lockKey);
        //read
        RLock writeLock = rReadWriteLock.writeLock();
        //write
        RLock rLock = rReadWriteLock.readLock();
        try{
            //Read and lock
            writeLock.lock();
            //Write lock
            rLock.lock();

            //Get inventory value in redis
            int kucun = Integer.valueOf(redisUtil.getStrKV("kucun").toString());
            //If inventory is greater than 0
            if(kucun>0){
                int newkucun = kucun - 1;
                //Store the inventory value in redis (representing the remaining inventory)
                redisUtil.setStrKV("kucun",newkucun);
                System.out.println("Remaining inventory"+newkucun);
            }else{
                System.out.println("Insufficient inventory");
            }
        }finally {
            //Release lock
            writeLock.unlock();
            rLock.unlock();

        }
        return "list";

    }

Problem: the cache is inconsistent with the database read and write
1. Delete the cache after updating the database; Another thread is stuck
2. Delay double deletion (how many milliseconds sleep); It's hard to estimate the jam time of another thread,
3. Memory queue (data operation directly into a queue, atomicity)
4. Distribution lock (operation of each section)
4.5. Read write lock (distributed read lock and write lock, read and write are related, and read and read are not related) -- source code: the key of read-write lock is the same, but the difference is to add a mode of read and write
>>>Scenario: when purchasing goods, there will be many steps during the period, such as "0 plus shopping cart, payment, etc." the cache inventory value is cleared when the real payment is made, and the cache is inconsistent,
>>>Solution: set a timeout (30s);
Read, write and multi-purpose cache is not good, and the hit rate is reduced. canal can also be used. canal middleware can monitor the sequence of database execution, and then update the cache

Topics: Database Redis Cache