redis分布式锁

系列文章目录

redis主从架构
redis哨兵架构
redis的集群架构
Redis的单线程和高性能
redis管道操作(节省网络IO开销)
redis的lua脚本
redis分布式锁
redis分布式锁redisson
redis缓存优化
redis的过期淘汰策略
redis连接池参数

文章目录

  • 系列文章目录
    • redis分布式锁
      • setnx命令
      • 一. 利用stringRedisTemplate 构建一个原始版分布式锁(``1.0版本``)
      • 二. 1.0版本存在问题
      • 三. 2.0版本存在问题
      • 四. 关于3.0版本
        • 3.0版本的问题
      • 五. 4.0版本存在问题
        • 锁续命

redis分布式锁

setnx命令

setnx key value

将key的值设为value,仅当key不存在时。

若给定的key已经存在,则setnx不做任何动作。

SETNX 是 set if not exists 的简写

由于redis的IO单线程特性,所以同一时间只会有一个线程设值成功。

一. 利用stringRedisTemplate 构建一个原始版分布式锁(1.0版本)

String lockKey = "lock:product_101";

boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey,"value");

if(result){
	//业务逻辑
}

//释放锁
stringRedisTemplate.delete(lockKey)

二. 1.0版本存在问题

假设业务逻辑里面抛异常,则锁并未得到释放。(2.0版本)

String lockKey = "lock:product_101";

boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey,"value");

if(result){
	try{
		//业务逻辑
	}catch(Exception e){
	
	}finally{
		//释放锁
		stringRedisTemplate.delete(lockKey)
	}
}


三. 2.0版本存在问题

如果业务逻辑执行到一半,宕机了。则锁也不会得到释放

解决办法,在设置锁的时候设置10秒钟的超时时间,到期自动释放 (3.0版本)

String lockKey = "lock:product_101";
//在设置锁的时候设置10秒钟的超时时间,到期自动释放
boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey,"value",10,TimeUnit.SECONDS);

if(result){
	try{
		//业务逻辑
	}catch(Exception e){
	
	}finally{
		//释放锁
		stringRedisTemplate.delete(lockKey)
	}
}

四. 关于3.0版本

大多数公司在并发量不是很高的情况下,也在允许小笔的超卖等业务的情况下,该版本适用, 该版本易于维护。

3.0版本的问题

如果在高并发的情况下,短时间大量业务请求进来的时候,程序会变慢,则设置的10秒钟自动释放可能不够用。会存在10秒钟该业务逻辑没有处理完的情况,在该情况下,锁被自动释放了。 这里当其他线程又拿到锁之后,进行业务逻辑处理。当业务逻辑处理到一部分的时候,之前的线程业务逻辑处理完了。并在这个时候把锁释放了。这里相当于A线程把B线程的锁释放了。这样C线程又会加锁成功。这样可能导致了这把锁一直失效,会导致大量的超卖。

解决办法: 将value设置成有标识的id,在释放锁的时候校验该id,属于自己的才释放该锁。(4.0版本)

这里不能设置成线程id,因为每台服务器都可能有相同的线程id

String lockKey = "lock:product_101";
//在设置锁的时候设置10秒钟的超时时间,到期自动释放
String clientId = UUID.randomUUID().toString();
boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey,clientId,10,TimeUnit.SECONDS);

if(result){
	try{
		//业务逻辑
	}catch(Exception e){
	
	}finally{
		//释放锁
        if(clientId.equals(stringRedisTemplate.opsForValue().get(lockKey))){
			stringRedisTemplate.delete(lockKey)
        }
	}
}

五. 4.0版本存在问题

//释放锁
        if(clientId.equals(stringRedisTemplate.opsForValue().get(lockKey))){
			stringRedisTemplate.delete(lockKey)
        }

这里释放锁逻辑会出现并发问题。当当前线程发现这把锁属于自己,并准备删除锁的时候。刚好这个时候锁超时了,锁自动释放,其他线程这个时候拿到了锁。
当前线程觉得这把锁是属于自己的,所以理所应当的把锁释放了。这时也相当于A线程把B线程的锁释放了。这个问题出现的核心是由于锁过期时间,到期自动释放导致。

解决办法: 单纯延长过期时间治标不治本。

锁续命

在主线程创建分布式锁的时候,创建一个子线程,定时(一定要小于锁过期时间)去延长锁的过期时间,让锁在主线程不退出的情况下,永远不过期。当主线程退出后,子线程也相应退出。

你可能感兴趣的:(redis,分布式,数据库)