记一个Redis分布式事务锁

Posted Mr_伍先生

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了记一个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);
    }

}

 

以上是关于记一个Redis分布式事务锁的主要内容,如果未能解决你的问题,请参考以下文章

Redis数据库系列Redis事务乐观锁和分布式锁

Redis实现分布式锁(设计模式应用实战)

Redis实现分布式锁(设计模式应用实战)

连载redis库存操作,分布式锁的四种实现方式[三]--基于Redis watch机制实现分布式锁

分布式锁分析:使用Redis实现分布式事务中的锁机制

超卖解决方案之redis分布式锁