본문 바로가기
Server

동시성 문제

by Ahngyuho 2025. 2. 26.

 

동시성 문제 테스크 도구

 

하나의 트랜잭션에 동시에 접근한다면?

 

Talend API Tester

 

https://chromewebstore.google.com/detail/talend-api-tester-free-ed/aejoelaoggembcahagimdiliamlcdmfm

 

Talend API Tester - Free Edition - Chrome 웹 스토어

Visually interact with REST, SOAP and HTTP APIs.

chromewebstore.google.com

 

동시성을 테스트 하기 위해 두개의 http 요청  프로그램을 사용하였습니다.

그리고 인텔리제이의 디버깅 시스템을 이용해서 각 스레드별 브레이크 포인트 실행

 

두 개의 요청을 보내고 순차적으로 각 스레드에 걸린 브레이크 포인트를 실행하면 동시성 상황을 만들 수 있습니다.

 

결과는 2개의 count 가 되어야 하는데 1로 나옵니다.

 

 

 

해결 방법 1 - 자바 문법

자바 문법만으로 해결하는 방법

단점은 성능이 많이 떨어짐

 

이 네모 영역을 하나의 스레드만 실행할 수 있도록 자바 문법을 사용해주는 것입니다.

 

 

결과

 

해결 방법 2 - 낙관적 락

 

✅ 1. 낙관적 락(Optimistic Lock)

🔹 개념

  • **"충돌 가능성이 적을 것"**이라고 가정하고 사용.
  • 데이터 충돌이 발생하지 않는다면 별다른 락을 사용하지 않으므로 성능이 좋음.
  • 트랜잭션 완료 시점에서 버전(@Version)을 체크하여 충돌을 감지.
  • 충돌이 발생하면 예외(OptimisticLockException)가 발생하고 재시도가 필요.

🔹 사용 사례

 동시 수정 가능성이 낮은 경우
 읽기 비율이 높고, 쓰기 비율이 낮은 경우
 API 트래픽이 많아 DB 락을 최소화해야 하는 경우
 쇼핑몰에서 상품 재고 수정 (사용자가 장바구니에 담고 일정 시간 후 결제하는 경우)

 

✅ @Version 필드를 활용하여 트랜잭션 완료 시 충돌을 감지.

🔹 낙관적 락 사용 예제

 

@Entity
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

	....

    @Version  // ✅ 낙관적 락 적용
    private Integer version;
}

이것만 추가해 주시면 됩니다.

 

 

결과

 

 

업데이트할 때 버전 번호를 함께 저장하고, 변경 시에도 동일한 버전인지 확인하는 방식입니다.

충돌이 발생하면 예외를 던지고, 재시도를 하도록 유도하는 방식입니다.

 

성공

 

실패

 

 

즉 둘 중 하나만 성공하게 하는 것!

 

🔹 장점

✅ 데이터 충돌이 적다면 성능이 매우 우수
✅ 락을 걸지 않아도 되므로 데드락(Deadlock) 위험 없음
✅ DB 부하가 낮아 트래픽이 많은 서비스에서 유리

🔹 단점

❌ 동시 수정이 발생하면 재시도가 필요함
❌ 충돌이 자주 발생하는 환경에서는 비효율적

 

 

해결방법  2 - 비관적 락

 

✅ 2. 비관적 락(Pessimistic Lock)

🔹 개념

  • **"충돌 가능성이 많을 것"**이라고 가정하고 사용.
  • 트랜잭션이 시작될 때 즉시 DB에 LOCK을 걸어 다른 트랜잭션이 접근하지 못하도록 차단.
  • 충돌이 발생할 가능성이 높거나, 데이터 정합성이 중요한 경우 사용.

🔹 사용 사례

동시에 여러 사용자가 같은 데이터를 수정할 가능성이 높은 경우
데이터 일관성이 절대적으로 필요한 경우 (은행 계좌, 포인트 차감 등)
낙관적 락에서 충돌이 너무 자주 발생하여 성능이 나빠지는 경우

 

