需求背景

在开发的收入结转平台界面上有一个归集按钮,可以实现抓取结转表里面的多条数据进行归集操作。为了防止多人多电脑同时操作一条数据,我们自己开发了一个简单的基于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();
return keyMap;
}
}

解锁

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对批量数据实现分布式锁的更多相关文章

  1. Redis中是如何实现分布式锁的?

    分布式锁常见的三种实现方式: 数据库乐观锁: 基于Redis的分布式锁: 基于ZooKeeper的分布式锁. 本地面试考点是,你对Redis使用熟悉吗?Redis中是如何实现分布式锁的. 要点 Red ...

  2. python使用redis实现协同控制的分布式锁

    python使用redis实现协同控制的分布式锁 上午的时候,有个腾讯的朋友问我,关于用zookeeper分布式锁的设计,他的需求其实很简单,就是节点之间的协同合作. 我以前用redis写过一个网络锁 ...

  3. 基于redis集群实现的分布式锁,可用于秒杀商品的库存数量管理,有測试代码(何志雄)

    转载请标明出处. 在分布式系统中,常常会出现须要竞争同一资源的情况,本代码基于redis3.0.1+jedis2.7.1实现了分布式锁. redis集群的搭建,请见我的另外一篇文章:<>& ...

  4. Redis的“假事务”与分布式锁

    关注公众号:CoderBuff,回复"redis"获取<Redis5.x入门教程>完整版PDF. <Redis5.x入门教程>目录 第一章 · 准备工作 第 ...

  5. 使用数据库、Redis、ZK分别实现分布式锁!

    分布式锁三种实现方式: 基于数据库实现分布式锁: 基于缓存(Redis等)实现分布式锁: 基于Zookeeper实现分布式锁: 基于数据库实现分布式锁 悲观锁 利用select - where - f ...

  6. 【spring boot】【redis】spring boot基于redis的LUA脚本 实现分布式锁

    spring boot基于redis的LUA脚本 实现分布式锁[都是基于redis单点下] 一.spring boot 1.5.X 基于redis 的 lua脚本实现分布式锁 1.pom.xml &l ...

  7. 基于数据库、redis和zookeeper实现的分布式锁

    基于数据库 基于数据库(MySQL)的方案,一般分为3类:基于表记录.乐观锁和悲观锁 基于表记录 用表主键或表字段加唯一性索引便可实现,如下: CREATE TABLE `database_lock` ...

  8. 分布式缓存技术redis学习系列(五)——redis实战(redis与spring整合,分布式锁实现)

    本文是redis学习系列的第五篇,点击下面链接可回看系列文章 <redis简介以及linux上的安装> <详细讲解redis数据结构(内存模型)以及常用命令> <redi ...

  9. 使用redis设计一个简单的分布式锁

    最近看了有关redis的一些东西,了解了redis的一下命令,就记录一下: redis中的setnx命令: 关于redis的操作命令,我们一般会使用set,get等一系列操作,数据结构也有很多,这里我 ...

随机推荐

  1. 干工第一天,这个api超时优化把我干趴下了!

    近日我司进行云服务商更换,恰逢由我负责新上线的三方调用 api 维护管理,在将服务由阿里云部署到腾讯云过程中,我们压测发现在腾讯云调用京东接口时 TP999 抖动十分剧烈,尽管业务层有重试操作但是超时 ...

  2. 从零开始, 开发一个 Web Office 套件 (3): 鼠标事件

    这是一个系列博客, 最终目的是要做一个基于HTML Canvas 的, 类似于微软 Office 的 Web Office 套件, 包括: 文档, 表格, 幻灯片... 等等. 对应的Github r ...

  3. 《STL源码剖析》学习半生记:第一章小结与反思

    不学STL,无以立.--陈轶阳 从1.1节到1.8节大部分都是从各方面介绍STL, 包括历史之类的(大致上是这样,因为实在看不下去我就直接略到了1.9节(其实还有一点1.8.3的内容)). 第一章里比 ...

  4. Windows RestartManeger重启管理器

    介绍   重启管理器API可以消除或是减少在完成安装或是更新的过程中系统需要重启的次数.软件安装或是更新过程之所以需要重启系统的原因在于一些需要更新的文件正在被运行中的程序或服务使用.而重启管理器可以 ...

  5. .NET core实现一个简易的事件协调器(saga)

    在领域驱动设计中,由于领域边界的存在,以往的分层设计中业务会按照其固有的领域知识被切分到不同的限界中,并且引入了领域事件这一概念来降低单个业务的复杂度,通过非耦合的事件驱动来完成复杂的业务.但是事件驱 ...

  6. Spring Boot部署之 web项目war包运行

    传统的部署方式:将项目打成war包,放入tomcat 的webapps目录下面,启动tomcat,即可访问. 具体打war包流程: 1.pom.xml配置文件修改: 2.改造启动类,如果是war包发布 ...

  7. MYSQL文件复制及备份

    周末研究了下mysql的数据结构,记录下: 场景1:当从一台电脑的mysql的data中复制数据库的文件夹到另一台电脑上时会发现 表不存在,函数等也不存在 方法:1.需要将data根目录下的ibdat ...

  8. elasticsearch算法之搜索模型(一)

    面对海量的信息,我们很容易被淹没在信息的海洋中:当我们需要查找某个信息的时候,我们就会输入能够体现我们意图的关键字,搜索引擎会通过解析我们的关键字从而构造相应的查询表示方法:然后搜索引擎通过构造的查询 ...

  9. python的字符串切片技术

    听说过python的字符串切片技术吗?是不是听着超高级的?实际上,也不用想得太难,python的字符串切片技术就是将字符串的某些字符提取出来而已~ 字符串切片 字符串是一种序列类型,可以按序号访问其中 ...

  10. 一、ES6基础

    一.ECMAScript和JavaScript关系 JavaScript 的创造者 Netscape 公司,决定将 JavaScript 提交给标准化组织 ECMA,希望这种语言能够成为国际标 准,但 ...