IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> redis分布式锁 -> 正文阅读

[大数据]redis分布式锁

为什么使用分布式锁

web应用中防止特别事务并发,或者需要保证业务操作顺序
在事务中,redis锁和数据库读写共同作用,达到线程安全
例如事务切面下

事务开始
//方法开始
redis.lock
select * from a
update a
redis.unlock
//方法结束
事务提交

那么可能就会发生事务A先占有锁并执行,执行完毕后释放锁,但是未提交,此时事务B也开始执行了,但是注意由于事务A未提交,a上的写锁未解锁,B事务依然卡在select * from a,因此依然达到了串行的效果

最简易锁

public boolean lock(String lockPrefix, String orderNo, int expireMillionSeconds) {
        String realKey = "RedisDistribLock_" + lockPrefix + "_" + orderNo;
        boolean res = this.redisService.setForLock(realKey, expireMillionSeconds);
        if (res) {
            this.logger.warn("====> 分布式锁 " + realKey + " 获取成功");
        } else {
            this.logger.warn("====> 分布式锁 " + realKey + " 获取失败");
        }

        return res;
    }

    public boolean unLock(String lockPrefix, String orderNo) {
        String realKey = "RedisDistribLock_" + lockPrefix + "_" + orderNo;
        boolean res = this.redisService.deleteByKey(realKey);
        if (res) {
            this.logger.warn("====> 分布式锁 " + realKey + " 删除成功");
        } else {
            this.logger.warn("====> 分布式锁 " + realKey + " 删除失败");
        }

        return res;
    }

setForLock

 public <T> boolean setForLock(String key, int expireMillionSeconds) {
        Jedis jedis = null;

        boolean var6;
        try {
            jedis = (Jedis)this.jedisPool.getResource();
            jedis.select(this.redisConfig.getSelectdb());
            SetParams setParams = new SetParams();
            setParams.nx();//
            setParams.px((long)expireMillionSeconds);
            String result = jedis.set(key, "1", setParams);
            if (result != null && result.equals("OK")) {
                var6 = true;
                return var6;
            }

            this.logger.info("=====> 分布式锁获取失败 key:" + key);
            var6 = false;
        } finally {
            this.returnToPool(jedis);
        }

        return var6;
    }

![image.png](https://img-blog.csdnimg.cn/img_convert/df05518d06764070436e840ea540a13d.png#clientId=ua202e097-c8aa-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=255&id=u496c04ef&margin=[object Object]&name=image.png&originHeight=382&originWidth=1368&originalType=binary&ratio=1&rotation=0&showTitle=false&size=94860&status=done&style=none&taskId=u0881cbce-1f8e-464f-a581-a8dffd7e41d&title=&width=912)deleteByKey

 public boolean deleteByKey(String key) {
        Jedis jedis = null;

        boolean var5;
        try {
            jedis = (Jedis)this.jedisPool.getResource();
            jedis.select(this.redisConfig.getSelectdb());
            long ret = jedis.del(key);
            var5 = ret > 0L;
        } finally {
            this.returnToPool(jedis);
        }

        return var5;
    }

这是最简单的redis分布式锁,简单的设置一个值,和过期时间占据锁,结算直接删除key,此种锁无阻塞

进阶redis锁

package com.xishan.store.item.server.redis;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.concurrent.TimeUnit;


@Component
public class RedisLock {

    @Autowired
    private RedisTemplate redisTemplate;


    private static final Long SUCCESS = 1L;
    //15秒未抢到,则失败,需要重新抢
    @Value("${lock.timeout:10000}")
    private long timeout; //获取锁的超时时间



    /**
     * 加锁,无阻塞
     *
     * @param
     * @param
     * @return
     */
    public Boolean  tryLock(String key, String value, long expireTime) {


        Long start = System.currentTimeMillis();
        try{
            for(;;){
                //SET命令返回OK ,则证明获取锁成功
                Boolean ret = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.MILLISECONDS);
                if(ret){
                    return true;
                }
                //否则循环等待,在timeout时间内仍未获取到锁,则获取失败
                long end = System.currentTimeMillis() - start;
                if (end>=timeout) {
                    return false;
                }
            }
        }finally {

        }

    }


    /**
     * 解锁
     *
     * @param
     * @param
     * @return
     */
    public Boolean unlock(String key, String value) {


        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
        Object result = redisTemplate.execute(redisScript, Collections.singletonList(key),value);
        if(SUCCESS.equals(result)) {
            return true;
        }

        return false;
    }

}

这种方式可以设置加锁失败的循环等待时间,并且可以多设置一个value,相当于多了一个参数,那么我们可以利用这个参数标识(例如UUID)加锁线程,这样就做到了 一个线程加的锁只有它自己能解锁。

可重入redis锁

@Slf4j
@Component
public class RedisDistributedLockImpl implements IRedisDistributedLock {

    /**
     * key前缀
     */
    public static final String PREFIX = "Lock:";
    /**
     * 保存锁的value
     */
    private ThreadLocal<String> threadLocal = new ThreadLocal<>();

    private static final Charset UTF8 = Charset.forName("UTF-8");
    /**
     * 释放锁脚本
     */
    private 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();
    }

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public boolean lock(String key, long requireTimeOut, long lockTimeOut) {
        //可重入锁判断
        String originValue = threadLocal.get();
        if (!StringUtils.isBlank(originValue) && isReentrantLock(key, originValue)) {
            return true;
        }
        String value = UUID.randomUUID().toString();
        long end = System.currentTimeMillis() + requireTimeOut;
        try {
            while (System.currentTimeMillis() < end) {
                if (setNX(wrapLockKey(key), value, lockTimeOut)) {
                    threadLocal.set(value);
                    return true;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    private boolean setNX(String key, String value, long expire) {
        List<String> keyList = new ArrayList<>();
        keyList.add(key);
        return (boolean) redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            Boolean result = connection
                    .set(key.getBytes(UTF8),
                            value.getBytes(UTF8),
                            Expiration.milliseconds(expire),
                            RedisStringCommands.SetOption.SET_IF_ABSENT);
            return result;
        });

    }

    /**
     * 是否为重入锁
     */
    private boolean isReentrantLock(String key, String originValue) {
        String v = (String) redisTemplate.opsForValue().get(key);
        return v != null && originValue.equals(v);
    }

    @Override
    public boolean release(String key) {
        String originValue = threadLocal.get();
        if (StringUtils.isBlank(originValue)) {
            return false;
        }
        return (boolean) redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            return connection
                    .eval(UNLOCK_LUA.getBytes(UTF8), ReturnType.BOOLEAN, 1, wrapLockKey(key).getBytes(UTF8),
                            originValue.getBytes(UTF8));
        });
    }


    private String wrapLockKey(String key) {
        return PREFIX + key;
    }

}

threadLocal中存放线程生成的UUID 存为value
加锁,解锁都需要尝试取出这个value。如果和key不匹配都会加锁,或者解锁

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-03-16 22:27:35  更:2022-03-16 22:27:53 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:33:12-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码