관리 메뉴

JIE0025

[JPA] 비관적락을 사용해 동시성 문제 해결하기 (curl command로 동시요청) 본문

백엔드/스프링, 스프링부트, JPA, Spring Webflux

[JPA] 비관적락을 사용해 동시성 문제 해결하기 (curl command로 동시요청)

Kangjieun11 2023. 11. 19. 01:46
728x90

 

 

✅ 선행 개념

먼저 비관적 락이 무엇인지에 대한 개념은 아래 글에 적어놓았다.

 

https://jie0025.tistory.com/603

 

비관적 락은 무엇이고 왜/언제 사용할까?

✅ 비관적 락 ? DB관리에서 사용하는 기술 중 하나이다. 충돌이 발생할 것 같으니 조취를 취하자. 간단하게 설명하면 어떤 이용자 A 가 데이터를 Read/Write할 때, 미리 락(Lock)을 걸어서 다른 사용자

jie0025.tistory.com

 

 

 

✅ 동시에 API 요청 넣어보기

 

락을 사용하지 않았을때 발생하는 문제를 직접 눈으로 확인해보자.


 

 

동시 요청을 보내는 가장 간단한 방법이 있다.

 

 

curl 커맨드  ?

CLI를 이용해 API 데이터를 요청할 수 있는,

HTTP 클라이언트 도구 중 하나 

 

 

* GET방식

옵션 없이 아래처럼 사용하면 된다. 

