利用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();
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对批量数据实现分布式锁的更多相关文章
- Redis中是如何实现分布式锁的?
分布式锁常见的三种实现方式: 数据库乐观锁: 基于Redis的分布式锁: 基于ZooKeeper的分布式锁. 本地面试考点是,你对Redis使用熟悉吗?Redis中是如何实现分布式锁的. 要点 Red ...
- python使用redis实现协同控制的分布式锁
python使用redis实现协同控制的分布式锁 上午的时候,有个腾讯的朋友问我,关于用zookeeper分布式锁的设计,他的需求其实很简单,就是节点之间的协同合作. 我以前用redis写过一个网络锁 ...
- 基于redis集群实现的分布式锁,可用于秒杀商品的库存数量管理,有測试代码(何志雄)
转载请标明出处. 在分布式系统中,常常会出现须要竞争同一资源的情况,本代码基于redis3.0.1+jedis2.7.1实现了分布式锁. redis集群的搭建,请见我的另外一篇文章:<>& ...
- Redis的“假事务”与分布式锁
关注公众号:CoderBuff,回复"redis"获取<Redis5.x入门教程>完整版PDF. <Redis5.x入门教程>目录 第一章 · 准备工作 第 ...
- 使用数据库、Redis、ZK分别实现分布式锁!
分布式锁三种实现方式: 基于数据库实现分布式锁: 基于缓存(Redis等)实现分布式锁: 基于Zookeeper实现分布式锁: 基于数据库实现分布式锁 悲观锁 利用select - where - f ...
- 【spring boot】【redis】spring boot基于redis的LUA脚本 实现分布式锁
spring boot基于redis的LUA脚本 实现分布式锁[都是基于redis单点下] 一.spring boot 1.5.X 基于redis 的 lua脚本实现分布式锁 1.pom.xml &l ...
- 基于数据库、redis和zookeeper实现的分布式锁
基于数据库 基于数据库(MySQL)的方案,一般分为3类:基于表记录.乐观锁和悲观锁 基于表记录 用表主键或表字段加唯一性索引便可实现,如下: CREATE TABLE `database_lock` ...
- 分布式缓存技术redis学习系列(五)——redis实战(redis与spring整合,分布式锁实现)
本文是redis学习系列的第五篇,点击下面链接可回看系列文章 <redis简介以及linux上的安装> <详细讲解redis数据结构(内存模型)以及常用命令> <redi ...
- 使用redis设计一个简单的分布式锁
最近看了有关redis的一些东西,了解了redis的一下命令,就记录一下: redis中的setnx命令: 关于redis的操作命令,我们一般会使用set,get等一系列操作,数据结构也有很多,这里我 ...
随机推荐
- 05 BOM与DOM
BOM和DOM 1. 什么是BOM和DOM 到目前为止,我们已经学过了JavaScript的一些简单的语法.但是这些简单的语法,并没有和浏览器有任何交互. 也就是我们还不能制作一些我们经常看到的网页的 ...
- Unreal ListView使用篇
应用 ListView,在Unreal UI界面开发中用途非常广泛,基本只要你使用列表,就得需要用ListView.比如排行榜100个列表,界面上只需要显示出10个,我们肯定不能生成100个ui实例, ...
- Linux性能优化之内存性能统计信息
关于内存的概念及其原理在任何一本介绍操作系统的书本中都可以查阅到. 理论放一遍,在Linux操作系统中如何查看系统内存使用情况呢?看看内存统计信息有哪些维度. 一.内存使用量 详细使用方法,man f ...
- 掌握这20个JS技巧,做一个不加班的前端人
摘要:JavaScript 真的是一门很棒的语言,值得学习和使用.对于给定的问题,可以有不止一种方法来达到相同的解决方案.在本文中,我们将只讨论最快的. 本文分享自华为云社区<提高代码效率的 2 ...
- FastDFS安装和简介详细总结
1.fastDFS简介 1 FastDFS是用c语言编写的一款开源的分布式文件系统. 2 FastDFS为互联网量身定制,充分考虑了冗余备份.负载均衡.线性扩容等机制,并注重高可用.高性能等指标, 3 ...
- ASP.NET Core 6框架揭秘实例演示[11]:诊断跟踪的几种基本编程方式
在整个软件开发维护生命周期内,最难的不是如何将软件系统开发出来,而是在系统上线之后及时解决遇到的问题.一个好的程序员能够在系统出现问题之后马上定位错误的根源并找到正确的解决方案,一个更好的程序员能够根 ...
- CobaltStrike逆向学习系列(2):Stageless Beacon 生成流程分析
这是[信安成长计划]的第 2 篇文章 关注微信公众号 [信安成长计划] 0x00 目录 0x01 Patch Beacon 0x02 Patch Loader 0x03 文件对比 0x04 流程图 C ...
- 『无为则无心』Python基础 — 63、Python中的生成器
目录 1.为什么要有生成器 2.创建生成器 (1)简单创建生成器 (2)生成器的使用 3.yield关键词 (1)yield关键词说明 (2)send()方法说明 4.使用yield实现斐波那契数列 ...
- 【c#新手学习 练习 案例】 阶段项目一:开发团队调度软件
案例是模仿java https://blog.csdn.net/bjfu170203101/article/details/109322590 改用C#:开发环境 vs2022/vscode .n ...
- C# 题目
题目 http://blog.zhaojie.me/2011/03/my-interview-questions-for-dotnet-programmers.html 1.考察对常量和自读字段 初始 ...