记一个Redis分布式事务锁
package com.mall.common; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import redis.clients.jedis.ShardedJedis; import redis.clients.jedis.ShardedJedisPool;
@Slf4j public class DisLock { @Autowired private RedisTemplate<String, Object> redisTemplate; // RedisTemplate public void oneLock(){ String key = "key-001"; Long currentTime = System.currentTimeMillis(); boolean lock = redisTemplate.opsForValue().setIfAbsent(key, currentTime); try { if (lock) { log.info("获取锁成功,开始逻辑处理"); }else{ log.info("获取锁失败,业务正在处理中,请稍后"); } }catch (Exception e){ log.error("系统异常",e); }finally { if (lock) { redisTemplate.delete(key); log.info("处理结束,释放锁!"); } else { log.info("没有获取到锁,无需释放锁!"); Long createTime = (Long) redisTemplate.opsForValue().get(key); Long nowTime = System.currentTimeMillis(); Long time = (nowTime - createTime) / 1000; log.info("没有获取到锁,检测获取锁的线程是否处理超时,超时则释放他的锁"); if (time > 10) {//自定义锁多久自动释放锁 redisTemplate.delete(key); log.info("逻辑处理超过20秒,释放锁!"); } } } } //jds public void twoLock() { //基于redis的分布式锁 String redisKey = "key-001"; boolean falg = false; try { Long lock = this.incrBy(redisKey, 1, 10);//一次,超时时间10秒 if (lock > 1) { log.info("请勿重复提交请求"); } log.info("逻辑处理开始。。。。"); } catch (Exception e) { log.error("#accumulatePoints() 出现异常:{}", e); } finally { if(falg) { this.del(redisKey); } } } static ShardedJedisPool pool; public static Long incrBy(String key, long num, int seconds) { ShardedJedis jds = null; jds = pool.getResource(); Long result = jds.incrBy(key, num); if (seconds > 0) { jds.expire(key, seconds); } return result; } public static void del(String key) { ShardedJedis jds = null; try { jds = pool.getResource(); jds.del(key); } catch (Exception e) { log.error("#RedisPool() del异常:", e); e.printStackTrace(); } } }
package com.redis.mq.util; import redis.clients.jedis.Jedis; import java.util.Collections; import java.util.UUID; /** * @author xiaowu * @date 2019-12-20 **/ public class RedisLock { /** * RedisLock的正确姿势 * 加锁: * 通过setnx 向特定的key写入一个随机数,并设置失效时间,写入成功即加锁成功 * 注意点: * 必须给锁设置一个失效时间 -----> 避免死锁 * 加锁时,每个节点产生一个随机字符串 -----> 避免锁误删 * 写入随机数与设置失效时间必须是同时 -----> 保证加锁的原子性 * 使用: * SET key value NX PX 3000 * * 解锁: * 匹配随机数,删除redis上的特定的key数据, * 要保证获取数据,判断一致以及删除数据三个操作是原子性 * 执行如下lua脚本: * if redis.call('get', KEYS[1]) == ARGV[1] then * return redis.call('del', KEYS[1]) * else * return 0 * end * */ // 使用jedis 客户端的 /**SET key value NX PX 3000 成功返回值*/ private static final String LOCK_SUCCESS = "OK"; /**表示 NX 模式*/ private static final String SET_IF_NOT_EXIST = "NX"; /**单位 毫秒**/ private static final String SET_WITH_EXPIRE_TIME_PX = "PX"; /**lua脚本**/ private static final String SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; /**存储随机数**/ private static final ThreadLocal<String> local = new ThreadLocal<>(); /** * 加锁 */ public static boolean lock(Jedis jedis, String key, int expireTime) { // 产生随机数 String uuid = UUID.randomUUID().toString().replaceAll("-", ""); String result = jedis.set(key, uuid, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME_PX, expireTime); if (LOCK_SUCCESS.equals(result)) { // 随机数绑定线程 local.set(uuid); return true; } return false; } /** * 释放分布式锁 */ public static boolean unLock(Jedis jedis, String key) { String uuid = local.get(); //当前线程没有绑定uuid //直接返回 if (uuid == null || "".equals(uuid)) { return false; } Object result = jedis.eval(SCRIPT, Collections.singletonList(key), Collections.singletonList(uuid)); if (Long.valueOf(1).equals(result)) { // 解除绑定线程的随机数 local.remove(); return true; } return false; } public static void main(String[] args) { Jedis jedis = new Jedis("localhost", 6379); jedis.auth("373616885"); jedis.select(0); final String LOCK_KEY = "LOCK_KEY"; RedisLock.lock(jedis,LOCK_KEY,5000); RedisLock.unLock(jedis,LOCK_KEY); } }