Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to solve the problem of High concurrency second kill with Redis Lock

2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

It is believed that many inexperienced people do not know what to do about how to solve the problem of high concurrency second kill with Redis lock. Therefore, this paper summarizes the causes and solutions of the problem. Through this article, I hope you can solve this problem.

1 lock in stand-alone environment

Save the quantity of goods in Redis. Each user needs to check the number of items in Redis (instead of mysql database) before snapping up. Regardless of transaction), if the quantity of the goods is greater than 0, it proves that the goods are in stock. Then we are doing inventory deduction and the next operation. Because of the multithreading concurrency problem, we have to use synchronous code blocks inside the get () method. This ensures the atomicity of inventory query and inventory reduction operations.

Package springbootdemo.demo.controller;/* * @ auther * @ mail dfsn19970313@foxmail.com * @ date 2020-01-13 11:19 * @ notify * @ version 1.0 * / import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class RedisLock {@ Autowired private RedisTemplate redisTemplate @ GetMapping (value = "buy") public String get () {synchronized (this) {String phone = redisTemplate.opsForValue () .get ("phone"); Integer count = Integer.valueOf (phone); if (count > 0) {redisTemplate.opsForValue () .set ("phone", String.valueOf (count-1)) System.out.println ("grab" + count + "item");} return ";} 2 use Redis locks in distributed cases.

However, due to the rise of business, the number of concurrency has increased. The company had to make a copy of the original system and put it on a new server. Then use nginx for load balancing. The Apache JMeter tool is used here to simulate a high concurrency environment.

Obviously, today's thread locks don't work. So we need to change the lock, which must not have any coupling with the two systems.

API that uses Redies sets a key if key does not exist. This key is the lock we use now. When each thread comes here, the lock is set first. if it fails to set the lock, it indicates that the current thread has acquired the lock and returns. In the end, we threw an exception to reduce inventory and other businesses without releasing the lock. The operation of releasing the lock is placed in the finally code block. It looks perfect.

Package springbootdemo.demo.controller;/* * @ auther * @ mail dfsn19970313@foxmail.com * @ date 2020-01-13 11:19 * @ notify * @ version 1.0 * / import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class RedisLock {@ Autowired private RedisTemplate redisTemplate @ GetMapping (value = "buy") public String get () {Boolean phoneLock = redisTemplate.opsForValue () .setIfAbsent ("phoneLock", ""); if (! phoneLock) {return ";} try {String phone = redisTemplate.opsForValue () .get (" phone "); Integer count = Integer.valueOf (phone) If (count > 0) {redisTemplate.opsForValue () .set ("phone", String.valueOf (count-1)); System.out.println ("grab" + count + "number of goods");}} finally {redisTemplate.delete ("phoneLock");} return "" }} 3 one service is down, so the lock cannot be released.

If an exception is thrown in try and the lock is entered into finally, the lock will still be released and will not affect other threads to acquire the lock. If an exception is also thrown in finally, or if the service is shut down directly in finally, then other services will never acquire the lock again. In the end, the goods could not be sold.

Package springbootdemo.demo.controller;/* * @ auther * @ mail dfsn19970313@foxmail.com * @ date 2020-01-13 11:19 * @ notify * @ version 1.0 * / import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class RedisLock {@ Autowired private RedisTemplate redisTemplate @ GetMapping (value = "buy") public String get () {int I = 0; Boolean phoneLock = redisTemplate.opsForValue () .setIfAbsent ("phoneLock", ""); if (! phoneLock) {return ";} try {String phone = redisTemplate.opsForValue () .get (" phone "); Integer count = Integer.valueOf (phone) If (count > 0) {I = count; redisTemplate.opsForValue (). Set ("phone", String.valueOf (count-1)); System.out.println ("grab" + count + "item");} finally {if (I = = 20) {System.exit (0) } redisTemplate.delete ("phoneLock");} return ";}} 4 add expiration time to each lock

The problem is that if an accident occurs, the lock cannot be released. Here we are introducing the API of Redis to set the expiration time of key. In this way, if the thread that acquires the lock does not have time to release the lock under any circumstances, it will automatically release the lock when the key time of Redis is up. But there's still a problem.

If the lock is released after the key expires, but the current thread does not finish executing. Then the other thread will get the lock and continue to snap up the goods, while the slower thread will release someone else's lock after execution. Cause the lock to fail!

Package springbootdemo.demo.controller;/* * @ auther teenager * @ mail dfsn19970313@foxmail.com * @ date 2020-01-13 11:19 * @ notify * @ version 1.0 * / import javafx.concurrent.Task;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;import java.util.Timer;import java.util.TimerTask Import java.util.concurrent.TimeUnit;@RestControllerpublic class RedisLock {@ Autowired private RedisTemplate redisTemplate; @ GetMapping (value = "buy") public String get () {Boolean phoneLock = redisTemplate.opsForValue () .setIfAbsent ("phoneLock", "", 3, TimeUnit.SECONDS); if (! phoneLock) {return ";} try {String phone = redisTemplate.opsForValue () .get (" phone ") Integer count = Integer.valueOf (phone); if (count > 0) {try {Thread.sleep (999999999L);} catch (Exception e) {} redisTemplate.opsForValue (). Set ("phone", String.valueOf (count-1)) System.out.println ("grab" + count + "item");}} finally {redisTemplate.delete ("phoneLock");} return ";}} 5 prolong the expiration time of the lock to solve the lock failure

The problem is that when a thread's key has expired, but the thread's task is really not finished, the transaction does not end. But the lock is gone. Now we have to extend the lock time. When judging that the goods are in stock, create a thread to renew the life of key as soon as possible.

Prevent key from expiring. Then at the end of the transaction, stop the timer and release the lock.

Package springbootdemo.demo.controller;/* * @ auther teenager * @ mail dfsn19970313@foxmail.com * @ date 2020-01-13 11:19 * @ notify * @ version 1.0 * / import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;import java.util.Timer;import java.util.TimerTask;import java.util.concurrent.TimeUnit @ RestControllerpublic class RedisLock {@ Autowired private RedisTemplate redisTemplate; @ GetMapping (value = "buy") public String get () {Boolean phoneLock = redisTemplate.opsForValue () .setIfAbsent ("phoneLock", "", 3, TimeUnit.SECONDS); if (! phoneLock) {return ";} Timer timer = null; try {String phone = redisTemplate.opsForValue (). Get (" phone ") Integer count = Integer.valueOf (phone); if (count > 0) {timer = new Timer (); timer.schedule (new TimerTask () {@ Override public void run () {redisTemplate.opsForValue () .set ("phoneLock", "", 3, TimeUnit.SECONDS) }}, 0,1); redisTemplate.opsForValue (). Set ("phone", String.valueOf (count-1)); System.out.println ("grab" + count + "number of goods");}} finally {if (timer! = null) {timer.cancel () } redisTemplate.delete ("phoneLock");} return "";}} 6 simplify the code using Redisson

In step 5, our code is perfect and there will be no high concurrency problems. But the code is too redundant, in order to use the Redis lock, we need to set a fixed-length key, and then delete the key when the purchase is complete. However, in order to prevent key from expiring prematurely, we have to add a new thread to perform scheduled tasks. Next we can use the Redissson framework to simplify the code. The getLock () method replaces Redis's setIfAbsent (), and lock () sets the expiration time. Eventually we release the lock at the end of the transaction. The operation of extending the lock is done for us by the Redisson framework, which uses polling to see if the key is out of date.

Automatically reset the key expiration time of Redis when the transaction is not completed

Package springbootdemo.demo.controller;/* * @ auther teenager * @ mail dfsn19970313@foxmail.com * @ date 2020-01-13 11:19 * @ notify * @ version 1.0 * / import org.redisson.Redisson;import org.redisson.api.RLock;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;import java.util.Timer Import java.util.TimerTask;import java.util.concurrent.TimeUnit;@RestControllerpublic class RedissonLock {@ Autowired private RedisTemplate redisTemplate; @ Autowired private Redisson redisson; @ GetMapping (value = "buy2") public String get () {RLock phoneLock = redisson.getLock ("phoneLock"); phoneLock.lock (3, TimeUnit.SECONDS); try {String phone = redisTemplate.opsForValue () .get ("phone") Integer count = Integer.valueOf (phone); if (count > 0) {redisTemplate.opsForValue (). Set ("phone", String.valueOf (count-1)); System.out.println ("grab" + count + "number of goods");}} finally {phoneLock.unlock ();} return "" }} after reading the above, have you mastered how Redis locks solve the problem of high concurrent second kill? If you want to learn more skills or want to know more about it, you are welcome to follow the industry information channel, thank you for reading!

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.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report