分布式锁Redis

目录

介绍

解决方案:使用redis实现分布式锁

问:当有人占用锁资源太久怎么办?

 问:当我们上锁setnx key 之后,服务器突然挂了怎么办?

UUID防止误删锁

引入:

解决方案:


 

介绍

分布式锁Redis_第1张图片

在单体应用中,上锁可以,但是当你是分布式集群中,你其他服务器就识别不到这把锁了(因为JVM不能跨服务器访问),所以我们需要一个叫分布式锁的东西——>让所有服务器都能共享这把锁;

解决方案:使用redis实现分布式锁

需要释放锁资源,别人才能拿到这个资源;

分布式锁Redis_第2张图片

指令:

setnx key value:获取锁资源,赋值

del key:删除锁资源

个人感觉很像synchronized

分布式锁Redis_第3张图片

问:当有人占用锁资源太久怎么办?

我们可以设置过期时间

指令:expire key times:给这个key设置过期时间times

ttl key:查看还有多久过期(-1代表过期)

分布式锁Redis_第4张图片

 问:当我们上锁setnx key 之后,服务器突然挂了怎么办?

我们可以上锁的时候把过期时间一起设置了

命令:set key value nx ex times

set users 20 nx ex 10:意思就是nx代表上锁了,ex代表过期时间设置 

 @GetMapping("testLock")
    public void testLock(){
        String uuid = UUID.randomUUID().toString();
        //1获取锁,setne
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid,3, TimeUnit.SECONDS);
        //2获取锁成功、查询num的值
        if(lock){
            Object value = redisTemplate.opsForValue().get("num");
            //2.1判断num为空return
            if(StringUtils.isEmpty(value)){
                return;
            }
            //2.2有值就转成成int
            int num = Integer.parseInt(value+"");
            //2.3把redis的num加1
            redisTemplate.opsForValue().set("num", ++num);
            //2.4释放锁,del
            //判断比较uuid值是否一样
            String lockUuid = (String)redisTemplate.opsForValue().get("lock");
            if(lockUuid.equals(uuid)) {
                redisTemplate.delete("lock");
            }
        }else{
            //3获取锁失败、每隔0.1秒再获取
            try {
                Thread.sleep(100);
                testLock();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

UUID防止误删锁

引入:

首先明白abc有的是分布式锁,谁都有机会拿到的 ,假设我们a先拿到这个锁,设置10s过期时间,突然服务器卡住,10s过去之后a的锁已经过期了,假设b拿到锁资源,然后服务器又好了,a又醒了,释放了b的锁资源;

也就是说,一个key有两种释放锁的方式:1、expire 设置的时间过期了 ;2、自己主动释放锁资源;

分布式锁Redis_第5张图片

解决方案:

1:set key uuid nx ex 时间times:(uuid为唯一标识)

2:释放锁的时候需要判断当前的uuid和要释放的uuid是否一样;

也就是说,每个线程将自己拿到的锁释放以后,被其他线程拿到会上uuid,此时就可以将锁资源理解为私有的了;

package com.atguigu.redis_springboot.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author diao 2022/3/13
 */
@RestController
@RequestMapping("/redisTest")
public class Test {
    @Autowired
    private RedisTemplate redisTemplate;
    
    @GetMapping("testLock")
    public void testLock(){
        String uuid = UUID.randomUUID().toString();
        //1、利用redisTemplate获得锁:set key uuid nx ex 时间
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 3, TimeUnit.SECONDS);
        
        if(lock){
            //2、获取键值
            Object value = redisTemplate.opsForValue().get("num");
            
            //2.1判断value值
            if(StringUtils.isEmpty(value)){
                return;
            }
            //2.2value值不为空,先将其转为int类型
            int num = Integer.parseInt(value + "");
            //+1
            redisTemplate.opsForValue().set("num",++num);
            //3.释放锁,比较uuid值是否一样
            String lockUid = (String) redisTemplate.opsForValue().get("lock");
            if(lockUid.equals(uuid)){
                //如果uuid一样就删除这个键
                redisTemplate.delete("lock");
            }
        }else{
            try {
                Thread.sleep(100);
                testLock();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
      
        
        
    }
    
}

你可能感兴趣的:(Redis,分布式)