Django 데이터베이스 동시성 해결

홍준식·2024년 2월 18일

때로는 데이터베이스를 설계할 때에 한 번 값이 입력되면 해당 값이 변경되지 않음을 보장해야하는 경우가 존재한다.

여러 사용자가 동시에 한 게시글을 수정하려고 할 때에 다른 사용자가 같은 게시글을 수정하지 못하고 최초로 변경한 변경사항만 반영되어야 한다고 하는 경우

만약 데이터베이스 락 없이 해당 케이스에 여러명이 동시에 요청을 한다면 최종 요청에 대한 정보가 데이터베이스에 기록되게 된다.

이를 해결하기 위해 첫번째 요청이 들어온 행에 shared lock 혹은 exclusive lock을 거는 방법인 비관적 락 방법과 어플리케이션 레벨에서 충돌을 막는 낙관적 락 방법이 존재한다.

비관적 락

Django에서 비관적 락을 거는 방법은 다음과 같다.

from django.db import transaction

posts = Post.objects.select_for_update().filter(title='변경할게시글')
with transaction.atomic():
    for post in posts:
        ...

위와 같이 select_for_update를 통해 호출하면 모든 매칭되는 쿼리들은 row에 lock이 걸리게된다.

하지만 위와 같은 방법은 모든 요청에 대해 해당 row에 락을 걸게 되어 충돌하지 않는 경우에도 성능이 저하되는 문제가 발생한다.

이를 위해 만약 충돌이 많지 않을 것으로 예상된다면 어플리케이션 레벨에서 사용하는 낙관적 락을 사용할 수 있다.

낙관적 락

from django.db import models

Post.objects.filter(
	title='변경할게시글',
	version=1,
).update(
	...
)

위와 같이 Post에 version 필드를 추가하거나 update_at 필드를 추가하고 update 쿼리를 호출하는 것으로 동시성을 제어할 수 있다.

위와 비슷하게 만약 테이블의 한 컬럼이 입력된 후에 변경되는 것을 막으려면

from django.db import models

Post.objects.filter(
	title='변경할게시글',
	content='',
).update(
	content=content,
)

위와 같이 동작하도록 하여 한번 content가 작성되면 이후의 요청들은 무시되도록 설정할 수 있다.

0개의 댓글