본문 바로가기

스파르타 부트캠프(spring)

[내일배움캠프_Spring] 자바 Spring 입문_동시성 처리_230304

참여 인원 수 만큼 Enrollment의 status를 TRUE로 업데이트 하는 경우, 동시에 여러 사용자가 동시에 참여 신청을 한다면, 여러 개의 Enrollment이 동시에 생성되고 업데이트되어 데이터 불일치 문제가 발생할 수 있습니다.

이를 해결하기 위해서는 동시성 문제를 처리해주는 메커니즘이 필요합니다. 여러 가지 방법이 있지만, 일반적으로 다음과 같은 방법을 사용합니다.

  1. Optimistic Locking

Optimistic Locking은 동시성 문제를 처리하는 방법 중 하나입니다. 이 방법은 여러 개의 사용자가 동시에 같은 데이터를 수정하려고 할 때, 데이터에 버전 정보를 추가해서 충돌을 감지하는 방법입니다.

만약 버전 정보가 일치하지 않는 경우, 충돌이 발생한 것으로 판단하여 다시 시도하도록 유도합니다. 이를 통해 동시성 문제를 처리할 수 있습니다.

  1. Pessimistic Locking

Pessimistic Locking은 데이터에 대한 잠금(lock)을 걸어서 여러 사용자가 동시에 접근하는 것을 막는 방법입니다. 데이터에 접근하는 동안 다른 사용자가 접근하지 못하도록 잠그는 것이며, 이를 통해 동시성 문제를 해결할 수 있습니다.

하지만 이 방법은 다른 사용자의 접근을 막기 때문에 성능 문제가 발생할 수 있으므로 사용에 주의가 필요합니다.

  1. Atomic Operations

Atomic Operations은 여러 개의 작은 연산으로 구성된 하나의 연산을 실행할 때, 동시성 문제를 처리하는 방법입니다. 이 방법은 작은 연산이 순차적으로 실행되도록 보장하여 데이터 불일치 문제를 해결합니다.

이러한 방법들 중에서, Optimistic Locking을 사용하는 것이 가장 일반적인 방법입니다. Enrollment의 status를 업데이트할 때, 버전 정보를 추가해서 충돌을 감지하고, 충돌이 발생한 경우 다시 시도하도록 처리하는 방식으로 동시성 문제를 처리할 수 있습니다.


 

Optimistic Locking은 데이터 충돌을 방지하기 위한 기법 중 하나입니다.

여러 사용자가 동시에 같은 데이터를 수정하려고 할 때 충돌이 발생할 수 있는데, Optimistic Locking은 이를 방지하기 위해 버전 관리를 하는 방법입니다.

데이터베이스 테이블에 버전 정보(version)를 추가하여 데이터 수정 시마다 버전 정보를 비교하고, 일치하지 않으면 다른 사용자가 이미 수정한 것으로 판단하여 예외를 발생시킵니다. 이를 통해 동시성 문제를 해결할 수 있습니다.

