当前位置:网站首页>Redis lock Optimization Practice issued by gaobingfa
Redis lock Optimization Practice issued by gaobingfa
2022-07-03 15:11:00 【Miss, do you fix the light bulb】
background : Reduce inventory by simulating the second kill activity of the mall , Given inventory 300
Code :
@RestController
public class IndexControllerCOPY {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RequestMapping("/deduct_stock")
public String deductStock() {
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock")); // jedis.get("stock")
if (stock > 0) {
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + ""); // jedis.set(key,value)
System.out.println(" Deduction succeeded , Surplus stock :" + realStock);
} else {
System.out.println(" Deduction failed , Insufficient inventory ");
}
// }
return "end";
}
}
Prerequisite : Simulated cluster deployment mode , Inventory operation , So start two Tomcat example ,8080 and 9080 port . And use nginx Load balancing , The configuration is shown in the figure below ;

start-up redis, Then start two more instances , use jmeter Pressure measurement , By visiting 80 port , Give Way nginx Listen to the corresponding service execution request ,1 second 200 A request to try , The pressure measurement configuration is shown in the figure below :

View results :
It can be found that the inventory is repeatedly deducted , Sell more than one commodity , Oversold , Obviously not . How to solve this problem at this time ? Some Taoist friends may say that this is because there is no lock , So in 8080 I saw oversold in the server , Good to meet your needs , I locked it in the test :
@RestController
public class IndexControllerCOPY {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RequestMapping("/deduct_stock")
public String deductStock() {
synchronized (this){
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock")); // jedis.get("stock")
if (stock > 0) {
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + ""); // jedis.set(key,value)
System.out.println(" Deduction succeeded , Surplus stock :" + realStock);
} else {
System.out.println(" Deduction failed , Insufficient inventory ");
}
}
return "end";
}
}


You can find : The first 198 stay 8080 and 9080 Services have appeared . So understand , Yours synchronize Just lock the thread , I am in cluster mode , It is impossible for your lock to be added to another process . as for 8080 Why do services appear 2 strip 197 The record of , Smart, guess for yourself , Very simple. (8080 Internal volume ,9080 Fish every day ,8080 Human modified value , Covered by Mr. fishing ).
Well, I'm busy today , Further optimization and continue next time , If someone needs to see it , I'll find time to improve .
Today, I will continue to talk about how to ensure data consistency in the cluster mode
《-----------------------------------------------------------------------------------------》
So much foreshadowing has been done to tell everyone : Cannot pass in cluster or distributed mode synchronize perhaps ReentrantLock To maintain data consistency
There are many solutions , Can pass zookeeper Or redis Of setnx Order to solve
Here I use redis Examples continue :
First of all, let's set the quantity of goods to 200, Modify the code to use redis Of setnx command ,jmeter Do pressure test .

