24.12.06 TIL 동시성 제어

신성훈·2024년 12월 6일
0

TIL

목록 보기
94/162

1. 동시성 제어(Concurrency Control)란?

동시성 제어는 여러 프로세스나 스레드가 동시에 동일한 자원(데이터베이스, 파일 등)을 접근할 때 발생할 수 있는 문제를 방지하고, 데이터의 일관성과 무결성을 유지하기 위한 기술과 방법을 말합니다.


2. 동시성 문제의 예시

  1. Dirty Read

    • 한 트랜잭션이 처리 중인 데이터를 다른 트랜잭션이 읽는 경우
    • 트랜잭션이 롤백되면 잘못된 데이터를 읽게 됩니다.
  2. Lost Update

    • 두 트랜잭션이 같은 데이터를 동시에 수정하고, 한 트랜잭션의 변경 사항이 덮어씌워지는 문제
  3. Non-Repeatable Read

    • 같은 데이터를 두 번 읽을 때 값이 달라지는 상황
  4. Phantom Read

    • 첫 번째 쿼리에서는 없었던 데이터가, 두 번째 쿼리에서 보이는 문제

3. 동시성 제어의 방법

1) 비관적 잠금 (Pessimistic Lock)

  • 데이터를 수정하거나 조회하기 전에 잠금을 걸어 다른 트랜잭션이 접근하지 못하도록 합니다.
  • 장점: 동시성 문제가 거의 발생하지 않음
  • 단점: 트랜잭션 충돌이 적은 경우에도 성능 저하
// JPQL 예시
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT p FROM Product p WHERE p.id = :id")
Product findByIdForUpdate(@Param("id") Long id);

2) 낙관적 잠금 (Optimistic Lock)

  • 데이터를 수정할 때, 다른 트랜잭션이 변경하지 않았는지 확인하는 방식
  • 주로 버전 번호(version)를 사용해 충돌 여부를 판단
  • 장점: 충돌 가능성이 적은 경우 성능이 뛰어남
  • 단점: 충돌 발생 시 재시도 로직 필요
@Entity
public class Product {
    @Id
    @GeneratedValue
    private Long id;

    @Version
    private Integer version;  // 낙관적 잠금에 사용
}

3) 데이터베이스 수준의 동시성 제어

  • 데이터베이스에서 제공하는 트랜잭션 격리 수준을 설정
  • 트랜잭션 격리 수준: Read Uncommitted, Read Committed, Repeatable Read, Serializable
  • 격리 수준이 높을수록 동시성 문제가 줄어들지만 성능은 저하됩니다

4. 동시성 제어 구현

Java의 동시성 제어

  1. Synchronized

    • 특정 코드 블록에 대한 스레드 간 상호 배제를 보장
    public synchronized void increment() {
        count++;
    }
  2. ReentrantLock

    • 명시적으로 락을 제어하며, 더 세부적인 동시성 처리가 가능
    Lock lock = new ReentrantLock();
    lock.lock();
    try {
        // 작업
    } finally {
        lock.unlock();
    }

Spring과 동시성 제어

  1. @Transactional

    • 데이터베이스 트랜잭션을 관리하고 동시성 문제를 완화
    • 기본적으로 Read Committed 격리 수준을 사용
  2. Redis 및 분산 락

    • 다중 서버 환경에서 분산 락을 통해 동시성 제어
    • 예: Redisson 라이브러리를 사용
    RLock lock = redissonClient.getLock("lockKey");
    lock.lock();
    try {
        // 작업
    } finally {
        lock.unlock();
    }

5. 마무리

  • 처음에는 동시성 제어가 어렵게 느껴졌지만, 비관적 잠금과 낙관적 잠금을 배우면서 상황에 맞는 선택이 중요하다는 것을 알게 되었습니다.
  • 특히 낙관적 잠금은 성능을 유지하면서도 동시성 문제를 해결할 수 있어 효율적인 방법임을 배웠습니다.
  • 또한, Redis를 활용한 분산 락 구현을 통해 동시성 제어가 단일 서버뿐 아니라 분산 환경에서도 필요함을 느꼈습니다.
  • 앞으로 다양한 동시성 문제를 접하며, 더 효율적이고 안전한 코드를 작성하고 싶습니다.
profile
조급해하지 말고, 흐름을 만들고, 기록하면서 쌓아가자.

0개의 댓글