동시성 처리하기

minisoo·2024년 2월 18일
0

최근 동시성 제어하기 강의를 들으면서 분산락이라는 용어를 접하게되었는데 업무 하면서 사용한적은 없지만, 인프런 인강을 통해 개념을 배우고 예제 코드를 작성해봤다!
해당 내용을 정리해보고자 한다 :)

동시성 문제

하나의 자원에 두개 이상의 스레드가 동시에 접근하면서 데이터의 정합성이 깨지는 문제

해결방법 탐구

비관적 락 (Pessimistic Lock)

자원 요청에 따라 동시성 문제가 발생할 것이라고 예상하고 락을 거는 방식

트랜잭션 시작 시 s-lock 또는 x-lock을 걸게 된다

Shared Lock(공유 잠금)

  • 읽는 동안 수정이 발생하지 않게 잠그는 것
  • 세션 A가 특정 데이터를 읽기 위해 s-lock을 획득한 경우, 다른 세션 B도 같은 데이터에 s-lock을 걸고 읽을 수 있지만 어떤 세션도 해당 데이터에 x-lock을 걸고 수정할 수 없다
  • s락 끼리는 호환이 가능하다

Exclusive Lock(배타 잠금)

  • 쓰는 동안 수정이 발생하지 않게 잠그는 것
  • 세션 A가 특정 데이터를 수정하기 위해 x-lock을 획득한 경우, 다른 세션 B가 같은 데이터에 s-lock을 획득하려고 하면 대기상태에 빠진다

테스트해보기

  1. A세션 공유 잠금 획득

    1-1. B세션 공유 잠금 획득 가능

    1-2. B세션 배타 잠금 획득 불가능 (대기상태)

    1-3. B세션에서 데이터 수정 불가능 (대기상태)

    1-4. B세션에서 데이터 조회 가능!

  2. A세션 배타잠금 획득

    2-1. B세션 데이터 조회 가능

    2-2. B세션 공유잠금 획득 불가능 (대기상태)

    2-3. B세션 배타잠금 획득 불가능 (대기상태)

    2-4. B세션 데이터 수정 불가능 (대기상태)


JPA의 동시성 제어 매커니즘 - 비관적 락

PESSIMISTIC_WRITE
select ... for update를 사용
하여 배타 잠금 사용
PESSIMISTIC_READ
select ... for share를 사용하여 공유 잠금 사용
PESSIMISTICFORCEINCREMENT
버전정보를 사용하는 비관적 락

PESSIMISTIC_WRITE 사용해보기

비관적 락을 사용하여 동시성을 제어하는경우 락을 통해 데이터 업데이트를 제어하기 때문에 정합성이 보장된다
단점으로는 락을 잡기 때문에 성능 감소가 있을 수 있다

낙관적 락 (Optimistic Lock)

트랜잭션 충돌이 발생하지 않을 것이라고 가정하는 방법으로, 데이터베이스가 제공하는 락을 사용하지 않고 엔티티 버전을 통해 동
시성을 제어하는 방법

일반적으로 버전을 통해 충돌을 확인하며, 충돌이 확인된 경우 롤백 처리한다
충돌이 안난다는 가정하에, 동시 요청에 대한 처리 성능이 좋지만 잦은 충돌이 일어나는 경우 롤백처리로 오히려 성능 감소가 있을 수 있다

JPA @Version 사용

분산락

여러서버에서 공유된 데이터를 제어하기 위해 사용하는 기술
락에 대한 정보를 어딘가에 공통적으로 보관하고 있어야 하는데, 여러대의 서버들은 공통된 어딘가를 바라보며 자신이 임계 영역에 접근할 수 있는지 확인한다
그 어딘가로 활용되는 기술이 Mysql의 네임드락, redis, zookeeper등이 있다

Mysql Named Lock

이름을 가진 메타데이터 락
pessimistic, optimistic lock은 stock에 대해 lock을 걸었다면 named lock은 별도 mysql 서버 공간에 lock을 걸게된다

Lettuce

Java redis client중 하나
구현이 비교적 간단하며, 별도의 라이브러리를 사용하지 않아도 된다
하지만 setnx, setex과 같은 명령어를 이용해 지속적으로 Redis에게 락이 해제되었는지 요청을 보내는 스핀락 방식으로 동작하기 때문에 요청이 많을수록 redis가 받는 부하가 커지게 된다

Redisson

Redisson도 java redis client중 하나로 lettuce와는 동작 구조가 다르다
redisson은 pub-sub 방식으로 구현되어있기 때문에 lettuce와 비교했을 때 redis에 부하가 덜 가게 된다
pub/sub 방식은 락이 해제될 때마다 구독중인 클라이언트들에게 알림을 보내기 때문에 클라이언트 측에서는 락 획득에 실패했을 때, redis에 계속 락 획득 요청을 하는 과정이 사라지게 되며, 계속된 요청으로 인한 부하가 발생하지 않게 된다
하지만 redis 명령과 redisson 메소드 명의 대부분 다르기 때문에 사용법 숙지가 필요하며 별도 라이브러리를 사용해야 된다

여기서 알게된 점 ..!

애플리케이션 공유 자원 = 데이터베이스 라고 생각하고 비관적 락으로 풀어낼 수 있는게 아닐까? 하고 생각했었다
하지만 동시성 문제는 인스턴스 필드 또는 static과 같은 공용 필드 접근에 대해서도 발생할 수 있다

또한 낙/비관적 락의 관심사는 엔티티에 대한 동시 접근 제어이다
동시성을 제어하기 위해서는 엔티티가 존재해야하며, 그렇기 때문에 엔티티 생성 개수를 제한하는 요구사항에서는 문제가 발생할 수 있다

분산된 DB환경에서는 분산락을 통해 동시성 제어가 필요하다
DB에서 제공하는 낙/비관적 락은 보통 레코드나 테이블과 같은 자원에 락을 걸지만, 분산락은 로직, API등과 같은 자원에 접근하려는 대상에 대해 락을 건다


[참고자료]
https://hudi.blog/jpa-concurrency-control-optimistic-lock-and-pessimistic-lock/
https://helloworld.kurly.com/blog/distributed-redisson-lock/
https://wildeveloperetrain.tistory.com/280
https://www.inflearn.com/course/%EB%8F%99%EC%8B%9C%EC%84%B1%EC%9D%B4%EC%8A%88-%EC%9E%AC%EA%B3%A0%EC%8B%9C%EC%8A%A4%ED%85%9C/dashboard
https://hudi.blog/distributed-lock-with-redis/
https://sjparkk-dev1og.tistory.com/225

profile
코딩하는 돌멩이 👻

2개의 댓글

comment-user-thumbnail
2024년 2월 28일

👍🏻 좋은 글이네요.

1개의 답글