Django Docs | select_for_update()

Jihun Kim·2022년 7월 26일
2

Django Docs

목록 보기
8/9
post-thumbnail

이 글은 장고 Docs를 참고해 작성했습니다.


앱을 사용하다 보면 여러 사람이 동시에 같은 row를 조회하게 되는 경우가 생길 수 있다.

가령 고객에게 DB에 있는 상품권을 지급하는 경우 여러 유저가 동시에 상품권을 조회하게 된다면 같은 상품권을 조회하게 될 수 있다는 문제점이 있다. 물론 상품권 조회에 대해 transaction lock을 걸어 놓겠지만 그렇게 되면 동시에 접근한 두 유저 중 한 유저는 에러를 보게 될 것이다.

이런 문제점을 해결하기 위해 사용할 수 있는 쿼리셋이 바로 select_for_update()이다. 이 쿼리셋을 이용하면 락이 걸린 rows는 건너 뛰거나 아니면 트랜잭션이 끝날 때까지 기다리도록 설정할 수 있다.


select_for_update()

Raw Query

select_for_update()를 사용하면 이 쿼리셋을 통해 만들어지는 로우쿼리는 SELECT ... FOR UPDATE 형태가 된다.

사용 방법

Parameters

  • skip_locked
    - True로 설정하면 락이 걸린 rows는 넘어가고 락이 걸리지 않은 rows를 찾는다.
  • nowait
    - True인 경우 락이 걸린 트랜잭션이 끝날 때까지 기다리지 않고 바로 DatabaseError를 뱉어 낸다.
    - False인 경우 해당 row에 락이 걸린 트랜잭션이 끝날 때까지 기다렸다가 끝나면 작업을 진행한다.

추가 옵션

  • of
    - 기본적으로 select_for_update()는 조회하는 모든 쿼리에 락을 잡는다. 따라서 select시 참조하는 모든 모델에 락을 잡는다. 그런데 만약 이러한 상황을 원하지 않는다면 of 옵션을 이용해 원하는 objects를 지정해 지정된 objects만 락을 잡도록 설정할 수 있다.
    - self로 지정할 경우 자기 자신만 조회하도록 한다.

주의 사항

  1. transaction.atomic() 블럭 안에서 select_for_update()를 사용해야 한다.

    • 그렇지 않으면 select_for_update()는 작동하지 않는다.
    • 이 블럭 안에 있으면 트랜잭션 블럭이 끝나기 전까지 블럭 안에서 조회한 쿼리에 락이 잡히게 된다. 따라서, 다른 트랜잭션은 이 트랜잭션을 획득하거나 변화를 줄 수 없다.
  2. skip_locked 옵션과 nowait 옵션은 상호 배타적인 관계로, 만약 두 옵션을 모두 사용하게 되면 ValueError가 발생 한다.

  3. select_for_update()는 null을 참조하는 경우에는 사용할 수 없다.

    • 따라서, 이런 경우 exclude를 이용해 None인 경우를 제외하고 사용해야 한다.
      Person.objects.select_related('hometown').select_for_update().exclude(hometown=None)
  4. select_for_update()는 eager loading을 한다.

    • 개발 하면서 알게 된 내용으로, select_for_update() 사용시 where 조건을 걸어 특정 유저 혹은 id에 대한 쿼리를 실행하지 않으면 해당 테이블 전체에 락을 잡아 앱의 성능이 매우 느려지는 이슈가 발생한다.
    • 아마도 select_for_update()는 기존의 select 쿼리와 달리 db를 바로 조회하는 것 같다.
    • 따라서, 아래와 같이 사용해야 한다.
    # 사용 O 	
     order = Order.objects.select_for_update(nowait=False).get(user=user)
    
     # 사용 X
     order_qs = Order.objects.select_for_update(nowait=False)
     order = order_qs.get(user=user)


profile
쿄쿄

1개의 댓글

comment-user-thumbnail
2023년 5월 29일

잘 읽고 갑니다

답글 달기