ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

Java: 并发情况下,数据插入重复(业务标识+Redisson分布式锁)

2022-07-21 03:00:09  阅读:214  来源: 互联网

标签:Redisson Java keys lock locks Code key 节点 分布式


方案二

期望在同一时间段,不允许相同的库存被操作

  • 通过Redisson以SKU Code为唯一标识+业务标识上锁
@Transactional(rollbackFor = Exception.class)
@DistributedLock(prefix = LOCK_STOCK_SYNC_PREFIX, key = "#skuCode")
@Override
public void syncStock(String skuCode, final StockSyncCmd cmd) {
  stockSyncCmdExe.execute(skuCode, cmd);
}
  • 自定义注解+AOP切面
// DistributedLockAdvice
@Around("process()")
public Object aroundExec(ProceedingJoinPoint pjp) throws Throwable {
  /* 
     通过连接点pjp获取所需属性,解析获取key...
   */
 final Map<String, RLock> locks = lock(keys);

  try {
    return pjp.proceed();
  } finally {
    unlock(locks);
  }
}

Redis锁缺陷

在Redis主从集群模式下,Client A对Master节点上的锁会异步复制到Slave节点,但是在这个过程中,如果Master节点发生宕机,Slave节点成为Master节点后,Client B又对这个原Slave现Master节点上了同一把锁,那这个时候就发生了多Client对同一业务ID上锁的情况

异常case

经过JMeter并发测试,发现达不到预期效果

  • 分析原因

    1. @Transaction 本质上也是利用Spring AOP切面,生成代理对象
    2. 切面之间是有优先级的
    3. 自定义切面注解优先级低于@Transaction注解
  • 综上

    • 第一步分:开启事务-加锁-执行业务 ✅
    • 第二部分:释放锁-提交事务❌

    如果业务请求1先释放锁,然后在还未提交事务的时间节点,有业务请求2进来请求的还是同一个SKU Code,因为已经释放了锁,但业务请求1的事务还未提交,所以这个时候查DB数据还是原始数据,那这个时候问题就出现了,会被乐观锁拒绝更新
    image

  • 解决方案

@Aspect
@Order(1)
@Component
@Slf4j
public class DistributedLockAdvice {
}

@Order注解可以决定IOC容器加载Bean的顺序

延伸问题

背景

假设业务需求现在要求同时操作一批SKU Code,也就是把一个集合的SKU Code加锁看作为一次原子操作

实现

  • 思路
    取出Code依次上锁
    1. 这批上锁操作全部成功,然后进入连接点继续执行
    2. 其中某把锁被占用,休眠等待通知,被唤醒,然后继续满足要求1
@Around("process()")
public Object aroundExec(ProceedingJoinPoint pjp) throws Throwable {
  // 获取method,args,prefix,key...
  Set<String> keys = new HashSet();
  // 解析EL表达式将结果逐一添加进keys
  Map locks = this.lock(keys);

  try {
    return pjp.proceed();
  } finally {
    unlock(locks);
  }
}

private Map<String, RLock> lock(Set<String> keys) {
  Map<String, RLock> locks = new HashMap<>(6);
  for (String key : keys) {
    // get lock instance
    final RLock lock = redissonClient.getLock(key);
    // lock
    lock.lock();
    // add to list
    locks.put(key, lock);
  }
  return locks;
}

异常case

image
当两个请求同时到来时,有可能会出现死锁的情况,都不释放已经获取的锁,都在等待唤醒通知,获取下一把锁。
image

  • 解决方案
    在不考虑替换整体解决方案的前提下,用TreeSet可以避免该问题
    • 根据阿里巴巴范式需要注意这一点
      image

标签:Redisson,Java,keys,lock,locks,Code,key,节点,分布式
来源: https://www.cnblogs.com/tanhaoo/p/16500518.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有