当前位置:网站首页>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";
}
}
边栏推荐
- C # realizes the login interface, and the password asterisk is displayed (hide the input password)
- Redis主从、哨兵、集群模式介绍
- Functional modules and application scenarios covered by the productization of user portraits
- 在MapReduce中利用MultipleOutputs输出多个文件
- Besides lying flat, what else can a 27 year old do in life?
- Kubernetes帶你從頭到尾捋一遍
- 4-20-4-23 concurrent server, TCP state transition;
- MySQL reports an error: [error] mysqld: file '/ mysql-bin. 010228‘ not found (Errcode: 2 “No such file or directory“)
- CentOS7部署哨兵Redis(带架构图,清晰易懂)
- [engine development] rendering architecture and advanced graphics programming
猜你喜欢
C string format (decimal point retention / decimal conversion, etc.)
[probably the most complete in Chinese] pushgateway entry notes
【可能是全中文网最全】pushgateway入门笔记
5.4-5.5
第04章_逻辑架构
5-1 blocking / non blocking, synchronous / asynchronous
How can entrepreneurial teams implement agile testing to improve quality and efficiency? Voice network developer entrepreneurship lecture Vol.03
视觉上位系统设计开发(halcon-winform)-4.通信管理
Pytorch深度学习和目标检测实战笔记
Matplotlib drawing label cannot display Chinese problems
随机推荐
[combinatorics] permutation and combination (set permutation, step-by-step processing example)
Yolov5系列(一)——网络可视化工具netron
Yolov5 advanced 8 format conversion between high and low versions
Neon global and Chinese markets 2022-2028: Research Report on technology, participants, trends, market size and share
Global and Chinese markets for transparent OLED displays 2022-2028: Research Report on technology, participants, trends, market size and share
[opengl] advanced chapter of texture - principle of flowmap
Solve the problem that pushgateway data will be overwritten by multiple push
【注意力机制】【首篇ViT】DETR,End-to-End Object Detection with Transformers网络的主要组成是CNN和Transformer
[graphics] adaptive shadow map
Idea does not specify an output path for the module
[ue4] material and shader permutation
【日常训练】395. 至少有 K 个重复字符的最长子串
SQL server安装位置改不了
5-1 blocking / non blocking, synchronous / asynchronous
解决pushgateway数据多次推送会覆盖的问题
基于SVN分支开发模式流程浅析
视觉上位系统设计开发(halcon-winform)-5.相机
Global and Chinese market of lighting control components 2022-2028: Research Report on technology, participants, trends, market size and share
【可能是全中文网最全】pushgateway入门笔记
[transformer] Introduction - the original author of Harvard NLP presented the annotated transformer in the form of line by line implementation in early 2018