본문 바로가기

interviewPrep 프로젝트

좋아요(추천) 기능과 동시성 이슈 해결

목차

1. 문제 상황 분석(좋아요 시 동시성 이슈)

2. 선택 가능한 기술 분석

3. 기술 선택 및 구현

 

 

1. 문제 상황 분석

- 사용자가 작성한 답안에 대한 좋아요 기능을 추가했습니다. 

  그런데 2명 이상의 사용자가 동시에 특정 답안에 대해서 추천을 하는 경우

  동시성 이슈가 발생할 수 있다고 판단했습니다.

 

 ※ 동시성 이슈란?

- JVM은 기본적으로 멀티 스레드를 지원합니다. 멀티 스레드란 하나의 JVM 프로세스 내에서 여러 개의 스레드가 동시에 실행될 수 있는 특성을 의미합니다. 그런데 멀티 스레드로 동작할 때, 두 개 이상의 스레드가 하나의 공유 자원에 동시에 접근할 때, 동시성 이슈가 발생할 수 있습니다. 

 예를 들어, 계좌에 잔액이 10000원일 때, 거래 1 스레드가 +1000원 요청을 발생 시키고, 거래 2 스레드가 -1500원 요청을 발생시켰다고 가정해봅시다. 이러한 경우, 최종 결과는 10000+1000-1500 = 9500원이 되어야 합니다. 하지만, 공유 데이터가 동기화하지 않은 상황에서는 거래1 스레드가 10000원을 가져와 11000원으로 먼저 업데이트하고, 거래2 스레드도 마찬가지로 10000원을 가져와 8500원을 업데이트해서, 최종 결과가 8500원이 되는 데이터 불일치가 발생할 수 있습니다.   

 

 

 

2. 선택 가능한 기술 분석

- 동시성 이슈와 관련해서 선택 가능한 기술을 3가지로 분석했습니다. 

  첫째는, 좋아요 추가 메서드에 synchronized 키워드를 적용하는 것

  둘째는, Pessimistic lock을 적용하는 것

  셋째는, Optimistic Lock을 적용하는 것이었습니다. 

 

(1) synchronized 키워드 적용

- 자바에서는 메소드에 synchronized 키워드를 추가할 수 있습니다. 

  메소드에 synchronized 키워드를 추가하면, 하나의 스레드가 메소드에 진입했을 때, 락이 걸리게 되어서

  다른 스레드가 해당 메소드를 사용할 수 없게 됩니다. 

  즉, 동기화가 가능합니다. 

 

- 반면, synchronized 키워드를 추가하는 것에는 단점이 존재합니다.

  바로 동기화가 하나의 프로세스 내에 있는 여러 스레드 간에만 동작한다는 점입니다. 

  예를 들어, 두 개 이상의 서버 프로세스가 실행중이라면,

  다른 프로세스 간의 스레드끼리는 synchronized 키워드를 추가하는 것으로 동기화가 동작하지 않습니다.

  즉, 서버의 확장성을 고려하면 synchronized 키워드를 추가하는 것만으로

  동기화가 제대로 발생하기 어렵습니다. 

 

(2) Pessimistic Lock 적용

- Pessimistic Lock은 DB 레벨에서 데이터에 실제 Lock을 적용하는 방식입니다. 

  JPA 기준으로 락 모드 PESSIMISTIC_WRITE를 통해서 적용할 수 있습니다. 

  Pessimistic Lock을 적용하면, 장점은 여러 서버 프로세스 간에도 동기화가 가능하다는 점입니다.

  반면, 단점은 실제 Lock을 잠궜다가 푸는 과정이 수반되기 때문에 서버의 성능이 떨어질 수 있다는 점입니다. 

 

(3) Optimistic Lock 적용

- Optimistic Lock은 실제 Lock을 적용하는 것이 아니라, 버전을 통해서 동기화를 하는 방식입니다.

  JPA 기준으로 락 모드 OPTIMISTIC을 통해서 적용할 수 있습니다.

  Optimistic Lock을 적용하기 위해서는 엔티티에 @Version을 통해서 버전 정보를 추가해줘야 합니다. 

  

  

3. 기술 선택 및 구현

- Optimistic Lock을 선택하고 구현했습니다. Optimistic Lock을 선택한 이유는

  다중 서버 프로세스 환경에서 성능 저하를 최소화하면서 동기화가 가능한 방식이라고 판단했기 때문입니다. 

  Optimistic Lock을 구현하기 위해, 우선 Heart(좋아요) 도메인에 버전 정보를 추가하고,

  JPA 영속성 계층에 findByIdWithOptimickLock이라는 메소드를 @Lock(value = LockModeType.OPTIMISTIC)

  과 함께 추가했습니다. 

 

- Optimistic Lock에 대한 테스트를 작성했습니다. 

  ExecutorService를 통해서 32개의 스레드로 구성된 스레드 풀을 만들고, 

  좋아요 추가에 대한 동시성 테스트를 진행했습니다. 

 

 

참고 자료

1) synchronized https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html