Mybatis 동시성제어

Jae Hun Lee·2023년 9월 27일
0

회사에서 개발을 진행하던 중
실 사용자가 신청하는 로직을 개발하게 되었는데
문제는 사이트가 오픈되면 동시에 많은 인원이 신청을 하게되지만
신청자에는 제한이 있는 경우도 존재한다.
바로 동시성 제어가 필요하다 라는 생각을 했지만
JPA로는 동시성 제어를 해본 경험이있지만 Mybatis는 해본적이 없고
물어볼 선임이 없는 상황이라 혼자 테스트를 진행하며 기록한다.

  • 환경
    • SpringBoot 2.7.1
    • Java 18
    • Junit 5
    • MyBatis

  • 최초 테스트 코드
    • 요청 횟수 : 1000번
    • 쓰레드 수 : 100
    • 제한 : 100
@Autowired
    private UserCommonMapper userCommonMapper;

    @Test
    void test() throws InterruptedException {
        int numberOfThreads = 1000;
        ExecutorService service = Executors.newFixedThreadPool(10);
        CountDownLatch latch = new CountDownLatch(numberOfThreads);
        for (int i = 0; i < numberOfThreads; i++) {
            service.submit(() -> {
                createTestData();
                latch.countDown();
            });
        }
        latch.await();
        Integer cnt = userCommonMapper.selectCntTest();
        Assertions.assertThat(cnt).isEqualTo(100);

    }

    void createTestData() {
        Integer cnt = userCommonMapper.selectCntTest();
        if (cnt < 100) {
            userCommonMapper.insertTest();
        }
    }
  • 결과
    • 기댓값 : 100
    • 실제값 : 120
  • 결과는 역시 동시성 제어가 이루어지지 않아 120개의 데이터가 입력이 되었다

  • 동시성 제어 고민
    • Jpa의 경우 다양한 동시성 제어를 고민한 적이 있다.
      하지만 이번엔 Mybatis를 이용한 동시성 제어를 진행해야 한다
    • For Update
      • 데이터를 조회 후 해당 데이터가 업데이트 될 때까지 락을 거는 방식이라고 한다 하지만 다양한 데이터를 조회해야 하고 내가 구성 해놓은 로직에는 맞지않다고 생각하여 패스
    • synchronized
      • synchronized가 가장 사용이 어렵지 않고 적합하다고 생각하여 적용 후 테스트를 진행했다. ( 성능 저하가 있을 수 있다고 한다 )

  • synchronized 적용 후 코드
synchronized void createSynchronizedTestData() {
        Integer cnt = userCommonMapper.selectCntTest();
        if (cnt < 100) {
            userCommonMapper.insertTest();
        }
    }
  • 테스트 결과
    • 기댓값 : 100
    • 실제값 : 100
  • 정상적으로 데이터가 입력되었다

  • 실제 로직에 테스트코드 적용
@Test
    void Test() throws Exception {

        int numberOfThreads = 300;
        ExecutorService service = Executors.newFixedThreadPool(100);
        CountDownLatch latch = new CountDownLatch(numberOfThreads);
        for (int i = 0; i < numberOfThreads; i++) {
            service.submit(() -> {
                try {
                    createData();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                latch.countDown();
            });
        }
        latch.await();
    }

    void createData() throws Exception {
        UserRegisterOneDTO oneDto = new UserRegisterOneDTO();
        oneDto.setOneTimeInfo(21);
        oneDto.setOneLevel(55);
        oneDto.setIsLevelTest("N");
        oneDto.setIsBook("N");
        oneDto.setIsMobilePc("mobile");
        oneDto.setTime1("15:00");
        oneDto.setTime2("16:00");
        HttpServletRequest httpServletRequest;
        httpServletRequest = null;
        userSugangService.insertRegister(oneDto,httpServletRequest);
    }
  • 적용 전
    • 기댓값 : 각 항목 10
    • 실제값 : 31, 30

  • 적용 후
    • 기댓값 : 각 항목 10
    • 실제값 : 각 항목 10

profile
기록을 남깁니다

0개의 댓글