저번 포스트 동시성문제(4) 에서는 낙관적 락으로 어떻게 동시성 문제를 해결할 수 있는지, 어떻게 적용하는지 알아보았다. 이번에는 Named 락을 통해 문제를 어떻게 해결할 수 있는지 알아보겠다.
이름을 가진 메타데이터 락
비관적 락과 비슷하다. 하지만 비관적락은 row 나 table 단위로 락을 걸고, 네임드락은 메타데이터 단위로 락을 건다는 차이가 있다.
메타데이터는 시간이 지남에 따라 많은 양의 데이터를 수집, 저장 및 분석할 수 있도록 일관된 방식으로 구조화된, 다른 데이터를 설명하는 데이터입니다.
대량의 정보 가운데에서 찾고 있는 정보를 효율적으로 찾아내서 이용하기 위해 일정한 규칙에 따라 콘텐츠에 대하여 부여되는 데이터이다.
-> 인스타그램의 해시태그('#')와 유사한 역할
비관적락은 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
@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
@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("테스트 통과");
}
.
.
네임드락은 주로 분산락을 구현할 때 사용한다. 비관적 락은 타임아웃을 구현하기가 힘들지만 네임드 락은 이를 손쉽게 구현할 수 있다는 특징이 있다. 이외에도 데이터 삽입 시 데이터 정합성을 맞춰야 하는 경우에도 네임드 락을 사용할 수 있다.
하지만 이 방법은 트랜잭션 종료 시에 락 해제, 세션 관리를 잘 해줘야 되기 때문에 주의해서 사용해야 하고, 실제로 사용시에는 구현 방법이 복잡할 수 있다.