当前位置:网站首页>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";
}
}
边栏推荐
- Kubernetes - YAML文件解读
- Matplotlib drawing label cannot display Chinese problems
- [ue4] cascading shadow CSM
- 4-33--4-35
- Several sentences extracted from the book "leather bag"
- The state does not change after the assignment of El switch
- Global and Chinese market of transfer case 2022-2028: Research Report on technology, participants, trends, market size and share
- "Seven weapons" in the "treasure chest" of machine learning: Zhou Zhihua leads the publication of the new book "machine learning theory guide"
- 什么是embedding(把物体编码为一个低维稠密向量),pytorch中nn.Embedding原理及使用
- Incluxdb2 buckets create database
猜你喜欢
C string format (decimal point retention / decimal conversion, etc.)
Basic SQL tutorial
MySQL reports an error: [error] mysqld: file '/ mysql-bin. 010228‘ not found (Errcode: 2 “No such file or directory“)
mysql innodb 存储引擎的特性—行锁剖析
视觉上位系统设计开发(halcon-winform)-4.通信管理
QT program font becomes larger on computers with different resolutions, overflowing controls
Composite type (custom type)
Remote server background hangs nohup
零拷贝底层剖析
【pytorch学习笔记】Datasets and Dataloaders
随机推荐
Use of Tex editor
Stress test WebService with JMeter
Devaxpress: range selection control rangecontrol uses
Kubernetes - YAML文件解读
QT - draw something else
How can entrepreneurial teams implement agile testing to improve quality and efficiency? Voice network developer entrepreneurship lecture Vol.03
Matlab r2011b neural network toolbox precautions
"Seven weapons" in the "treasure chest" of machine learning: Zhou Zhihua leads the publication of the new book "machine learning theory guide"
Influxdb2 sources add data sources
[graphics] adaptive shadow map
[transform] [NLP] first proposed transformer. The 2017 paper "attention is all you need" by Google brain team
QT program font becomes larger on computers with different resolutions, overflowing controls
基础SQL教程
Yolov5系列(一)——網絡可視化工具netron
Construction of operation and maintenance system
Apache ant extension tutorial
CentOS7部署哨兵Redis(带架构图,清晰易懂)
Global and Chinese market of lighting control components 2022-2028: Research Report on technology, participants, trends, market size and share
Solve the problem that pushgateway data will be overwritten by multiple push
Série yolov5 (i) - - netron, un outil de visualisation de réseau