동시성 문제

·2024년 9월 3일
0

CS

목록 보기
6/6
post-thumbnail

동시성 문제란?

  • 동시성 문제란 여러 스레드나 프로세스가 동시에 공유 자원에 접근하려고 할 때 발생하는 문제를 말한다. 두 개의 스레드가 동시에 같은 변수에 같은 값을 쓰려고 할 때, 한 스레드가 값을 갱신하기 전에 다른 스레드가 값을 읽어 버리면, 데이터가 일관성이 없게 될 수 있기 때문이다. 이런 상황을 ‘경쟁 상태’ 라고도 부른다.

동시성 문제의 해결

  • 이런 문제를 해결하기 위해 여러 동기화 메커니즘이 사용된다. 가장 일반적인 방법은 “락” 을 사용하는 것으로, 락을 사용하면 한 번에 하나의 스레드만 공유 자원에 접근할 수 있기 때문에 데이터의 일관성을 유지할 수 있다.
  • 하지만 락을 잘못 사용하게 된다면 “교착 상태(deadlock) 같은 새로운 문제가 생길 수 있다. 교착 상태는 두 개 이상의 스레드가 서로 상대방이 가지고 있는 락을 기다리면서 무한히 대기하는 상태를 말한다.

예를 들어?

  • 동일한 하나의 데이터에 2 이상의 스레드, 혹은 세션에서 가변 데이터를 동시에 제어할 때 나타나는 문제가 될 수 있겠다. 하나의 세션이 데이터를 수정 중인데, 다른 세션에 수정 전의 데이터를 조회해 로직을 처리함으로써 데이터의 “정합성” 이 깨지는 상태가 되는 것이다.

Race Condition

  • Race Condition 을 해결하기 위해서는 하나의 스레드가 작업을 완료한 후에, 다른 스레드가 공유된 작업에 접근 가능하도록 해야 한다.
  • 따라서, 이를 해결하기 위해서는 하나의 스레드가 작업을 완료한 후에, 다른 스레드가 공유된 자원에 접근 가능하도록 해야 한다.

그렇다면 Race Condition 을 막을 수 있는 방법에는 뭐가 있을까?

(1) Spring 의 동시성 제어, Synchronized

Synchronized 란?

  • 스레드 동기화를 위해 자바에서 지원하는 기술이다.
  • 하나의 공유 자원에 동시에 접근하지 못하도록 막는다. 공유되는 데이터의 Thread-safe 를 위해, synchronized 로 스레드간 동기화를 시켜 thread - safe 하게 만들어 준다.
  • @Transational 을 같이 사용하는 경우 동기화 문제가 그대로 발생할 수 있다.

⇒ 현재 데이터를 사용하고 있는 해당 스레드를 제외하고 나머지 스레드들은 데이터 접근을 막아 순차적으로 데이터에 접근할 수 있도록 해 준다.

Synchronized 의 문제점

  • 자바의 Synchronized 는 하나의 프로세스 안에서만 보장이 되며, 여러 서버에서 해당 공유 자원에 접근 시 원활하게 동작하지 않을 수가 있다.

(2) Optimistic Lock 사용하기

Optimistic Lock (낙관적 락) 이란?

  • DB Lock 대신 Version 관리를 통해 애플리케이션 level 에서 동시성을 처리하는 락을 말한다.
  • JPA 가 제공하는 버전관리 기능을 사용하며, 트랜잭션 커밋 전에는 트랜잭션 충돌을 알 수 없다.
  • 먼저 데이터를 읽은 후에 UPDATE 를 수행하며 현재 내가 읽은 버전이 맞는지 확인 후 업데이트
  • 자원에 락을 걸어서 선점하지 않고, 동시성 문제가 발생하면 그때 가서 처리한다.

Optimistic Lock 과정

(1) 서버 1 이 version1 임을 조건절에 명시하며 업데이트 쿼리를 날람.

(2) version1 쿼리가 업데이트 되어서 디비는 version2 가 됨.

(3) 서버 2가 version1 로 업데이트 쿼리를 날리면 버전이 맞지 않아 실패

(4) 쿼리가 실패하면 서버2 에서 다시 조회하며 버전을 맞춘 뒤 업데이트 쿼리를 날리는 과정을 거침

Optimistic Lock 의 장단점

  • 장점
    • 충돌이 나지 않는다는 가정 하에, 별도의 락을 잡지 않으므로 디비 제어보다 성능이 좋을 수 있다.
  • 단점
    • 업데이트 실패 시, 롤백 로직을 개발자가 직접 개발해야 함
    • 충돌이 빈번하게 일어나면 롤백 처리로 인해 디비 제어 방식보다 성능이 좋지 않을 수 있음

(3) Named Lock 사용하기

Named Lock 이란?

  • 이름을 가진 metadata locking 으로, 이름을 가진 lock 을 획득한 후 해제할 때까지 다른 세션은 이 lock 을 획득할 수 없도록 함.
  • 트랜잭션이 종료될 때, Lock 이 자동 해제되지 않기 때문에 별도의 명령이 필요
  • 디비 제어 방식과 유사하지만, Named Lock 은 metadata 단위로 락을 건다는 차이점이 존재한다.
    • 데이터 자체보다는 데이터에 관한 정보에 락을 거는 것이기 때문에, 데이터의 일관성보다는 특정 작업이 동시에 이루어지지 않도록 제어하는 데 주로 사용된다. 데이터베이스에서 특정 스크립트나 프로시저가 동시에 실행되지 않도록 할 때 유용히 사용될 수 있다.

Named Lock 의 장단점

  • 장점
    • Named Lock 은 주로 분산락을 구현할 때 사용한다
    • timeout 을 비교적 손쉽게 구현할 수 있다
  • 단점
    • Named Lock 은 트랜잭션 종료 시에 락 해제와 세션 관리를 해 주어야 한다.
    • 실제 사용할 때는 구현 방법이 복잡할 수 있다

(4) Redis 사용하기

Lettuce

  • setnx 명령어를 활용하여 분산락을 구현
  • spin lock 방식, retry 로직을 개발자가 작성 : Lock 을 획득하려는 스레드가 Lock 을 획득할 수 있는지 확인하면서 반복적으로 시도하는 방법

Redisson 사용하기

  • Pub-Sub 기반으로 Lock 구현 제공
  • Pub-Sub 방식이란, 채널을 하나 만들고, 락을 점유 중인 스레드가 락을 해제했음을 대기 중인 스레드에게 알려 주면 대기 중인 스레드가 락 점유를 시도하는 방식
  • 이 방식은 Lettuce 와 다르게 별도의 Retry 방식을 작성하지 않아도 되므로 깔끔
profile
자바 백엔드 개발자 개인 위키

0개의 댓글