What is the purpose of redis distributed lock to solve?
In order to solve the problem that synchronized can not achieve synchronization in distributed situations, because synchronized synchronization is the method of one jvm, which can not be done by multiple JVMs.
1. Implement the inventory reduction scenario of redis basic business
Integer stock = (Integer)redisTemplate.opsForValue().get("stock"); log.info("Remaining"+stock); if (stock<=0){ log.info("Rush purchase failed"); } redisTemplate.opsForValue().set("stock",stock-1);
This code implements the basic business scenario. When there is concurrency, multiple threads will get the same stock, resulting in repeated minus 1 operations and oversold
2. Realize the basic functions of redis distributed lock
String key = "lockKey"; Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, "1"); //setnx key 1 //If locking fails, it indicates that other threads have been locked if (!flag){ log.info("The system is abnormal. Please try again later"); return; } try{ Integer stock = (Integer)redisTemplate.opsForValue().get("stock"); //get key if (stock<=0){ log.info("Rush purchase failed"); return; } Long stock1 = redisTemplate.opsForValue().decrement("stock"); //decr key log.info("Remaining"+stock1); }finally { //Prevent code errors from causing distributed locks not to be deleted redisTemplate.delete(key); //del key }
This code solves the oversold problem, but if the application goes down during business processing, the lock will be locked.
3. What about application downtime?
The solution is to add the expiration time to the redis key. Even after a period of downtime, redis will release the distributed lock
String key = "lockKey"; //Writing method 1 // redisTemplate.opsForValue().setIfAbsent(key, "1"); //setnx key 1 // redisTemplate.expire(key,10,TimeUnit.SECONDS); //expire key 10 //Writing method 2 //A redis command is atomic and better than the above method Boolean flag = redisTemplate.opsForValue().setIfAbsent(key,"1",10, TimeUnit.SECONDS); //setnx key 1 10 //If locking fails, it indicates that other threads have been locked if (!flag){ log.info("The system is abnormal. Please try again later"); return; } try{ Integer stock = (Integer)redisTemplate.opsForValue().get("stock"); //get key if (stock<=0){ log.info("Rush purchase failed"); return; } Long stock1 = redisTemplate.opsForValue().decrement("stock"); //decr key log.info("Remaining"+stock1); }finally { //Prevent code errors from causing distributed locks not to be deleted redisTemplate.delete(key); //del key }
At this time, A new problem will appear. Thread A has not finished the business and the lock is released after expiration. At this time, thread B will add A new lock, and thread A will release the lock of thread B after completing the business. There is A problem with this lock.
One solution is to increase the lock a little longer, and only allow to delete the lock added by its own thread, which is fully applicable to ordinary business scenarios. However, in the case of high concurrency, for example, if the expiration time of 1 minute is set, but the system goes down after locking, this method is locked, which is extremely fatal to the system.
There is a problem of how long the expiration time is set in high concurrency scenarios. In this case, a lock renewal function needs to be added. When the lock expires and the business has not been executed, set an expiration time for the lock until the current thread business synchronization method is executed.
4. redisson is used to realize this function
The framework has solved these problems for us. I'll just use it briefly here. For specific usage, please go to the official website.
Import dependency:
<dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.11.1</version> </dependency>
Configuration class:
@Bean public Redisson redisson() { Config config = new Config(); // This is stand-alone mode config.useSingleServer().setAddress("redis://127.0.0.1:6379s").setDatabase(0); return (Redisson) Redisson.create(config); }
Synchronization code:
String key = "lockKey"; //Get reisson lock RLock lock = redisson.getLock(key); try{ //The default lock is unfair lock, and the lock time of 30s will be automatically renewed lock.lock(); Integer stock = (Integer)redisTemplate.opsForValue().get("stock"); //get key if (stock<=0){ log.info("Rush purchase failed"); return; } Long stock1 = redisTemplate.opsForValue().decrement("stock"); //decr key log.info("Remaining"+stock1); }finally { lock.unlock(); }
In this way, redis distributed lock is used to solve the concurrency problem.