记一个,生产遇到的redission锁 + 事务提交,导致重复插入数据的问题

场景: 在插入表1的时候需要先查询id 是否存在。如果不存在,则插入,存在则update。

问题: 并发的时候,即便加锁了,也存在问题,因为线程1在insert结束之后还要处理其他逻辑,但是线程2在等待的时候已经去查询了数据库,

  此时线程1还没有commit事务,所以线程2查询是看不到数据的,导致也insert了。 这表又不设置主键,可以重复插入。

解决:在事务提交之前不能释放锁,一定要等事务完全提交之后才可以释放锁。代码如下:

最外层方法,是在checkIsOcToPusCard(buName,posMemberQueryVo);里面去调用加锁的方法,里面逻辑太长,省略了:
@Transactional
    public ResponseMessages<PosMemberQueryResVO> posMemberQuery(PosMemberQueryVo posMemberQueryVo) {
        String buId = buManager.getBuId(posMemberQueryVo.getBuId());
        String buName = buManager.getBuName(posMemberQueryVo.getBuId());
        String subBuCode = posMemberQueryVo.getBuId();
        if (buId == null) {
            return new ResponseMessages<>(ResultCode.INVALID_BU_ID.getCode(),
                    ResultCode.INVALID_BU_ID.getMessage(), SystemConstant.SUCCESS_FALSE);
        }
        DistLock distLock = null;
        try (ShardingCtx s = ShardingCtx.setShardingValue(buId)) {

            // ACSCHK-3970
            distLock = checkIsOcToPusCard(buName,posMemberQueryVo);

            PosMemberQueryResVO posMemberQueryResVO = Stream.of(
                    executeIf(posMemberQueryVo.getIsPartnerCallFlag(), () -> {
                        PosMemberQueryResVO partnerMemberRes = executePosMemberQuery(posMemberQueryVo, buId);
                        buildSegmentAndAccount(buId, posMemberQueryVo, partnerMemberRes);
                        return partnerMemberRes;
                    }),
                    executeIf(posMemberQueryVo.getIsStaffCallFlag(), () ->
                        executePosMemberQueryByStaffCall(buId, posMemberQueryVo)
                    )
                )
                    .filter(Objects::nonNull)
                    .filter(e -> StringUtils.hasText(e.getCardId()))
                    .reduce(this::combinePosMemberRes)
                    .orElse(null);

            buildFiveLine(buName, subBuCode, posMemberQueryResVO);

            return new ResponseMessages<>(ERROR_CODE_1000, SUCCESS, SUCCESS_TRUE, posMemberQueryResVO);
        } catch (ApiVerifyException e) {
            return new ResponseMessages<>(Integer.parseInt(e.getErrorCode()), e.getErrorMessage(),
                SystemConstant.SUCCESS_FALSE);
        } catch (Exception e) {
            log.error(ResultCode.GENERAL_ERROR.getMessage(), e);
            return new ResponseMessages<>(ResultCode.GENERAL_ERROR.getCode(), e.getMessage(),
                SystemConstant.SUCCESS_FALSE);
        } finally {
            releaseTransactionLock(distLock);
        }
    }
    //事务完全提交在释放锁
    private void releaseTransactionLock(DistLock distLock) {
        if (TransactionSynchronizationManager.isSynchronizationActive()) {
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCompletion(int status) {
                    releaseLock(distLock);
                }
            });
        } else {
            releaseLock(distLock);
        }
    }

    private void releaseLock(DistLock distLock) {
        if (distLock != null) {
            distLock.unlock();
        }
    }

代码2:

加锁代码:
private DistLock checkIsUpdateContactSegment(MemberCard memberCard, String ocToPusNew, String ocToPusOld) {

        // ACSCHK-3826
        boolean locked = false;

        String lockId = CONTACT_SEGMENT_UPDATE_LOCK + memberCard.getBuId() + "_" + memberCard.getVisibleCard();
        DistLock distLock = distLockRegistry.getLock(lockId);

        try {
            locked = distLock.tryLock(contactXmUpsetLockWaitTime, contactXmLockLeaveTime, TimeUnit.SECONDS);
        } catch (Exception ignored) {
        }

        if (!locked) {
            String errorMessage = String.format("failed to lock Octopus card for jointMember MemberNumber: %s", memberCard.getVisibleCard());
            log.error(errorMessage);
            throw new RuntimeException(errorMessage);
        }

        String otpNew = String.valueOf(Integer.parseInt(ocToPusNew));
        String otpOld = String.valueOf(Integer.parseInt(ocToPusOld));
        Optional.of(memberCard)
            .filter(card -> IS_DEFAULT_Y.equals(card.getOcToPusFlag()))
            .filter(card -> Objects.equals(otpOld, card.getSegmentNum()))
            .ifPresent(card -> updateSegment(otpNew, otpOld, card.getPrevConId()));
        return distLock;
    }

    private void updateSegment(String otpNew, String otpOld, String prevConId) {

        String name = PARTNER_SEGMENT + otpNew + LocalDate.now().toString().replaceAll("-", "");
        CustomerLoyContactXmPO loyContactXmPO = buildLoyContactXm(otpNew, name, prevConId);
        boolean numFlag = customerLoyContactXMRepository.findIsExistByIdAndTypeAndName(prevConId, name, SEGMENT_TYPE);
        boolean countFlag = customerLoyContactXMRepository.findIsExistByIdAndTypeAndOctoPusCard(prevConId, SEGMENT_TYPE, PARTNER_SEGMENT, otpNew, MBAPP_OCTOPUS);

        if (numFlag || countFlag) {
            updateCustomerLoyContactXMByFlag(numFlag, countFlag, loyContactXmPO);
        } else {
            customerLoyContactXMRepository.insertLoyContactXMByOctoPus(loyContactXmPO, otpNew);
        }

        CustomerLoyContactXmPO loyContactXmOld = buildOldLoyContactXm(otpOld, prevConId);
        customerLoyContactXMRepository.updateCustomerLoyContactXMByOctopus(loyContactXmOld, otpOld);
    }

 

posted @ 2024-06-27 16:45  威兰达  阅读(23)  评论(0编辑  收藏  举报