Redis [3] interview question: design distributed locks + code to realize native distributed locks

You and other students 2022-02-13 08:09:06 阅读数:509

redis interview question design distributed

This article has been arranged since childhood d Class notes and java Advanced warehouse , any similarity , Most of them are written by others

java Advanced warehouse :https://doocs.github.io/advanced-java/#/

Redis[3] Interview questions : Design distributed locks + The code implements the native distributed lock

The purpose of distributed lock is to ensure that only one client can operate on shared resources at the same time

It can be used to limit the number of coupons 、 These scenarios of oversold inventory

In order to prevent multiple processes from interfering with each other in a distributed system , We need a distributed coordination technology to schedule these processes

Use the mutual exclusion mechanism to control the access of shared resources , This is the problem of distributed lock

It can be realized by locking :

  • Local lock :synchronize、lock etc. , Locked in the current process , There are still problems under cluster deployment
  • Distributed lock :redis、zookeeper Such as implementation , Although it's still locked , But lock tags shared by multiple processes , It can be used Redis、Zookeeper、Mysql And so on.

Question 1 Make sure the lock can be released

The lock needs to be guaranteed to be released , Such as client crash or network outage

Can be based on redis Implement distributed locks

  • Lock SETNX key value
setnx That means SET if Not Exists, There are two parameters setnx(key, value), The method is atomic
If key non-existent , Set current key success , return 1;
If at present key Already exist , Set current key Failure , return 0
  • Unlock del (key)
 The thread that gets the lock completes the task , Need to release lock , So that other threads can enter , call del(key)
  • Configure lock timeout expire (key,30s)
 Client crash or network outage , Resources will be locked forever , That's deadlock , So we need to give key Configure expiration time , To ensure that even if not explicitly released , This lock should also be released automatically after a certain time
  • Comprehensive pseudo code
methodA(){
String key = "coupon_66"
if(setnx(key,1) == 1){ //1
expire(key,30,TimeUnit.MILLISECONDS)//2
try {
// Do the corresponding business logic
// Query whether the user has received vouchers
// If not, deduct inventory
// Add a voucher collection record
} finally {
del(key)
}
}else{
// sleep 100 millisecond , Then call this method
methodA()
}
}

For the above code

if 1 The statement at has been completed , Downtime at this time , The expiration time cannot be set , This resource will always hold the lock , The lock cannot be released

Therefore, you need to set the expiration time when locking , Either lock and set the expiration time , Or leave it unlocked

The solution is :
Use atomic commands : Set and configure expiration time setnx / setex

 Such as : set key 1 ex 30 nx
java Inside redisTemplate.opsForValue().
setIfAbsent("seckill_1","success",30,TimeUnit.MILLISECONDS)

Question two : Business timeout , There are other threads deleted by mistake

A business takes a long time , Current thread a Incomplete operation , Threads b Got the lock , After a while a completion of enforcement , Threads a Will delete the thread b Lock added .

Solution :

Can be in del Make a decision before releasing the lock , Verify whether the current lock is a lock added by itself , that value It should be the ID of the current thread or uuid


String key = "coupon_66"
String value = Thread.currentThread().getId()
if(setnx(key,value) == 1){
expire(key,30,TimeUnit.MILLISECONDS)
try {
// Do the corresponding business logic
} finally {
// Delete lock , Determine whether it is added by the current thread
if(get(key).equals(value)){
// There are also time intervals ------------- 1
del(key)
}
}
}else{
// sleep 100 millisecond , Then call this method
}
  • Further refine the false deletion
    • When a thread A When a normal value is obtained , The return band code determines that the period lock has expired , Threads B Just reset the new value , Threads A There's judgment over there value It's your own logo , And then call del Method , The result is to delete the newly set thread B Value
    • The core is to judge and delete commands Not atomic operations
  • summary
    • Lock + Configure expiration time : Guarantee atomic operation
    • Unlock : Prevent accidental deletion of 、 Also ensure atomic operation

Question 3 : Determine whether the lock is your own lock and There is a time interval between deleting locks

Solution :

Atomicity of multiple commands : use lua Script +redis, because 【 Judge and delete 】 yes lua Script execution , So either it's all successful , Or it's a total failure

  • All the code
/** * Native distributed locks Start * 1、 Atomic locking Set expiration time , Prevent downtime deadlock * 2、 Atoms unlock : You need to judge whether it's your own lock */
@RestController
@RequestMapping("/api/v1/coupon")
public class CouponController {

@Autowired
private StringRedisTemplate redisTemplate;
@GetMapping("add")
public JsonData saveCoupon(@RequestParam(value = "coupon_id",required = true) int couponId){

// Prevent other threads from deleting by mistake 
String uuid = UUID.randomUUID().toString();
String lockKey = "lock:coupon:"+couponId;
lock(couponId,uuid,lockKey);
return JsonData.buildSuccess();
}
private void lock(int couponId,String uuid,String lockKey){

//lua Script 
String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
Boolean nativeLock = redisTemplate.opsForValue().setIfAbsent(lockKey,uuid,Duration.ofSeconds(30));
System.out.println(uuid+" Locked state :"+nativeLock);
if(nativeLock){

// Locking success 
try{

//TODO Do relevant business logic 
TimeUnit.SECONDS.sleep(10L);
} catch (InterruptedException e) {

} finally {

// Unlock 
Long result = redisTemplate.execute( new DefaultRedisScript<>(script,Long.class),Arrays.asList(lockKey),uuid);
System.out.println(" Unlock state :"+result);
}
}else {

// Spin operation 
try {

System.out.println(" Locking failed , sleep 5 second Spin ");
TimeUnit.MILLISECONDS.sleep(5000);
} catch (InterruptedException e) {
 }
// Sleep for a while and then try to get the lock 
lock(couponId,uuid,lockKey);
}
}
}

Question 4 : How to avoid long business execution time , The lock is out of date

Expiration time of lock , How to realize the automatic renewal of locks perhaps Avoid long business execution time , The lock is out of date ?

  • In the native way , Generally, set the expiration time of the lock longer , such as 10 Minute time

Screenshot of actual operation effect

Access the interface three times , All three threads can get the lock , And release the lock
 Insert picture description here

copyright:author[You and other students],Please bring the original link to reprint, thank you. https://en.javamana.com/2022/02/202202130809035652.html