Observations :8080 The server 
9090 The server :
redis The remaining inventory of this commodity :
It can be seen that : The two processes did not sell the same goods , And the negotiated quantity finally sold 103+97=200, It's completely in line . stay 5 Seconds 2000 The consistency of data is guaranteed under concurrency .
Of course, there is still room for optimization of the code here :
1. If the program goes wrong , The lock was not released in time , So we put the logic of releasing locks in finally Block of code
package com.redisson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class IndexControllerCOPY {
@Autowired
private StringRedisTemplate stringRedisTemplate;
private volatile int count;
@RequestMapping("/deduct_stock")
public String deductStock() {
// synchronized (this){
// redis Distributed locks for setnx
Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock","value"); // Distributed lock jedis.setnx(k,v)
try {
// If the lock is obtained, it will return true
if (!lock){
return "error";
}
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock")); // jedis.get("stock")
if (stock > 0) {
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + ""); // jedis.set(key,value)
System.out.println(" Deduction succeeded , Surplus stock :" + realStock);
count++;
} else {
System.out.println(" Deduction failed , Insufficient inventory , Total sales "+count);
}
}finally {
// You can't release others' locks without obtaining them
if (lock){
stringRedisTemplate.delete("lock");
}
}
return "end";
}
}
2. For example, if 8080 The service is getting the lock , The business logic has not yet handled the release of locks , The service outage , The lock will not be released , So you can add an expiration time
3. But after adding an expiration time, there will be two other problems , One 、( Threads A The processing time after obtaining the lock is longer than the expiration time of the lock , At this time, another thread B Will get all , At this time, the business of the previous thread is finished , To release the lock is B Of , It reciprocates greatly in turn GUB), The solution is to let each thread release its own lock , How to distinguish ? It's simple , Each locked thread sets a local variable value value , When you want to release the lock, take it out key-value, Judge value Whether it is equal to the value of the current thread stack , Equal release .
package com.redisson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@RestController
public class IndexControllerCOPY {
@Autowired
private StringRedisTemplate stringRedisTemplate;
private volatile int count;
@RequestMapping("/deduct_stock")
public String deductStock() {
String uuid= UUID.randomUUID().toString();
// synchronized (this){
// redis Distributed locks for setnx
Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock","value",10, TimeUnit.MILLISECONDS); // Distributed lock jedis.setnx(k,v)
try {
// If the lock is obtained, it will return true
if (!lock){
return "error";
}
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock")); // jedis.get("stock")
if (stock > 0) {
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + ""); // jedis.set(key,value)
System.out.println(" Deduction succeeded , Surplus stock :" + realStock);
count++;
} else {
System.out.println(" Deduction failed , Insufficient inventory , Total sales "+count);
}
}finally {
// You can't release others' locks without obtaining them
if (lock && uuid.equals(stringRedisTemplate.opsForValue().get("lock"))){
stringRedisTemplate.delete("lock");
}
}
return "end";
}
}
Two 、( Or thread? A The business is not finished , It's time for the lock to expire , Threads B Got the lock , Not good either. , So we need to extend the life of lock ) solve the problem : Ensure that on the premise of adding the expiration time , It is also necessary to ensure that the lock is released after the warehouse inventory operation . Lock for life , Give a child thread a scheduled task iterm,
// After a period of time, check whether the main thread has finished executing , No, just add another expiration time
There is also a good framework here redission It can perfectly solve all the above problems . programme :
// There is an implemented client toolkit
// Import
//
// org.redisson
// redisson
// 3.6.5
//
Tired , Later, I'll make up redisson The first part gives 
package com.redisson;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class IndexController {
@Autowired
private Redisson redisson;
@Autowired
private StringRedisTemplate stringRedisTemplate;
private volatile int count;
@RequestMapping("/deduct_stock")
public String deductStock() {
String lockKey = "product_101";
RLock redissonLock = redisson.getLock(lockKey);
try {
// Lock
redissonLock.lock(); //setIfAbsent(lockKey, clientId, 30, TimeUnit.SECONDS);
// synchronized (this){ Plan one plus synchronized, But it can only solve the situation of stand-alone application in the same process , If distributed deployment is not feasible
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock")); // jedis.get("stock")
if (stock > 0) {
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + ""); // jedis.set(key,value)
count++;
System.out.println(" Deduction succeeded , Surplus stock :" + realStock);
} else {
System.out.println(" Deduction failed , Insufficient inventory , Total sales "+count);
}
// }
} finally {
redissonLock.unlock();
/*if (lock && clientId.equals(stringRedisTemplate.opsForValue().get(lockKey))) { stringRedisTemplate.delete(lockKey); }*/
}
return "end";
}
}
边栏推荐
- What are the composite types of Blackhorse Clickhouse, an OLAP database recognized in the industry
- Neon global and Chinese markets 2022-2028: Research Report on technology, participants, trends, market size and share
- Center and drag linked global and Chinese markets 2022-2028: Research Report on technology, participants, trends, market size and share
- QT program font becomes larger on computers with different resolutions, overflowing controls
- Série yolov5 (i) - - netron, un outil de visualisation de réseau
- 官网MapReduce实例代码详细批注
- 什么是one-hot encoding?Pytorch中,将label变成one hot编码的两种方式
- Global and Chinese market of Bus HVAC systems 2022-2028: Research Report on technology, participants, trends, market size and share
- Besides lying flat, what else can a 27 year old do in life?
- 零拷贝底层剖析
猜你喜欢

复合类型(自定义类型)

Vs+qt multithreading implementation -- run and movetothread

Yolov5系列(一)——网络可视化工具netron
![MySQL reports an error: [error] mysqld: file '/ mysql-bin. 010228‘ not found (Errcode: 2 “No such file or directory“)](/img/cd/2e4f5884d034ff704809f476bda288.png)
MySQL reports an error: [error] mysqld: file '/ mysql-bin. 010228‘ not found (Errcode: 2 “No such file or directory“)

There are links in the linked list. Can you walk three steps faster or slower

5.4-5.5

How can entrepreneurial teams implement agile testing to improve quality and efficiency? Voice network developer entrepreneurship lecture Vol.03
![[transform] [practice] use pytoch's torch nn. Multiheadattention to realize self attention](/img/94/a9c7010fe9f14454469609ac4dd871.png)
[transform] [practice] use pytoch's torch nn. Multiheadattention to realize self attention

Composite type (custom type)

redis缓存穿透,缓存击穿,缓存雪崩解决方案
随机推荐
4-33--4-35
Incluxdb2 buckets create database
Yolov5 advanced 8 format conversion between high and low versions
QT - draw something else
406. 根据身高重建队列
What is machine reading comprehension? What are the applications? Finally someone made it clear
Remote server background hangs nohup
What are the composite types of Blackhorse Clickhouse, an OLAP database recognized in the industry
.NET六大设计原则个人白话理解,有误请大神指正
B2020 points candy
高并发下之redis锁优化实战
ASTC texture compression (adaptive scalable texture compression)
Web server code parsing - thread pool
Finally, someone explained the financial risk management clearly
北京共有产权房出租新规实施的租赁案例
视觉上位系统设计开发(halcon-winform)-1.流程节点设计
Mmdetection learning rate and batch_ Size relationship
Dataframe returns the whole row according to the value
PyTorch crop images differentiablly
redis缓存穿透,缓存击穿,缓存雪崩解决方案