利用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等一系列操作,数据结构也有很多,这里我 ...
随机推荐
- JVM的监控
# Table of Contents * 一.jvm常见监控工具&指令 * 1. jps:jvm进程状况工具 * 2.jstat: jvm统计信息监控工具 * 3.jinfo: java配置 ...
- Spring Security 介绍
Spring Security介绍 开源 提供企业级的安全认证和授权 Spring安全拦截器 认证管理器 认证模式 Basic HTTP 1.0中使用的认证方法,使用用户名和密码Base64编码的方式 ...
- Spring Boot数据访问之Druid连接池的配置
在Spring Boot数据访问之数据源自动配置 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)中数据源连接池方式缺省(type)情况下默认使用HikariCP,那不缺省如何配置呢?我们 ...
- Zookeeper应用之一:数据发布与订阅初体验
Zookeeper到底是什么?可以从Zookeeper提供的功能来理解.本篇小作文就是使用其提供的功能之一:数据发布与订阅. 需求:服务端开启多个实例提供服务,客户端使用服务.如果服务端某个服务下线或 ...
- 菜鸟到大神之多图预警——从 RAID 到分布式系统中的副本分布
我们知道,在面对大规模数据的计算和存储时,有两种处理思路: 垂直扩展(scale up):通过升级单机的硬件,如 CPU.内存.磁盘等,提高计算机的处理能力. 水平扩展(scale out):通过添加 ...
- MyBatisPlus根据ID修改对应的值,其他属性不变.如何实现?
1.基本操作 // 修改订单的状态为已发货和已支付 Order order = new Order(); order.setSend(1); // 已发货 order.setPay(1); // 已支 ...
- 攻防世界Web_shrine
题目: 给的是源代码,整理一下如下: 看到jinjia flask,render_template_string猜测到这题应该是考查python模板注入. 代码分析可以得到以下信息: 1.路径在 /s ...
- HMS Core在MWC2022展示最新开放能力,助力开发者构建精品应用
[2022年2月28日,巴塞罗那]世界移动通信大会MWC2022在巴塞罗那开幕.HMS Core设立了3个展台(Fira Gran Via,Hall 1),向全球开发者展示HMS Core 6的全新开 ...
- IGMP协议测试-网络测试仪实操
一.前言:IGMP协议用于IPv4系统向任何邻居组播路由器报告其组播成员资格.IP组播路由器自己本身也可以是一到多个组播组的成员.这时,组播路由器要实现协议的组播路由器部分. IGMP存在三个不同版本 ...
- 思迈特软件 Smartbi数据查询能力如何?
随着对BI应用程度的加深,用户需要连接和管理的数据越来越多,也越来越复杂. Smartbi支持丰富的数据源接入,但一般并不能直接使用接入的业务库直接进行数据分析.所以在报表开发前的取数过程,把需要的数 ...