본문 바로가기

학습 기록 (Learning Logs)/Today I Learned

동시성 문제 해결

동시성 문제 해결

동시성 문제란 무엇이며, 이를 해결하기 위한 기본적인 전략을 설명해주세요. 실제 운영 환경에 적용한 사례가 있다면 함께 설명해 주어도 좋습니다.

 

동시성은 다중 사용자/요청이 동시에 자원에 접근할 때 발생합니다. 락(lock), 큐(queue), 트랜잭션 격리 수준 등의 개념을 활용해 본 경험을 떠올려 보세요.


 

동시성 문제는 스프링에서 서버를 띄어놓고, 여러요청이 왔을때 같은 자원에 대해서 서로 경쟁하게되면 발생하는 데이터 오류입니다.
가령  영수와 철수가 각각 100만원씩 통장에 돈이 있는 상태에서 영수가 철수에게 20만원을 보내고, 철수가 영수에 21만원을 동시에 보내면, 우리가 기대하는 값은 영수 101만원, 철수 99만원입니다.
그러나 결과가 예사오가 다르게 나오게 됩니다. 영수 입장에서는 영수의 통장잔고 조회와, 철수 통장잔고를 조회한 시점이 같은 자원을 조회하고, 업데이트 하는 과정에서 그떄 당시에는 철수가 100만원이었지만 나중에는 80만원이라서 100+20 으로 업데이트 하지 않고, 80+20으로 업데이트를 하게 되서 철수는 20만원을 받았음에도 불구하고 결과론적으로 80만원만 통장에 잔고로 남게 됩니다.

이러한 현상을 막기위해 락과, 큐, 트랜잭션 격리 수준에 따라 같은 자원에 접근할때 막는다라던지(락), 막고있는동안 대기를 해서 순서를 나눠준다던지(큐), 읽을떄는 막지않고 데이터를 쓰거나 읽는 곳이 많은 경우에만 읽을떄도 락을 건다던지(격리수준) 각각 개발 비지니스나 도메인, 환경에 따라 다르게 적용해야합니다.


 

동시성 문제는 여러명의 사용자가 하나의 자원에 접근할 떄 생길 수 있는 문제를 말합니다. 

이를 해결하기 위해서는

DB 수준에서 트랜잭션을 활용해 락을 거는 방법, 

애플리케이션 레벨에서 을 거는 방법이 있고, 

레디스를 활용하여 분산락을 활용하는 방법이 있습니다.


 

동시성 문제는 스레드, 프로세스 볼 수 있다.

멀티 스레드 환경인 경우 voltile , synchronous, thread safe 한 자료구조 등을 이용하면 동시성 문제를 방지할 수 있다.
멀티 프로세스 환경인 경우 여러대의 서버에서 동시에 요청이 오는 것이므로 하나의 서버에서 동시성 문제가 제어되도 발생할 수 있다.
이 경우 레디스와 같은 외부 저장소를 lock으로 사용하는 redisson distributed lock을 사용하거나

writer db가 하나인 경우 DB 단에 제약조건, 락을 적용하여 제어할 수 있다.


 

동시성 문제 정의 및 개요

동시성 문제란, 여러 사용자의 요청이 동시에 동일한 자원에 접근하거나 수정할 때 발생할 수 있는 문제입니다.

이러한 상황에서 적절한 제어가 없으면 데이터 정합성에 오류가 생기고, 예상과 다른 결과가 나올 수 있습니다.

예를 들어, 영수와 철수가 각각 100만 원을 가지고 있고,

  • 영수가 철수에게 20만 원을 송금하고
  • 철수가 영수에게 21만 원을 동시에 송금한다면

우리는 영수 101만 원, 철수 99만 원이 되길 기대합니다.
그러나 각 요청이 동시에 처리되어 서로의 잔고 조회와 업데이트가 어긋난다면,

실제로는 철수가 20만 원을 받았음에도 잔고가 80만 원으로 잘못 계산되는 등 정합성 오류가 발생할 수 있습니다.

 

 

동시성 문제가 발생하는 환경

  1. 멀티 스레드 환경 (단일 서버 내부)
    • 동일한 JVM 프로세스 내에서 여러 스레드가 공유 자원에 접근할 경우 발생
    • synchronized, ReentrantLock, volatile, ConcurrentHashMap 등 Thread-safe한 처리 방식이 필요
  2. 멀티 프로세스 환경 (서버 여러 대)
    • MSA 구조나 로드밸런싱된 환경에서, 여러 서버 인스턴스가 동시에 같은 데이터를 처리하려 할 때 발생
    • 단일 서버의 락만으로는 제어 불가능하며, 외부 분산 락 또는 DB 기반 제어가 필요

 

동시성 문제 해결 전략


구분 전략 설명
DB 수준 트랜잭션 + 락 @Transactional + PESSIMISTIC_WRITE, OPTIMISTIC 등 DB 락을 활용해 충돌 방지
애플리케이션 수준 스레드 락 synchronized, ReentrantLock 등으로 JVM 내 스레드 동기화 처리
분산 환경 Redis 분산락 Redisson 등으로 분산된 서버 간 락 제어
비동기 직렬화 Queue 처리 Kafka, RabbitMQ 등의 메시지 큐를 활용해 순차적 처리를 통해 충돌 방지
트랜잭션 격리 수준 격리 레벨 조정 READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE 등으로 충돌 허용 범위 조절