Graphical Redis distributed lock, well written!

Click on “Yao Dao Source Code” above and select “Set as Star”

Does she care about the front wave or the back wave?

A wave that can wave is a good wave!

Update articles every day at 10:33, lose a million bits of hair every day…

Source code boutique column

  • Original | Java 2021 Super God Road, very liver~

  • An open source project with detailed annotations in Chinese

  • RPC framework Dubbo source code analysis

  • Network application framework Netty source code analysis

  • Message middleware RocketMQ source code analysis

  • Database middleware Sharding-JDBC and MyCAT source code analysis

  • Job Scheduling Middleware Elastic-Job Source Code Analysis

  • Distributed transaction middleware TCC-Transaction source code analysis

  • Eureka and Hystrix source code analysis

  • Java Concurrency Source Code

Source: blog.csdn.net/zhangkaixuan456/

article/details/110679617

  • Fundamental

  • stage one

  • stage two

  • stage three

  • stage four

  • Phase Five – Final Form

  • Redisson

The Evolution of Distributed Locks

Rationale

We can go to a place to “occupy a pit” at the same time, and if we occupy it, execute the logic. Otherwise, you must wait until the lock is released. “Zhankeng” can go to redis, to the database, and to any place that everyone can access. Waiting can spin that way.

Background management system + user applet based on Spring Boot + MyBatis Plus + Vue & amp; Element, supports RBAC dynamic permissions, multi-tenancy, data permissions, workflow, three-party login, payment, SMS, mall and other functions

  • Project address: https://gitee.com/zhijiantianya/ruoyi-vue-pro

  • Video tutorial: https://doc.iocoder.cn/video/

Phase 1

2992afe21ede419149c3882c5d13c043.png

