需求背景

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

    PHP面试专栏正式起更,每周一.三.五更新,提供最好最优质的PHP面试内容.继上一篇"PHP面试常考内容之面向对象(2)"发表后,今天更新面向对象的最后一篇(3).需要(1),(2 ...

  2. 前端框架 bootstrap 的使用

    内容概要 前端框架 bootstrap 简介 引入方式 布局容器 栅格系统 图标 内容详细 简介 网址:https://v3.bootcss.com/ 版本: 2.X 3.X 4.X 推荐使用3.X版 ...

  3. Solution -「CCO 2019」「洛谷 P5532」Sirtet

    \(\mathcal{Description}\)   Link.   在一个 \(n\times m\) 的网格图中,每个格子上是空白 . 或沙子 #,四联通的沙子会连成一个整体.令此时所有沙子块同 ...

  4. Solution -「AGC 004E」「AT 2045」Salvage Robots

    \(\mathcal{Description}\)   Link.   有一个 \(n\times m\) 的网格.每个格子要么是空的,要么有一个机器人,要么是一个出口(仅有一个).每次可以命令所有机 ...

  5. pytest(13)-多线程、多进程执行用例

    有些项目的测试用例较多,测试用例时需要分布式执行,缩短运行时间. pytest框架中提供可用于分布式执行测试用例的插件:pytest-parallel.pytest-xdist,接下来我们来学习这两个 ...

  6. 手把手教你把 Git 子模块更新到主项目

    本文以 skywalking-rocketbot-ui子模块合并到 skywalking 为例,手把手教你如何把 Git 子模块更新到主项目中去. 首先,把fork的skywalking项目克隆到本地 ...

  7. 图解AI数学基础 | 线性代数与矩阵论

    作者:韩信子@ShowMeAI 教程地址:http://www.showmeai.tech/tutorials/83 本文地址:http://www.showmeai.tech/article-det ...

  8. Nginx 配置apple-app-site-association

    ios突然给我发了如上链接和一个json,说他那边需要放一个 apple-app-site-association 文件用来支持他那边的功能,文件不需要后缀. 先说一下要求:线上官网的地址后面跟上他所 ...

  9. java 接口 文件传输

    调用接收端 @ApiOperation(value = "文件请求展示方法") @RequestMapping(value = "/showFile", met ...

  10. CentOS单机安装k8s并部署.NET 6程序 压测 记录

    前面部分依照CentOS单机安装k8s并部署.NET 6程序来进行,内存.cpu.centos版本一致,之前222元买的三年8M 2c4g腾讯云轻量服务器,这个教程算是写的很详细的了,基本可以一致执行 ...