Skip to content

비관적 락(Pessimistic Lock)

  1. 레코드 단위 테스트 환경 구성
    1. Mysql 콘솔을 세개 띄운다.
    2. 첫번째 콘솔은 락 정보를 조회하는 용도로 사용한다.
      SELECT * FROM performance_schema.data_locks;
    3. 두번째 콘솔에는 레코드 단위로 비관적 락을 설정하는 용도로 사용한다.
      set autocommit = false;
      select * from T_API where API_ID = 1000 for update;
      commit;
      rollback;
    4. 세번째 콘솔은 락이 걸린 레코드에 추가로 비관적 락을 설정하는 용도로 사용한다.
      set autocommit = false;
      select * from T_API where API_ID = 1000 for update;
    5. 두번째와 세번째 콘셀에서 오토커밋 설정을 끈다.
      set autocommit = false;
    6. 첫번째 콘솔의 쿼리를 실행해서 락 데이터가 존재하는지 확인한다.
      SELECT * FROM performance_schema.data_locks;
    7. 락 데이터가 조회되는 경우 해당 트랜잭션을 찾아서 락을 제거한다.
      commit;
      rollback;
  2. 비관적 락 테스트
    1. 일반 select 구문의 마지막에 “for update” 구문을 붙여주는 것으로 비관적 락을 생성할 수 있다.
    2. 두번째 콘솔에서 select … for update 쿼리를 실행하여 레코드 단위의 비관적 락을 생성한다.
    3. 첫번째 콘솔에서 락을 조회했을 때, 두 건의 레코드가 생성된 것을 확인한다.
    4. 세번째 콘솔에서 동일한 쿼리를 실행했을 때, 락이 풀릴때까지 대기하는것을 확인할 수 있다. (대기하다가 타임아웃에 걸린다)
    5. 두번째 콘솔에서 커밋하거나 롤백한다.
    6. 첫번째 콘솔의 쿼리를 실행했을 때 락이 삭제된 것을 볼 수 있다.
    7. 세번째 콘솔의 쿼리를 실행하면, 새로운 락이 생성된 것을 확인할 수 있다.
  3. 백앤드 애플리케이션에서 레코드 단위의 비관적 락을 적용
    1. API 호출이 완료되는 시점에 커밋된다.
    2. 오류 발생시 롤백되도록 설정한다.
      1. 서비스 메소드에 try catch 구문을 사용해서 익셉션이 발생했을 익셉션을 던지도록 한다.
      2. Transaction 어노테이션에 rollbackFor 속성을 추가한다.
      3. 아래 예제에서는 BizException이 발생했을때 롤백되도록 설정되어 있다.
      @Transactional(rollbackFor = BizException.class)
      public void insertBookmark(final int menuId) {
      try {
      final User sessionUser = ServletUtil.getSessionUser();
      final String remoteAddr = RemoteAddr.getRequestIpAddress();
      final CommonBookmarkContext context = CommonBookmarkContext.builder()
      .userId(Objects.requireNonNull(sessionUser).getUserId())
      .menuId(menuId)
      .createIp(remoteAddr)
      .updateIp(remoteAddr)
      .build();
      commonBookmarkMapper.insertBookmark(context);
      } catch (final Exception e) {
      throw new BizException(e);
      }
      }
    3. 모든 경우에 커밋/롤백 작업이 실행되기 때문에 레코드 단위의 비판적 락을 적용할 수 있다.