Spring에서는 JPA에서 제공하는 Optimistic Locking 기능을 사용할 수 있습니다. @Version 어노테이션을 엔티티 클래스의 필드에 추가하면 해당 필드를 버전 관리에 사용하게 됩니다.


  1. Optimistic Locking 방식을 많이 사용한다고 함.(//선택한 이유를 더 적자. redis에서 동시성 제어가 가능하다고 하는데 그건 어떨까)

Optimistic Locking은 데이터베이스 트랜잭션 충돌을 방지하기 위해 비관적인(lock) 동시성 제어 대신에 낙관적인(optimistic) 방식으로 동시성 제어를 하는 방법입니다.

장점:

  • 성능이 좋습니다. 비관적인 동시성 제어 방식보다 빠릅니다.
  • 충돌이 발생할 가능성이 낮기 때문에 재시도(retry)를 줄일 수 있습니다.

단점:

  • 충돌이 발생하는 경우 다시 시도해야 합니다. 이를 위해 클라이언트에서 재시도 로직을 구현해야 합니다.
  • 데이터 변경 사항이 자주 일어나는 경우 충돌이 발생할 확률이 높아지기 때문에 적용하기 어렵습니다.

또한 Redis Lock 방식과 Optimistic Locking 방식은 동시성 제어의 방법이 다르기 때문에 상황에 따라 적용해야 할 방법이 달라집니다. Redis Lock 방식은 외부 서비스와의 연동을 필요로 하지만, Optimistic Locking 방식은 데이터베이스에서 제공하는 기능을 활용하기 때문에 애플리케이션 내부에서 구현이 가능합니다.


Optimistic Locking은 데이터를 수정하기 전에 데이터가 다른 곳에서 수정되었는지 체크하는 방식의 동시성 제어 방법입니다.

Optimistic Locking은 다음과 같은 과정을 거칩니다.

  1. 데이터를 읽어옵니다.
  2. 데이터를 수정하기 전에 버전을 체크합니다.
  3. 버전이 일치하면 데이터를 수정하고 저장합니다. 버전이 일치하지 않으면 다른 곳에서 이미 데이터가 수정되었다는 것이므로 처리를 중단하고 에러를 발생시킵니다.

예를 들어, A와 B가 동시에 같은 데이터를 수정하려고 합니다. A는 데이터를 먼저 읽어와서 수정을 시도하고, 이때 B도 데이터를 읽어와서 수정을 시도합니다. 이때 Optimistic Locking을 사용하면 B가 먼저 데이터를 수정하고 저장하면서 버전이 증가합니다. 그리고 A가 데이터를 수정하려고 할 때 버전을 체크하면 B가 먼저 데이터를 수정하고 버전이 증가했으므로 에러가 발생합니다. 이를 통해 동시성 문제를 방지할 수 있습니다.

하지만 Optimistic Locking은 데이터의 충돌이 발생할 가능성이 높은 경우에는 사용하기 어려울 수 있습니다. 또한, 충돌이 발생하면 에러를 발생시키므로 충돌을 처리하는 추가적인 작업이 필요합니다.


Optimistic Locking을 적용하기 위해서는 다음과 같은 단계가 필요합니다.

  1. 엔티티 클래스에 버전 정보를 추가합니다.
    • 버전 정보는 JPA에서 지원하는 @Version 어노테이션을 이용합니다.
    • 버전 정보는 숫자형(int, long)이어야 하며, 각 엔티티마다 별도로 관리됩니다.
    • 엔티티가 수정될 때마다 버전 정보가 자동으로 증가합니다.
    • 즉, 버전 정보를 통해 엔티티가 수정될 때 다른 트랜잭션이 수정을 시도하면 버전 정보가 달라져서 에러가 발생하게 됩니다.
  2. 서비스 메서드에서 엔티티를 조회하고 수정합니다.
    • 엔티티를 조회한 후에는 해당 엔티티의 버전 정보를 저장합니다.
    • 엔티티를 수정할 때 버전 정보를 함께 저장합니다.
    • 이후에 엔티티를 저장할 때, 버전 정보가 다르다면 OptimisticLockException이 발생합니다.
    • 이때는 롤백을 수행하고, 사용자에게 에러 메시지를 출력해줍니다.

예를 들어, 위에서 구현한 EnrollmentServiceImpl에서 Enrollment 엔티티 클래스에 @Version 어노테이션을 추가하고, attend() 메서드에서 엔티티를 조회하고 수정하는 코드에 버전 정보를 추가하여 Optimistic Locking을 적용할 수 있습니다. 이때, attend() 메서드에서 예외가 발생했을 때는 롤백을 수행하고, 사용자에게 적절한 에러 메시지를 출력해주어야 합니다.

 

@Version


동시성을 테스트하는 코드는 복잡하고 다양한 요인들이 있어 일반적으로 작성하기 어렵습니다. 하지만 몇 가지 기본적인 방법들이 있습니다.

  1. 멀티스레드 환경에서의 테스트 병렬적으로 동작하는 스레드를 생성하여 해당 스레드에서 동시에 동일한 작업을 수행하는 것이 일반적인 방법입니다. 이 때, 스레드가 동시에 데이터에 접근할 때 발생할 수 있는 문제점을 재현할 수 있습니다.
  2. 병렬 프로세스를 이용한 테스트 병렬 프로세스를 이용하여 여러 개의 프로세스에서 동시에 동일한 작업을 수행하는 것도 가능합니다. 이 방법은 멀티스레드 테스트보다 더 복잡하고 많은 자원이 필요하지만, 실제 서버에서의 상황과 더 유사한 환경에서 테스트할 수 있습니다.
  3. 동시성 테스트 도구 사용 동시성 테스트를 위한 다양한 도구들이 존재합니다. 예를 들어, Apache JMeter, Gatling, Taurus 등의 도구들은 멀티스레드, 병렬 프로세스 등의 방식으로 동시성 테스트를 수행할 수 있습니다.
  4. 테스트 데이터의 생성과 유지 동시성 테스트를 위해서는 많은 양의 테스트 데이터가 필요합니다. 이를 위해서는 데이터의 생성과 유지를 자동화해야 합니다. 예를 들어, Faker,