利用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 scIds, Long timeOutToDeleteRedisKey, Long tenantId,
Supplier
【利用Redis对批量数据实现分布式锁】getLockByIds
批量获取每一条数据的Redis锁
private static Map getLockByIds(List scIds, Long timeOutToDeleteRedisKey, Long tenantId) {
Map 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 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;
}
}
实现效果 当对数据进行批量加锁的时候,若在加锁的过程中出现加锁失败,则回滚直接加的锁,并提示"归集数据有程序正在运行,请稍候刷新页面重试"。
若业务代码逻辑执行成功或者执行报错都会自动的解锁当前加锁的数据。
推荐阅读
- python统计数据画概率曲线_利用Python实现并解释概率图形
- c++对数函数_数据库OceanBase创始人阳振坤(通关TPC-C到底有多难())
- 新程序员|不做工程等于纸上谈兵——对话OceanBase创始人阳振坤
- android|android 利用数据库实现历史搜索记录功能
- PHP学习|PHP知识点笔记
- 面试|腾讯三面终拿Offer,关于redis,高并发,分布式,微服务一键领取
- 数据库|redis
- 分布式|深度剖析(Redis分布式锁到底安全吗(看完这篇文章彻底懂了!))
- 故障转移集群无法连接到节点_为什么redis哨兵集群只有2个节点无法正常工作(...)
- 程序员|怎么用Redis分布式锁才能确保万无一失(进阶加薪全靠它!)