curl [원하는 요청 URL/URI]
$ curl https://jsonplaceholder.typicode.com/posts/1
{
  "userId": 1,
  "id": 1,
  "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}%

 

* POST / DELETE , 다른 방식을 사용해야할 땐 

-X,   --request 옵션을 사용한다.

$ curl https://jsonplaceholder.typicode.com/posts/1 -X DELETE
{}%

 

 

* Body, Header 설정

$ curl https://jsonplaceholder.typicode.com/posts \
  -d '{"title": "foo", "body": "bar", "userId": 1}' \
  -H 'Content-type: application/json; charset=UTF-8'
{
  "title": "foo",
  "body": "bar",
  "userId": 1,
  "id": 101
}%

 

 


 

💻 Terminal

아래 형태로 명령어를 입력하면 된다.  (완전 동시를 보장하지는 않지만,  동시에 여러 API 를 서버에 보내는것과 유사한 효과가 발생한다.)

 

 

😆 Example

콘서트 좌석 <2번>

3명의 유저<2,5,6>가 동시 예약 요청

curl -X POST 'http://localhost:8080/seats/reservations' -H 'Content-Type: application/json' -d '{"user": 2, "concertSeat": 2}' &
curl -X POST 'http://localhost:8080/seats/reservations' -H 'Content-Type: application/json' -d '{"user": 5, "concertSeat": 2}' &
curl -X POST 'http://localhost:8080/seats/reservations' -H 'Content-Type: application/json' -d '{"user": 6, "concertSeat": 2}' &
# ... 이런 식으로 계속 지정 가능

 

 

테스트 Response 응답

 

 

 

 

MYSQL까지 직접 확인해보자

 

절대 일어나면 안되는 일이 발생하는걸 볼 수 있다.

 

 

이런 이유로 우리는 비관적 락을 사용해서 동시성 제어를 해줘야한다.

 

 

 

 


 

 

✅ JPA와 Lock 사용하기

 

JPA에선 @Lock 어노테이션을 이용하면 간단하게 락을 사용할 수 있다. 

 

 

✔️ Lock의 적용 범위

  1. EntityManager.lock() , EntityManager.find(), EntityManager.refresh()
  2. Query.setLockMode()
  3. @NamedQuery

 

 

✔️ JPA - Lock 옵션

 

설명 타입 설명
낙관적 락(Optimisstic Lock) OPTIMISTIC 낙관적 Lock 사용
낙관적 락(Optimisstic Lock) OPTIMISTIC_FORCE_INCREMENT 낙관적 Lock + 버전 정보 강제 증가
비관적 락(Pessimistic Lock) PESSIMISTIC_READ 비관적 Lock, 읽기 Lock 사용
비관적 락(Pessimistic Lock) PESSIMISTIC_WRITE 비관적 Lock, 쓰기 Lock 사용
비관적 락(Pessimistic Lock) PESSIMISTIC_FORCE_INCREMENT 비관적 Lock + 버전 정보 강제 증가
기타 NONE 엔티티에 @Version이 있으면 낙관적 Lock을 적용함
기타 READ 하위 호환을 위한 것으로 OPTIMISTIC와 같음
기타 WRITE 하위 호환을 위한 것으로 OPTIMISTIC_FORCE_INVREMENT와 같음

 

 

콘서트 좌석 요청에 대한 것이기 때문에

비관적 락 - PESSIMISTIC_WRITE 타입을 사용해보면 될것 같다.

 

( *도서 - 자바 ORM 표준 JPA 프로그래밍 )

 


 

 

✅ JPA Repository 인터페이스를 사용하는 경우

 

Repository에 정의된 메서드 위 @Lock  어노테이션을 쓰면 된다. 

 

 

✍️ Java

나는 QueryDsl 사용 경우에 적용을 해서 

다른 곳에서 예제를 가져왔다. 

https://sabarada.tistory.com/187

public interface UserRepository extends JpaRepository<User, Long>, UserRepositoryV1Custom {
  @Lock(LockModeType.PESSIMISTIC_READ) // Lock 적용 (LockModeType을 이용해 옵션 변경)
  Optional<User> findWithPessimisticLockById(Long id);
}


✍️ kotlin

위 자바코드를 참고해서 만든 코틀린 예제!

@Repository
interface SeatRepository : JpaRepository<Seat, Long> {

    @Lock(LockModeType.PESSIMISTIC_WRITE)
    fun findBySeatNumber(number: Int): Seat?
}

 

✅ QueryDSL을 사용하는 경우 

 

setLockMode() 메서드를 사용하면 된다. 

 

    override fun findByNumber(number: Long?): myTable? {
            return jpaQueryFactory
                .selectFrom(qUser)
                .where(조건문)
                
                .setLockMode(LockModeType.PESSIMISTIC_WRITE) // 비관적 Lock 적용
                
                .fetchOne()
    }

 

 


 

✅ 동시 요청 테스트  결과

 

락이 잘 걸렸다면, 아래처럼 결과가 출력되는 것을 확인할 수 있다!

 

 

 

 

✔️ DB에서 확인해봐도 데이터는 1개만 들어갔다

 

 

 

 

 


References

https://www.daleseo.com/curl/

 

curl 커맨드: 터미널에서 HTTP 호출하기

Engineering Blog by Dale Seo

www.daleseo.com

https://velog.io/@znftm97/%EB%8F%99%EC%8B%9C%EC%84%B1-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0-V2-%EB%B9%84%EA%B4%80%EC%A0%81-%EB%9D%BDPessimistic-Lock

 

동시성 문제 해결하기 V2 - 비관적 락(Pessimistic Lock)

동시성 문제, 비관적 락, Pessimistic Lock

velog.io

 

https://velog.io/@hyojhand/%EC%83%81%ED%92%88-%EC%A3%BC%EB%AC%B8-%EB%8F%99%EC%8B%9C%EC%84%B1-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0-DeadLock-%EB%82%99%EA%B4%80%EC%A0%81-%EB%9D%BD-%EB%B9%84%EA%B4%80%EC%A0%81-%EB%9D%BD

 

상품 주문 동시성 문제 해결하기 - DeadLock, 낙관적 락(Optimistic Lock) & 비관적 락(Pessimistic Lock)

여러명의 사용자가 동시에 상품의 주문을 요청했을때 발생하는 동시성 문제의 해결과정

velog.io