비관적 락의 주요 종류:

  1. PESSIMISTIC_READ → 다른 트랜잭션이 읽기/쓰기 모두 불가능
  2. PESSIMISTIC_WRITE → 다른 트랜잭션이 쓰기 불가능, 읽기 가능
  3. PESSIMISTIC_FORCE_INCREMENT → PESSIMISTIC_WRITE + 버전 증가

🔹 비관적 락 사용 예제

public interface PostRepository extends JpaRepository<Post, Long> {
    //비관적 락 : DB 단에서 LOCK
    //PESSIMISTIC_WRITE : 다른 트랜잭션이 읽기 O , 쓰기 X
    //PESSIMISTIC_READ : 다른 트랜잭션이 읽기 X, 쓰기 X
    //다른 트랜잭션이 읽지 못하도록
    @Lock(LockModeType.PESSIMISTIC_READ)
    Optional<Post> findById(Long id);
    
    ...

 

이렇게만 추가해주시면 됩니다!

 

 

JPA 에서 비관적 락과 관련된 락 모드 3가지를 지원해주고 있습니다.

 

성공

 

실패

 

콘솔 화면 

 

🔹 장점

✅ 데이터 정합성이 보장됨
✅ 충돌 발생 시 재시도 로직이 필요 없음

🔹 단점

❌ 트랜잭션이 길어질수록 DB 성능 저하
 데드락(Deadlock) 위험 존재
 동시 처리 성능이 낮음

 

 

 

✅ 3. 낙관적 락 vs 비관적 락 비교

낙관적 락 (Optimistic Lock)비관적 락 (Pessimistic Lock)

락 거는 시점 트랜잭션 완료 시점(버전 체크) 트랜잭션 시작 시점(DB에서 락)
성능 우수함 (락을 사용하지 않음) 낮음 (DB에서 락을 걸어야 함)
충돌 발생 시 OptimisticLockException 발생 후 재시도 필요 충돌 발생하지 않음 (다른 트랜잭션 차단)
데드락 위험 없음 있음
사용 예시 쇼핑몰 상품 재고 업데이트, 게시글 수정 은행 계좌 이체, 포인트 차감

✅ 4. 락보다 더 빠른 동시성 제어: 캐시 서버 활용

🔹 락 방식의 한계

  • 낙관적 락: 충돌이 자주 발생하면 재시도로 인해 성능이 저하됨.
  • 비관적 락: 동시 요청이 많을수록 성능 저하가 심함.
  • 해결 방법: Redis 같은 캐시 서버를 활용하여 동시성을 제어.

🔹 Redis 분산 락 사용

Redis의 SETNX(Set if Not Exists) 기능을 이용하면, DB가 아닌 메모리 캐시에서 락을 관리하여 성능을 크게 개선할 수 있음.

캐시를 활용하면 DB 부하를 줄이면서도 동시성 문제를 해결 가능.

 

 

 

결론

✅ 1. 낙관적 락 (Optimistic Lock)

  • 충돌 가능성이 적다면 낙관적 락이 더 유리
  • 트랜잭션 종료 시 @Version 필드로 충돌 감지
  • 읽기 트래픽이 많고 쓰기 트래픽이 적은 경우 사용

✅ 2. 비관적 락 (Pessimistic Lock)

  • 충돌 가능성이 많다면 비관적 락이 필요
  • 트랜잭션 시작 시 DB에서 강제로 락을 걸어 다른 트랜잭션 차단
  • 은행 계좌 이체, 포인트 차감 등 강력한 데이터 정합성이 필요한 경우 사용

✅ 3. 캐시 서버 (Redis)

  • 락 방식은 기본적으로 DB를 사용하므로 성능 한계가 있음
  • 트래픽이 많은 경우, Redis 같은 캐시 서버를 사용하여 동시성 문제를 해결
  • SETNX를 이용한 분산 락을 적용하면 DB 부하를 줄이면서 성능을 크게 개선 가능 🚀

 

 

 

'Server' 카테고리의 다른 글

PinPoint  (0) 2025.02.21
CQRS  (0) 2025.02.19
웹 소켓(Web Socke!)  (0) 2025.02.17
멀티파트 폼 데이터(Multipart Form Data)  (0) 2025.01.11
Spring boot 애플리케이션 ec2에 jar로 배포  (0) 2023.07.23