卡飞资源网

专业编程技术资源共享平台

Java高并发下如何解决数据的唯一性

问题说明

最近做了一个功能,通过Rocket MQ 作为消息队列,根据条件生成奖励数据,在高并发的情况,一次性产生消息15万多条,实际数据只有其中的一半,导致生成的奖励数据重复。虽然在奖励生成的消费者端做了重复校验,但是还是没有避免。

数据库的事务隔离级别为:Read Committed

   /**
     * 根据订单的生产奖励信息
     *
     * @param order 订单议信息
     */
    @Override
    @Transactional
    public void buildAgreementAwardDataByOrder(Order order) {
    }

判断唯一性的示例代码,在数据库中查询消费者是否已经存在有效的奖励数据信息,如果不存在就生成奖励数据,如果存在就直接跳过:

/**
     * 判断当前用户是否已经存在有效的奖励信息
     *
     * @param order     协议信息
     * @param awardStageEnum
     * @return
     */
    private Boolean existValidAwardDetail(Order order, AwardStageEnum awardStageEnum, TaskTypeEnum taskTypeEnum) {
        List<OrderAwardDetail> list = this.awardDetailRepository.lambdaQuery().eq(OrderAwardDetail::getActivityCode, order.getActivityCode())
                .eq(OrderAwardDetail::getCustomerCode, order.getCustomerCode())
                .eq(OrderAwardDetail::getAwardStage, awardStageEnum.getValue())
                .eq(OrderAwardDetail::getTaskType, taskTypeEnum.getDictCode())
                .ne(OrderAwardDetail::getCheckStatus, AwardCheckStatusEnum.ABANDON.getDictCode()).list();

        if (CollectionUtils.isNotEmpty(list)) {
            log.error("当前用户已经存在本周期内有效的奖励信息,用户编码为:【" + order.getCustomerCode() + ",活动编码【" + order.getActivityCode() + "】");
            return true;
        }
        return false;
    }

当在高并发的情况,同样订单order进入生成奖励数据逻辑,判断奖励是否存在奖励可能失效,因为在@Transactional未设置实物隔离级别,将采用数据库的事务隔离级别,而数据库的隔离级别是Read Committed,在未提交之前,相同的order进入后都判断到未创建奖励数据,导入生成重复的奖励信息。

解决方案

  1. 在消费者获取消息后,引入redis做过期缓存,将订单的id作为唯一key,缓存时间为设置为5分钟,如果存在直接返回,不存在则写入缓存,往下执行生成奖励信息;
  2. 在奖励表中创建一个基于订单信息的唯一索引,如果Redis 判断因为某些原因失效,最后数据库唯一索引也能避免生成重复的奖励信息。
控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言