[Spring] 동시성 문제(5) - Named 락

Kyungmin·2024년 6월 22일
0

Spring

목록 보기
26/39

저번 포스트 동시성문제(4) 에서는 낙관적 락으로 어떻게 동시성 문제를 해결할 수 있는지, 어떻게 적용하는지 알아보았다. 이번에는 Named 락을 통해 문제를 어떻게 해결할 수 있는지 알아보겠다.

1. Named(네임드) 락

이름을 가진 메타데이터 락
비관적 락과 비슷하다. 하지만 비관적락은 row 나 table 단위로 락을 걸고, 네임드락은 메타데이터 단위로 락을 건다는 차이가 있다.

메타데이터란?

메타데이터는 시간이 지남에 따라 많은 양의 데이터를 수집, 저장 및 분석할 수 있도록 일관된 방식으로 구조화된, 다른 데이터를 설명하는 데이터입니다.

대량의 정보 가운데에서 찾고 있는 정보를 효율적으로 찾아내서 이용하기 위해 일정한 규칙에 따라 콘텐츠에 대하여 부여되는 데이터이다.
-> 인스타그램의 해시태그('#')와 유사한 역할

2. 네임드 락 동작


비관적락은 stock 에 대해서 락을 걸었다면 네임드락은 stock 에는 lock 을 걸지않고 별도의 공간에 lock 을 걸게 된다.

1. 세션 1이 '1' 이라는 이름으로 락을 획득한다.(get_lock)
2. 세션 1는 세션 1이 락을 해제할 때까지 락을 획득 할 수 없다.
3. 세션 1이 release_lock 으로 락을 해제하고 난 후, 세션 2는 락을 획득한다.

코드로 네임드 락을 어떻게 적용할 수 있는지 알아보자
.
.
LockRepository

@Query(value = "select get_lock(:key, 3000)", nativeQuery = true)
void getLock(String key);

@Query(value = "select release_lock(:key)", nativeQuery = true)
void releaseLock(String key);

NamedLockStockFacade

 	@Transactional
    public void decrease(Long id, Long quantity) {
        try {
        	// 락 획득
            lockRepository.getLock(id.toString());
            // 로직 수행
            stockService.decrease(id, quantity);
            
        } finally {
        	// 락 해제
            lockRepository.releaseLock(id.toString());
        }

StockService

  • 재고 서비스에서는 부모의 트랜잭션과 별도로 수행되어야 되기 때문에 Propagation 을 변경해준다.
  • propagation = Propagation.REQUIRES_NEW
@Transactional(propagation = Propagation.REQUIRES_NEW)
public synchronized void decrease(Long id, Long quantity) {
        Stock stock = stockRepository.findById(id).orElseThrow();
        stock.decrease(quantity);

        stockRepository.save(stock);

    }

Requires_New

  • 항상 새로운 트랜잭션을 시작해야 하는 경우
  • 이미 진행중인 트랜잭션이 있으면 이를 보류시키고 새로운 트랜잭션을 만들어 시작함

NamedLockStockFacadeTest

  • 재고 서비스에서는 부모의 트랜잭션과 별도로 수행되어야 되기 때문에 Propagation 을 변경해준다.
  • propagation = Propagation.REQUIRES_NEW
 	@Test
    @DisplayName("네임드 락을 걸었을 경우")
    public void sameRequest() throws InterruptedException {
        int threadCount = 100;

        // ExecutorService : 비동기로 실행하는 작업을 단순화하여 사용할 수 있도록 도와주는 자바의 API
        ExecutorService executorService = Executors.newFixedThreadPool(32);

        // CountDownLatch : 다른 스레드에서 수행중인 작업이 완료될 때 까지 대기할 수 있도록 도와주는 클래스
        CountDownLatch latch = new CountDownLatch(threadCount);

        for (int i=0; i < threadCount; i++) {
            executorService.submit( () -> {
                try {
                    namedLockStockFacade.decrease(1L, 1L);
                } finally {
                    latch.countDown();
                }
            });
        }

        latch.await();
        Stock stock = stockRepository.findById(1L).orElseThrow();
        assertEquals(0, stock.getQuantity(),"테스트 미통과");
        System.out.println("테스트 통과");
    }


.
.

네임드락은 주로 분산락을 구현할 때 사용한다. 비관적 락은 타임아웃을 구현하기가 힘들지만 네임드 락은 이를 손쉽게 구현할 수 있다는 특징이 있다. 이외에도 데이터 삽입 시 데이터 정합성을 맞춰야 하는 경우에도 네임드 락을 사용할 수 있다.

하지만 이 방법은 트랜잭션 종료 시에 락 해제, 세션 관리를 잘 해줘야 되기 때문에 주의해서 사용해야 하고, 실제로 사용시에는 구현 방법이 복잡할 수 있다.


참고

https://kr.teradata.com/glossary/what-is-metadata

https://joyfuls.tistory.com/24

profile
Backend Developer

0개의 댓글

관련 채용 정보