Row Lock과 SELECT * FOR UPDATE
UPDATE / DELETE 없이, SELECT 만으로 Row Lock을 잡고 싶을 때는, "SELECT * FOR UPDATE" 쿼리를 사용하면 된다.
이렇게 Row Lock을 잡고있는 도중에는 다른 트랜잭션에서 해당 Row를 변경 / 삭제할 수 없다.
select_for_update
Django에서 SELECT * FOR UPDATE 쿼리를 사용할 때는, Django ORM select_for_update 함수를 사용하면 된다. 이 함수는 항상 transaction과 함께 사용된다.
Lock을 잡으려는데, 이미 Lock이 잡혀있는 경우
일반적으로는, 락이 풀릴 때까지 기다린다. ( 이 경우, DB Connection이 무한정 쌓이고 밀릴 수 있다. )
락이 풀릴때까지 기다리지 않고, 오류를 내뱉고 싶으면 select_for_update에 nowait=True 옵션을 추가하면 된다.
락이 잡혀있어도, 무시하고 싶으면 skip_locked=True 옵션을 추가하면 된다.
그래서 nowait 옵션과 skip_locked 옵션은 공존할 수 없다.
class UserPoint(models.Model):
id = models.AutoField(primary_key=True)
user = models.ForeignKey(User)
total_point = models.IntegerField(default=0)
with transaction.atomic():
user_point = UserPoint.objects.get(pk=id).select_for_update()
user_point.total_point += point
user_point.save()
주의사항
Django에서 select_for_update를 사용하면, select_related 되어있는 테이블이나, FK으로 연결되어있는 객체 테이블에도 락이 함께 잡힌다.
예를 들면, 아래와 같은 모델에 select_for_update를 적용하게 되면, user 테이블에도 락이 잡힌다.
of 옵션을 주면, 락 잡을 테이블을 정할 수 있다. ( Django 2.0부터 사용할 수 있다 )
그런데 FK로 연결되어있는 레코드의 PK가 변경되지 않는다면, 락이 함께 잡혀도 별 영향은 없어보인다. ( Selecting for Share and Update in PostgreSQL - The Effect of Select For Update on Foreign Keys )
참고 글
'소프트웨어-이야기 > 프로그래밍 언어와 프레임워크' 카테고리의 다른 글
(Django) cached_property 란? (1) | 2019.04.20 |
---|---|
(saleor) 가독성 좋은 함수명 만들 때 유용한 팁 모음집 (0) | 2019.02.10 |
(Django) Django로 e-commerce 개발할 때 참고하기 좋은 오픈소스 (0) | 2018.10.20 |
(Django) Rest Frame Work Filter 활용하기 (0) | 2018.09.09 |
(Django) DB Connection을 관리하는 방법 (0) | 2018.08.04 |