Redis对批量数据实现分布式锁的实现代码

 更新时间:2022年03月24日 09:46:10   作者:HCAN  
为了防止多人多电脑同时操作一条数据,我们自己开发了一个简单的基于Redis实现的分布式锁,Redis对批量数据实现分布式锁相关知识感兴趣的朋友一起看看吧

需求背景

在开发的收入结转平台界面上有一个归集按钮,可以实现抓取结转表里面的多条数据进行归集操作。为了防止多人多电脑同时操作一条数据,我们自己开发了一个简单的基于Redis实现的分布式锁。

代码实现

逻辑代码中的使用案例

参数说明:

scIds:结转数据的ID主键集合。

timeOutToDeleteRedisKey:最大锁超时时间(用于自动解锁)

organizationId:租户ID(这个参数根据情况选择是否需要)

ReturnLock returnLock = RedisLock.applyByIds(scIds, redisLockTime, organizationId, () -> {
    // dothing 具体的代码业务逻辑
    return 123;
});
 
if (!returnLock.getFlag()) {
    if(returnLock.getLock()){
        throw new CommonException("归集数据有程序正在运行,请稍候刷新页面重试");
    }else {
        throw new CommonException(returnLock.getErrorMsg());
    }
}
// 返回对象
System.out.println(returnLock.getResObj()) // 123

Redis加锁方法封装

public static ReturnLock applyByIds(List<Long> scIds, Long timeOutToDeleteRedisKey, Long tenantId,
                                    Supplier<Object> supplier) {
    // 获得锁
    Map<String, String> keyMap = getLockByIds(scIds, timeOutToDeleteRedisKey, tenantId);
    ReturnLock returnLock = new ReturnLock();
    returnLock.setFlag(true);
    returnLock.setLock(false);
    try {
        // 判断主键ID数量和加锁的数量是否一致,不一致说明有加锁失败的数据,返回失败锁信息
        if(scIds.size() > keyMap.size()){
            returnLock.setFlag(false);
            returnLock.setLock(true);
            returnLock.setKeyMap(keyMap);
            return returnLock;
        }
        // 应用代码执行后的返回结果 supplier:java8四大内置函数的供给型接口
        // Supplier<T>(供给型接口)无参数,返回类型为T的对象:T get()
        returnLock.setResObj(supplier.get());
    }catch (Exception e) {
        returnLock.setFlag(false);
        returnLock.setErrorMsg(e.getMessage());
        e.printStackTrace();
    }finally {
        // 应用代码执行报错,解锁
        unLockByIds(keyMap);
    }
    return returnLock;
}

getLockByIds

批量获取每一条数据的Redis锁

private static Map<String, String> getLockByIds(List<Long> scIds, Long timeOutToDeleteRedisKey, Long tenantId) {
    Map<String, String> keyMap = new HashMap<>();
    // 从spring上下文中获取Redis的操作对象,因为这个代码是写在util中,所以通过上下文方式获取bean对象
    // RedisHelper:Redis的操作对象,我们自己公司基于redisTemplate封装的
    RedisHelper redisHelper = SpringUtil.getBean(RedisHelper.class);
    try {
        if (CollectionUtil.isNotEmpty(scIds)) {
            for (int i = 0; i < scIds.size(); i++) {
                String item = "L_" + scIds.get(i);
                String UUID = UUIDUtils.generateTenantUUID(tenantId);
                // 判断key值是否被锁,如果没有锁则加锁并设置过期时间, 如果有锁, 则报错并立即释放已加的锁
                // strSetIfAbsent会返回true/false,底层是封装了java的setIfAbsent方法和Redis的setnx方法
                // 如果设置成功返回true否则false
                if (redisHelper.strSetIfAbsent(item, UUID)) {
                    // 设置过期时间
                    redisHelper.setExpire(item, timeOutToDeleteRedisKey, TimeUnit.SECONDS);
                    // 保存设置的锁信息
                    keyMap.put(item, UUID);
                } else {
                    // 锁设置异常,表示有数据被锁住了,解锁之间加锁的数据
                    if (MapUtil.isNotEmpty(keyMap)) {
                        if (unLockByIds(keyMap)) {
                            // 解锁失败再次解锁
                            unLockByIds(keyMap);
                        }
                    }
                    // 返回锁信息
                    return keyMap;
                }
            }
        }
        return keyMap;
    }catch (Exception e) {
        e.printStackTrace();
    }
}

