简单实用的 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)
复制代码
直接上代码,可以直接用的工具类
@Slf4jpublic 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 加入
还未添加个人简介










评论