In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-04-02 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article introduces the knowledge of "how to use redis distributed locks". In the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!
1. Redis in practical application
Not only can it be used to cache data, but in distributed application development, it is often used as a distributed lock, so why use a distributed lock?
In distributed development, it is explained by the update function of e-commerce inventory. In practical applications, there are many consumers with the same function. If multiple consumers want to consume a piece of data at the same time, if the business logic processing logic is to query out the inventory of goods in redis, and if the first consumer who comes in has obtained the inventory, the inventory reduction operation has not been carried out yet. Relatively late consumers get the inventory of goods, which leads to errors in the data and more data on consumption.
For example, consumer An and consumer B consume the data of producer C1 and producer C2 respectively, and producers all use the same redis database. If producer C1 receives a message from consumer A, it first queries the inventory, and then when it is time to reduce inventory, because producer C2 also queries inventory after receiving the message from consumer B, and because producer C1 has not yet updated the inventory. As a result, the inventory number obtained by producer C2 is dirty data, rather than the updated data of producer C1, which leads to business errors.
If it is not a distributed application, synchronized can be used to prevent inventory update problems, but synchronized is only based on the JVM level, if in different JVM, can not achieve such a function.
@ GetMapping ("getInt0") public String test () {synchronized (this) {/ / get the quantity of the current commodity int productNum = Integer.parseInt (stringRedisTemplate.opsForValue () .get ("product")) / / then subtract 1 / * * a business logic * / if (productNum > 0) {stringRedisTemplate.opsForValue () .set ("product", String.valueOf (productNum-1)); int productNumNow = productNum-1 } else {return "product=0";} int productNumNow = productNum-1; return "success=" + productNumNow;}} 2. How to use the function of redis to realize distributed Lock 2.1 redis distributed Lock thought
If we are familiar with redis, we can think of the command setnx in redis. The function of this command is similar to that of set, but the command of setnx checks whether the same key already exists in redis before storing data, and returns false if so, otherwise, it returns true, so we can use the function of this command to design a distributed lock.
2.1.1 Design ideas:
When requesting an interface with the same function, use the setnx command of redis. If true is returned after using the setnx command, it means that there is no other call to this API at this time, which is equivalent to acquiring the lock, and then you can continue to execute the next business logic. When the business logic is executed, the key is deleted before the data is returned, and then other requests can acquire the lock.
If you use the setnx command, false is returned, indicating that other consumers are calling the API at this time, so you need to wait for other consumers to complete the consumption successfully before acquiring the distributed lock.
2.1.2 implement the code according to the above design idea
Code snippet [1]
@ GetMapping ("getInt1") public String fubushisuo () {/ / setIfAbsent has the same instruction function as the setNx function in the redis command. If the same key already exists in redis, it returns false String lockkey = "yigehaimeirumengdechengxuyuan"; String lockvalue = "yigehaimeirumengdechengxuyuan"; boolean opsForSet = stringRedisTemplate.opsForValue () .setIfAbsent (lockkey,lockvalue) / / if lockkey can be successfully set, it means that the current distributed lock if (! opsForSet) {return "false";} / / gets the quantity of current goods int productNum = Integer.parseInt (stringRedisTemplate.opsForValue () .get ("product")) / / then subtract 1 / * * a business logic * / if (productNum > 0) {stringRedisTemplate.opsForValue (). Set ("product", String.valueOf (productNum-1)); int productNumNow = productNum-1;} else {return "product=0" } / / then release the lock stringRedisTemplate.delete (lockkey); int productNumNow = productNum-1; return "success=" + productNumNow;} 2.1.2.1 reflect on the code snippet [1]
If you use this approach, there will be a deadlock:
What happens when a deadlock occurs:
(1) if the delete () operation cannot be performed when there is an error in the a business logic, so that other requests cannot acquire the distributed lock, the business lockkey always exists in the reids, resulting in the failure of the setnx operation, so the distributed lock cannot be acquired.
(2) the solution is to use try to the business code. Catch operation. If an error occurs, delete the key using finally.
Optimize the code [2]
@ GetMapping ("getInt2") public String fubushisuo2 () {/ / setIfAbsent has the same instruction function as the setNx function in the redis command. If the same key already exists in redis, it returns false String lockkey = "yigehaimeirumengdechengxuyuan"; String lockvalue = "yigehaimeirumengdechengxuyuan"; boolean opsForSet = stringRedisTemplate.opsForValue () .setIfAbsent (lockkey,lockvalue); int productNumNow = 0 / / if lockkey can be successfully set, it means that the distributed lock if (! opsForSet) {return "false";} try {/ / gets the quantity of current goods int productNum = Integer.parseInt (stringRedisTemplate.opsForValue (). Get ("product")) / / then perform the outgoing operation on the goods, that is, subtract 1 / * * b business logic * * / if (productNum > 0) {stringRedisTemplate.opsForValue () .set ("product", String.valueOf (productNum-1)); productNumNow = productNum-1 } else {return "product=0";}} catch (Exception e) {System.out.println (e.getCause ());} finally {/ / then release lock stringRedisTemplate.delete (lockkey);} return "success=" + productNumNow;} 2.1.2.2 reflect on the code [2]
The situation in which the problem occurs:
If this situation also happens, if there are multiple servers running the method
One of the methods acquires the distributed lock, and while running the following business code, the server suddenly goes down, causing the others to be unable to acquire the distributed lock.
Solution: add the expiration time, but the service is down. After the set time, redis can delete the key, so that other servers can be locked normally.
Optimize the code [3]
@ GetMapping ("getInt3") public String fubushisuo3 () {/ / setIfAbsent has the same instruction function as the setNx function in the redis command. If the same key already exists in redis, it returns false String lockkey = "yigehaimeirumengdechengxuyuan"; String lockvalue = "yigehaimeirumengdechengxuyuan"; / / [01] boolean opsForSet = stringRedisTemplate.opsForValue () .setIfAbsent (lockkey,lockvalue) / / set the expiration time to 10 seconds, but if you use this command, there is no atomicity and there may be a downtime before executing expire instead of setting the expiration time. / / [02] stringRedisTemplate.expire (lockkey, Duration.ofSeconds (10)); / / use setIfAbsent (lockkey,lockvalue,10,TimeUnit.SECONDS) The code replaces the above [01], [02] line code Boolean opsForSet = stringRedisTemplate.opsForValue (). SetIfAbsent (lockkey, lockvalue, 10, TimeUnit.SECONDS); int productNumNow = 0; / / if lockkey can be successfully set, it means that the distributed lock if (! opsForSet) {return "false" is currently acquired. } try {/ / get the quantity of the current commodity int productNum = Integer.parseInt (stringRedisTemplate.opsForValue () .get ("product")) / / then perform the outgoing operation on the goods, that is, subtract 1 / * * c business logic * * / if (productNum > 0) {stringRedisTemplate.opsForValue () .set ("product", String.valueOf (productNum-1)); productNumNow = productNum-1 } else {return "product=0";}} catch (Exception e) {System.out.println (e.getCause ());} finally {/ / then release lock stringRedisTemplate.delete (lockkey);} return "success=" + productNumNow } 2.1.2.3 reflection on optimized code [3]
The situation in which the problem occurs:
If the c business logic continues to exceed the setup time, the lockkey in the redis expires
Other users get the lock when they access the method at this time, and at this point, the previous c business logic is executed, but he executes delete and deletes the lcokkey. Caused an error in the distributed lock.
Example: at 12:01:55, an An executes the getInt3 method and successfully acquires the lock, but A cannot complete the business logic after 10 seconds, resulting in the expiration of the lock in redis, while B executes the getint3 method in 11 seconds because key is deleted by A, resulting in B being able to successfully acquire the redis lock. After B acquires the lock, A deletes the key in reids because the execution is complete. But what we notice is that the lock deleted by An is added by B, and the lock of An is deleted by redis itself because it has expired, so this causes C to acquire redis distributed locks if it comes at this time.
Solution: use UUID to generate a random number. When you want to delete (delete) the key in the redis, determine whether it is the UUID set by yourself.
Code optimization [4]
@ GetMapping ("getInt4") public String fubushisuo4 () {/ / setIfAbsent has the same instruction function as the setNx function in the redis command. If the same key already exists in redis, return false String lockkey = "yigehaimeirumengdechengxuyuan"; / / get UUID String lockvalue = UUID.randomUUID (). ToString (); Boolean opsForSet = stringRedisTemplate.opsForValue (). SetIfAbsent (lockkey, lockvalue, 10, TimeUnit.SECONDS); int productNumNow = 0 / / if lockkey can be successfully set, it means that the distributed lock if (! opsForSet) {return "false";} try {/ / gets the quantity of current goods int productNum = Integer.parseInt (stringRedisTemplate.opsForValue (). Get ("product")) / / then perform the outgoing operation on the goods, that is, subtract 1 / * * c business logic * * / if (productNum > 0) {stringRedisTemplate.opsForValue () .set ("product", String.valueOf (productNum-1)); productNumNow = productNum-1 } else {return "product=0";}} catch (Exception e) {System.out.println (e.getCause ());} finally {/ / release lock if (lockvalue==stringRedisTemplate.opsForValue (). Get (lockkey)) {stringRedisTemplate.delete (lockkey) }} return "success=" + productNumNow;} 2.1.2.4 reflection on optimized Code [4]
The situation in which the problem occurs:
At this time, this method is perfect, generally, concurrency is not super large, but the expiration time of key needs to be set according to the time of business execution, so as to prevent key from expiring before the business is finished.
Solution: at present, there are many redis distributed lock frameworks, of which redisson uses more
2.2 using redisson to implement distributed locks
First add the maven dependency of redisson
Org.redisson redisson 3.11.1
Bean configuration of redisson
@ Configurationpublic class RedissonConfigure {@ Bean public Redisson redisson () {Config config = new Config (); config.useSingleServer () .setAddress ("redis://27.196.106.42:6380") .setDatabase (0); return (Redisson) Redisson.create (config);}}
The code to implement the distributed lock is as follows
@ GetMapping ("getInt5") public String fubushisuo5 () {/ / setIfAbsent has the same instruction function as the setNx function in the redis command. If the same key already exists in redis, it returns false String lockkey = "yigehaimeirumengdechengxuyuan"; / / gets UUID RLock lock = redisson.getLock (lockkey); lock.lock (); int productNumNow = 0 / / if lockkey can be set successfully, it means that the current distributed lock try {/ / gets the quantity of the current product int productNum = Integer.parseInt (stringRedisTemplate.opsForValue () .get ("product")). / / then the goods are exported, that is, minus 1 / * * c business logic * * / if (productNum > 0) {stringRedisTemplate.opsForValue (). Set ("product", String.valueOf (productNum-1)); productNumNow = productNum-1;} else {return "product=0" }} catch (Exception e) {System.out.println (e.getCause ());} finally {lock.unlock ();} / then release the lock return "success=" + productNumNow;}
From the aspect, we can see that it is very simple for redisson to implement distributed lock, as long as a few simple commands can achieve the function of distributed lock.
Redisson implements distributed locks as long as the principle is as follows:
Redisson uses the Lua scripting language to make the command atomic. When redisson acquires the lock, it will set the expiration of 30 seconds to press, while redisson will record the thread number of the current request, and then regularly check the status of the thread. If it is still in the execution state, and key is almost out of date, redisson will modify the expiration time of key, generally increasing by 10 seconds. In this way, the expiration time of key can be set dynamically, which makes up for the fragment of optimized code [4].
That's all for "how to use redis distributed Lock". Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.