동시성을 제어하는 방법

분산락과 네임드락

Posted by Yan on February 12, 2025

동시성 제어 (Concurrency Control)

동시에 실행되는 여러 트랜잭션의 실행 순서를 제어하여 데이터의 일관성과 무결성을 보장하는 것.
특히 멀티스레드 환경에서는 공유 자원(예: 데이터베이스, 변수)에 대한 동시 접근을 조정해야 한다.

동시성 제어의 주요 목적

  • 트랜잭션의 직렬성 보장 : 트랜잭션이 순차적으로 수행되는가?
  • 공유도 최대화
  • 응답시간 최소화
  • 시스템 활동의 최대

동시성을 제어해야 하는 이유?

동시성을 제어하지 않을 경우, 아래와 같은 현상이 생길 수 있다.

  1. Race Condition (경쟁상태)
    • 둘 이상의 프로세스나 스레드가 공유 자원에 동시에 접근하여 결과값에 예측할 수 없는 영향을 줄 수 있는 상태를 말한다. 이는 공유 데이터의 최종값을 보장할 수 없는 상황을 초래할 수 있다.
  2. Dead Lock (교착상태)
    • 두 개 이상의 트랜잭션이 서로가 보유한 자원의 잠금(Lock)을 해제하기를 기다리며 진행하지 못하는 상태다. 이는 시스템에서 프로세스들이 서로의 자원을 기다리며 무한히 대기하는 상황을 초래한다.
  3. Data Corruption (데이터 손상)
    • 동시성 제어가 제대로 이루어지지 않을 경우, 여러 트랜잭션이 동시에 같은 데이터에 접근하여 데이터의 일관성을 해치는 상황이 발생할 수 있다. 이는 데이터베이스의 무결성을 위협하는 심각한 문제다.
  4. Lost Update (데이터 손실)
    • 하나의 트랜잭션이 갱신한 내용을 다른 트랜잭션이 덮어씀으로써 갱신이 무효화가 되는 것을 의미
    • 두 개이상 트랜잭션이 한 개의 데이터를 동시에 갱신(Update)할 때 발생
  5. 현황파악오류 (Dirty Read)
  6. 모순성 (Inconsistency)
    • 다른 트랜잭션들이 해당 항목 값을 갱신하는 동안 한 트랜잭션이 두 개의 항목 값 중 어떤 것은 갱신되기 전의 값을 읽고 다른 것은 갱신된 후의 값을 읽게 되어 데이터의 불일치가 발생하는 상황
  7. 연쇄복귀 (Cascading Rollback)
    • 두 트랜잭션이 동일한 데이터 내용을 접근할 때 발생
    • 한 트랜잭션이 데이터를 갱신한 다음 실패하여 Rollback 연산을 수행하는 과정에서 갱신과 Rollback 연산을 실행하고 있는 사이에 해당 데이터를 읽어서 사용할 때 발생할 수 있는 문제

동시성Concurrency과 병렬성Parallelism은 어떻게 다른 것인가?

동시성은 싱글 코어에서도 가능하지만, 병렬성은 멀티 코어에서만 가능하다.
동시성은 물리적으로 동시에 실행되는 것이 아니라 논리적으로 동시에 실행되는 것처럼 보이는 것이다.

동시성(Concurrency)

  • 여러 작업(스레드, 프로세스)이 번갈아 실행되면서 동시에 실행되는 것처럼 보이는 성질을 의미한다.
  • 싱글 코어 CPU에서도 동시성을 구현할 수 있다. → 운영체제(OS) 스케줄러가 빠르게 여러 스레드를 전환하면서 실행되므로, 마치 동시에 실행되는 것처럼 보인다.

멀티 스레드는 동시성을 구현하는 방법 중 하나일 뿐, 동시성 자체와 동일한 개념이 아니다.

예시:

  • 싱글 스레드에서 코루틴(Coroutine) 을 사용하여 동시성을 만족할 수 있음 (예: Kotlin 코루틴) : 코루틴은 실제로 하나의 스레드에서 여러 개의 작업을 동시 실행하는 것처럼 보이게 만든다.
  • Java의 ExecutorService 를 활용한 멀티스레딩 기반 동시 처리
  • Node.js의 이벤트 루프 기반 비동기 처리 (싱글 스레드에서도 동시성을 제공)

병렬성(Parallelism)

  • 여러 작업이 물리적으로 동시에 실행되는 것을 의미한다.
  • 멀티 코어 CPU에서 여러 개의 작업을 실제로 동시에 실행할 수 있다.

즉, 병렬성은 동시성과 다르게, 실제로 여러 작업이 동시에 수행되는 것을 의미한다.

예시:

  • 멀티 코어 CPU에서 각 코어가 서로 다른 스레드를 동시에 실행하는 경우
  • GPU에서 여러 개의 연산을 병렬로 처리하는 경우

동시성 제어 기법들

Locking

  • 데이터에 대한 접근을 물리적으로 제어
  • 트랜잭션이 데이터에 잠금(lock)을 설정하면 다른 트랜잭션은 해당 데이터에 대해 잠금이 해제(unlock)될 때까지 접근/수정/삭제가 불가하다.
  • 트랜잭션이 사용하는 자원에 대하여 상호 배제(Mutual Exclusive) 기능을 제공하는 기법

Timestamp

  • 각 트랜잭션과 데이터 항목에 타임스탬프를 부여하여 논리적으로 접근을 제어
  • 타임스탬프를 기반으로 트랜잭션의 실행 순서를 결정

Locking vs Timestamp

어떤 방식을 사용할지는 시스템의 특성(트랜잭션 충돌 빈도, 성능 요구사항 등)에 따라 결정해야 함!

비교 항목 락킹(Locking) 타임스탬프(Timestamp)
접근 방식 데이터 접근을 물리적으로 제어하며, 트랜잭션이 접근 전 락을 획득하고 작업 후 해제 각 트랜잭션과 데이터 항목에 타임스탬프를 부여하여 논리적으로 접근 제어
충돌 처리 충돌 발생 시, 한 트랜잭션이 락을 획득할 때까지 대기 (교착 상태 발생 가능) 충돌 발생 시, 타임스탬프를 비교하여 즉시 롤백 또는 작업 수행 (교착 상태 없음)
동시성 수준 락 범위 및 유지 시간에 따라 동시성이 달라짐 (보수적인 락킹은 동시성 저하) 일반적으로 락킹보다 높은 동시성을 제공, 병렬 실행 가능
구현 복잡성 구현이 상대적으로 간단하며, 2PL, 낙관적 락킹 등 다양한 기법 존재 구현이 복잡할 수 있으며, 시스템 전체의 일관된 시간 관리 필요
성능 특성 충돌이 빈번한 환경에서는 대기 시간이 길어질 수 있음 충돌이 적은 환경에서 효율적이지만, 롤백으로 인한 오버헤드 발생 가능
장점 데이터의 일관성을 직관적으로 보장 교착 상태가 발생하지 않으며, 동시성이 높음
단점 교착 상태 발생 가능, 동시성 저하 우려 롤백이 많으면 성능 저하 가능