解锁

private static Boolean unLockByIds(Map<String, String> keyMap) {
    RedisHelper redisHelper = SpringUtil.getBean(RedisHelper.class);
    try {
        if (MapUtil.isEmpty(keyMap)) {
            return false;
        }
        // 判断是否是自己的锁
        for (String key : keyMap.keySet()) {
            String uuid = keyMap.get(key);
            if (StringUtils.equals(uuid, redisHelper.strGet(key))) {
                // 封装了redisTemplate.delete(key);
                redisHelper.delKey(key);
            }
        return true;
    }catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}

实现效果

当对数据进行批量加锁的时候,若在加锁的过程中出现加锁失败,则回滚直接加的锁,并提示"归集数据有程序正在运行,请稍候刷新页面重试"。

若业务代码逻辑执行成功或者执行报错都会自动的解锁当前加锁的数据。

到此这篇关于利用Redis对批量数据实现分布式锁的文章就介绍到这了,更多相关Redis分布式锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • redis内部数据结构之SDS简单动态字符串详解

    redis内部数据结构之SDS简单动态字符串详解

    SDS是Redis中实现的一种数据结构,用来存储字符串,最近学习中正好学习到了这里,所以下面这篇文章主要给大家介绍了redis内部数据结构之SDS简单动态字符串的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-11-11
  • Redis和MySQL保证双写一致性的问题解析

    Redis和MySQL保证双写一致性的问题解析

    Redis和MySQL的双写一致性指的是在同时使用缓存和数据库存储数据的时候,保证Redis和MySQL中数据的一致性,那么如何才能保证他们的一致性呢,下面小编就来为大家详细讲讲
    2023-11-11
  • Redis操作相关命令之查看、停止、启动命令

    Redis操作相关命令之查看、停止、启动命令

    这篇文章主要介绍了Redis操作相关命令之查看、停止、启动命令,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • 浅谈一下Redis的缓存穿透、击穿和雪崩

    浅谈一下Redis的缓存穿透、击穿和雪崩

    这篇文章主要介绍了浅谈一下Redis缓存穿透、击穿和雪崩,缓存穿透是指在使用缓存系统时,频繁查询一个不存在于缓存中的数据,导致这个查询每次都要通过缓存层去查询数据源,无法从缓存中获得结果,需要的朋友可以参考下
    2023-08-08
  • redis实现共同好友的思路详解

    redis实现共同好友的思路详解

    微信朋友圈大家都玩过吧,那么朋友圈的点赞、评论只能看到自己好友的信息是怎么操作的呢?下面通过本文给大家分享下此功能的实现流程,对redis实现共同好友的方法感兴趣的朋友一起看看吧
    2021-05-05
  • 深入理解 Redis Template及4种序列化方式

    深入理解 Redis Template及4种序列化方式

    这篇文章主要介绍了深入理解 Redis Template及4种序列化方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • redis缓存存储Session原理机制

    redis缓存存储Session原理机制

    这篇文章主要为大家介绍了redis缓存存储Session原理机制详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2021-11-11
  • Redis链表底层实现及生产实战

    Redis链表底层实现及生产实战

    Redis 的 List 是一个双向链表,链表中的每个节点都包含了一个字符串。是redis中最常用的数据结构之一,本文主要介绍了Redis链表底层实现及生产实战,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2023-03-03
  • Redis中Bloom filter布隆过滤器的学习

    Redis中Bloom filter布隆过滤器的学习

    布隆过滤器是一个非常长的二进制向量和一系列随机哈希函数的组合,可用于检索一个元素是否存在,本文就详细的介绍一下Bloom filter布隆过滤器,具有一定的参考价值,感兴趣的可以了解一下
    2022-12-12
  • Redis数据备份与恢复方式的五种方式

    Redis数据备份与恢复方式的五种方式

    本文主要介绍了Redis数据备份与恢复方式,包含了五种方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-07-07

最新评论