public Map<String, List<Catalog2Vo>> getCatalogJsonDbWithRedisLock() {
        //stage one
        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", "111");
        //Obtain the lock and execute the business
        if (lock) {
            Map<String, List<Catalog2Vo>> categoriesDb = getCategoryMap();
            //Delete the lock, if an error or downtime is reported before then, it will cause a deadlock
            stringRedisTemplate.delete("lock");
            return categoriesDb;
        } else {
            // Did not get the lock, wait 100ms to try again
            try {
                Thread. sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return getCatalogJsonDbWithRedisLock();
        }
    }
 
public Map<String, List<Catalog2Vo>> getCategoryMap() {
        ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
        String catalogJson = ops. get("catalogJson");
        if (StringUtils. isEmpty(catalogJson)) {
            System.out.println("Cache miss, ready to query the database...");
            Map<String, List<Catalog2Vo>> categoriesDb= getCategoriesDb();
            String toJSONString = JSON.toJSONString(categoriesDb);
            ops.set("catalogJson", toJSONString);
            return categoriesDb;
        }
        System.out.println("cache hit...");
        Map<String, List<Catalog2Vo>> listMap = JSON. parseObject(catalogJson, new TypeReference<Map<String, List<Catalog2Vo>>>() {});
        return listMap;
    }

Problem: Setnx has taken up the position, the business code is abnormal or the program crashes during the page process. The deletion lock logic is not executed, which causes a deadlock

Solution: Set the automatic expiration of the lock, even if it is not deleted, it will be deleted automatically.

Background management system + user applet based on Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & amp; Element, supporting RBAC dynamic permissions, multi-tenancy, data permissions, workflow, three-party login, payment, SMS, mall and other functions

  • Project address: https://gitee.com/zhijiantianya/yudao-cloud

  • Video tutorial: https://doc.iocoder.cn/video/

Phase 2

74a4547436418aef342031d8291dd853.png

public Map<String, List<Catalog2Vo>> getCatalogJsonDbWithRedisLock() {
        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", "111");
        if (lock) {
            //Set the expiration time
            stringRedisTemplate. expire("lock", 30, TimeUnit. SECONDS);
            Map<String, List<Catalog2Vo>> categoriesDb = getCategoryMap();
            stringRedisTemplate.delete("lock");
            return categoriesDb;
        } else {
            try {
                Thread. sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return getCatalogJsonDbWithRedisLock();
        }
    }

Problem: setnx is set, and I am about to set the expiration time, and the system crashes. Deadlocked again.

Solution: Setting expiration time and placeholder must be atomic. Redis supports the use of the setnx ex command.

Stage Three

06e3e053a9f5e366fbacc0f9080dfe3e.png

public Map<String, List<Catalog2Vo>> getCatalogJsonDbWithRedisLock() {
    //Set the expiration time while locking, the two are atomic operations
    Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", "1111",5, TimeUnit.SECONDS);
    if (lock) {
        Map<String, List<Catalog2Vo>> categoriesDb = getCategoryMap();
        //Simulate a long business execution time
        try {
            Thread. sleep(6000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        stringRedisTemplate.delete("lock");
        return categoriesDb;
    } else {
        try {
            Thread. sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return getCatalogJsonDbWithRedisLock();
    }
}

Question: Delete the lock directly? ? ? If the lock itself expires due to a long business time, we delete it directly, and it is possible to delete the lock that others are holding.

Solution: When occupying a lock, the value is specified as uuid, and each person matches their own lock to delete it.

Phase Four

644793269807a814588d5e8a35ea996b.png

public Map<String, List<Catalog2Vo>> getCatalogJsonDbWithRedisLock() {
        String uuid = UUID. randomUUID(). toString();
        ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
      //Set a unique uuid for the current lock, and delete the lock only when the uuid is the same
        Boolean lock = ops.setIfAbsent("lock", uuid,5, TimeUnit.SECONDS);
        if (lock) {
            Map<String, List<Catalog2Vo>> categoriesDb = getCategoryMap();
            String lockValue = ops. get("lock");
            if (lockValue. equals(uuid)) {
                try {
                    Thread. sleep(6000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                stringRedisTemplate.delete("lock");
            }
            return categoriesDb;
        } else {
            try {
                Thread. sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return getCatalogJsonDbWithRedisLock();
        }
    }

Problem: If it happens to be judged to be the current value, when the lock is about to be deleted, the lock has expired and someone else has set it to a new value. Then what we delete is someone else’s lock

Solution: Deleting locks must ensure atomicity. Done using redis + Lua script.

Phase Five – Final Form

f493fb6d00fcf1c56325a09521020fb2.png

public Map<String, List<Catalog2Vo>> getCatalogJsonDbWithRedisLock() {
        String uuid = UUID. randomUUID(). toString();
        ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
        Boolean lock = ops.setIfAbsent("lock", uuid,5, TimeUnit.SECONDS);
        if (lock) {
            Map<String, List<Catalog2Vo>> categoriesDb = getCategoryMap();
            String lockValue = ops. get("lock");
            String script = "if redis. call("get",KEYS[1]) == ARGV[1] then\\
" +
                    " return redis. call("del",KEYS[1])\\
" +
                    "else\\
" +
                    " return 0\\
" +
                    "end";
            stringRedisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList("lock"), lockValue);
            return categoriesDb;
        } else {
            try {
                Thread. sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return getCatalogJsonDbWithRedisLock();
        }
    }

Guarantee the atomicity of locking [occupancy + expiration time] and deleting locks [judgment + deletion]. The more difficult thing is the automatic renewal of the lock.

Redisson

Redisson is a Java in-memory data grid (In-Memory Data Grid) implemented on the basis of Redis. It not only provides a series of distributed common Java objects, but also provides many distributed services.

These include (BitSet, Set, Multimap, SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, AtomicLong, CountDownLatch, Publish / Subscribe, Bloom filter, Remote service, Spring cache, Executor service, Live Object service , Scheduler service)

Redisson provides the easiest and most convenient way to use Redis. The purpose of Redisson is to promote users’ separation of concerns about Redis (Separation of Concern), so that users can focus more on processing business logic.

For more information, please refer to the official documentation:

https://github.com/redisson/redisson/wiki

Welcome to join my knowledge planet, discuss architecture and exchange source code together. How to join, Long press the QR code below:

445a2e8233dc55ea546b8ca099179bf2.png

The source code has been updated on Knowledge Planet and the analysis is as follows:

3b6d188fa844070db54d60450bca4d87.jpeg

dc79ebc83e4b2e94a387b60d80b89403.jpeg

4f821c5af7d1cde9901973f279c5c59b.jpeg

dc6a8345e3581d706b487635f740f2d4.jpeg

The recently updated series “Introduction to Taro SpringBoot 2.X” has more than 101 articles, covering MyBatis, Redis, MongoDB, ES, sub-database and sub-table, read-write separation, SpringMVC, Webflux, permissions, WebSocket, Dubbo, RabbitMQ, RocketMQ , Kafka, performance testing, etc.

Provides a SpringBoot example with nearly 3W lines of code, and an e-commerce microservice project with more than 4W lines of code.

How to get it: Click “Looking“, follow the official account and reply to 666 to receive, more content will be provided one after another.

If the article is helpful, please read it and forward it.
Thank you for your support (*^__^*)