我们在Springboot项目中分别整合了redis和redission框架。
下边我记录一下再框架中分别使用redis和redission实现分布式锁的代码。
一:redis-lua脚本实现分布式锁
lua本身是不具备原子性的,但由于Redis的命令是单线程执行的,它会把整个Iua脚本作为一个命令执行,会阻塞其间接受到的其他命令,这就保证了lua脚本的原子性。
Lua脚本好处:
1) 原子性:Lua脚本的所有命令在执行过程中是原子的,避免了并发修改带来的问题。
2) 减少网络往返次数:通过在服务器端执行脚本,减少了客户端和服务器之间的网络往返次数,提高了性能。
3) 复杂操作:可以在Lua脚本中执行复杂的逻辑,超过了单个Redis命令的能力。
lua脚本使用注意点
1) 由于Redis执行lua脚本其间,无法处理其他命令,因此如果lua脚本的业务过于复杂,则会产生长时间的阻塞,因此编写Lua脚本时应尽量保持简短和高效。
2) Redis默认限制lua执行脚本时间为5s,如果超过这个时间则会终止且抛错,可以通过lua-time-limit调整时长。
但是,redis-lua分布式锁有一个小小的问题,他不具备可重入性。针对这个问题,我对redis-lua分布式锁实现了封装。代码如下所示:
RedisLuaUtils.java
package com.modules.redis.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @author camellia
* redis 加锁工具类
*/
@Slf4j
public class RedisLuaUtils
{
/**
* 超时时间(毫秒)
*/
private static final long TIMEOUT_MILLIS = 15000;
/**
* 重试次数
*/
private static final int RETRY_TIMES = 10;
/***
* 睡眠时间(重试间隔)
*/
private static final long SLEEP_MILLIS = 100;
/**
* 用来加锁的lua脚本
* 因为新版的redis加锁操作已经为原子性操作
* 所以放弃使用lua脚本
*/
private static final String LOCK_LUA =
"if redis.call(\"setnx\",KEYS[1],ARGV[1]) == 1 " +
"then " +
" return redis.call('expire',KEYS[1],ARGV[2]) " +
"else " +
" return 0 " +
"end";
/**
* 用来释放分布式锁的lua脚本
* 如果redis.get(KEYS[1]) == ARGV[1],则redis delete KEYS[1]
* 否则返回0
* KEYS[1] , ARGV[1] 是参数,我们只调用的时候 传递这两个参数就可以了
* KEYS[1] 主要用來传递在redis 中用作key值的参数
* ARGV[1] 主要用来传递在redis中用做 value值的参数
*/
private static final String UNLOCK_LUA =
"if redis.call(\"get\",KEYS[1]) == ARGV[1] "
+ "then "
+ " local number = redis.call(\"del\", KEYS[1]) "
+ " return tostring(number) "
+ "else "
+ " return tostring(0) "
+ "end ";
/**
* 检查 redisKey 是否上锁(没加锁返回加锁)
*
* @param redisKey redisKey
* @param template template
* @return Boolean
*/
public static Boolean isLock(String redisKey, String value, RedisTemplate<Object, Object> template)
{
return lock(redisKey, value, template, RETRY_TIMES);
}
private static Boolean lock(String redisKey, String value, RedisTemplate<Object, Object> template, int retryTimes)
{
boolean result = lockKey(redisKey, value, template);
// 循环等待上一个用户锁释放,或者锁超时释放
while (!(result) && retryTimes-- > 0)
{
try
{
log.debug("lock failed, retrying...{}", retryTimes);
Thread.sleep(RedisLuaUtils.SLEEP_MILLIS);
}
catch (InterruptedException e)
{
return false;
}
result = lockKey(redisKey, value, template);
}
return result;
}
/**
* 加锁
* @
标签:脚本,return,Springboot,KEYS,redis,redisKey,lua,SpringBoot3
From: https://blog.csdn.net/qq_39708228/article/details/144500158