简单实用的 redis 分布式锁
作者:Rubble
- 2022 年 4 月 15 日
本文字数:1529 字
阅读完需:约 5 分钟
基于 setnx 加锁,基于 lua 实现锁释放。
实现了根据 key 及唯一标识 requestId 进行加锁,并设置了超时时间;释放锁时根据 key 及 requestId 进行释放。
SET_IF_ABSENT 没有 key 才设置,expire 设置超时时间,作为原子性操作
RedisCallback<Boolean> callback = (connection) -> {
return connection.set(lockKey.getBytes(Charset.forName("UTF-8")), requestId.getBytes(Charset.forName("UTF-8")), Expiration.seconds(timeUnit.toSeconds(expire)), RedisStringCommands.SetOption.SET_IF_ABSENT);
};
return (Boolean)redisTemplate.execute(callback)
复制代码
直接上代码,可以直接用的工具类
@Slf4j
public class RedisDistributedLock {
private RedisTemplate redisTemplate;
public RedisDistributedLock(RedisTemplate redisTemplate){
this.redisTemplate = redisTemplate;
}
public static final String UNLOCK_LUA;
/**
* 释放锁脚本,原子操作
*/
static {
StringBuilder sb = new StringBuilder();
sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] ");
sb.append("then ");
sb.append(" return redis.call(\"del\",KEYS[1]) ");
sb.append("else ");
sb.append(" return 0 ");
sb.append("end ");
UNLOCK_LUA = sb.toString();
}
/**
* 获取分布式锁,原子操作
* @param lockKey
* @param requestId 唯一ID, 可以使用UUID.randomUUID().toString();
* @param expire
* @param timeUnit
* @return
*/
public boolean tryLock(String lockKey, String requestId, long expire, TimeUnit timeUnit) {
try{
RedisCallback<Boolean> callback = (connection) -> {
return connection.set(lockKey.getBytes(Charset.forName("UTF-8")), requestId.getBytes(Charset.forName("UTF-8")), Expiration.seconds(timeUnit.toSeconds(expire)), RedisStringCommands.SetOption.SET_IF_ABSENT);
};
return (Boolean)redisTemplate.execute(callback);
} catch (Exception e) {
log.error("redis lock error.", e);
}
return false;
}
/**
* 释放锁
* @param lockKey
* @param requestId 唯一ID
* @return
*/
public boolean releaseLock(String lockKey, String requestId) {
RedisCallback<Boolean> callback = (connection) -> {
return connection.eval(UNLOCK_LUA.getBytes(), ReturnType.BOOLEAN ,1, lockKey.getBytes(Charset.forName("UTF-8")), requestId.getBytes(Charset.forName("UTF-8")));
};
return (Boolean)redisTemplate.execute(callback);
}
/**
* 获取Redis锁的value值
* @param lockKey
* @return
*/
public String get(String lockKey) {
try {
RedisCallback<String> callback = (connection) -> {
return new String(connection.get(lockKey.getBytes()), Charset.forName("UTF-8"));
};
return (String)redisTemplate.execute(callback);
} catch (Exception e) {
log.error("get redis occurred an exception", e);
}
return null;
}
}
复制代码
此工具简易轻量,基本满足加锁的一些场景。
VS redission 分布式锁
redisson 实现了加锁尝试时间
看门狗延长锁持有时间。
redission 支持多样锁如可重入锁、读写锁、信号量等。
直接用 setNx 的问题,无超时时间程序意外挂掉,导致 key 不释放,好像新版本有 setnx+过期时间的方法。
释放锁直接用 delete 可能删除非当前线程的锁,增加唯一 value 校验,两步操作非原子性。
划线
评论
复制
发布于: 刚刚阅读数: 2
Rubble
关注
还未添加个人签名 2021.06.01 加入
还未添加个人简介
评论