Transaction 사용하기: #1 Django와 Mysql의 transaction

정재혁·2022년 11월 20일
0

django의 transaction 사용법을 간단하게 설명하고, Mysql의 transaction 전략들을 알아본다.

Django의 transaction 사용법

간단하게 설명하자면 Django의 transaction은 아래와 같이 사용한다.

def _insert_audio_and_update_indexs(
        self, project_id: int, index: int, text: str, speed: int, user_id: int
    ) -> dict:
        """
        오디오를 지정한 위치에 생성하고, 뒤로 밀려나게 되는 audio의 index를 업데이트 합니다.
        생성된 오디오 정보를 dict로 return
        """
        with transaction.atomic():
            self._update_audios_index(project_id, index, is_increase=True)
            create_params = {
                "project": project_id,
                "index": index,
                "text": text,
                "speed": speed,
                "user": user_id,
            }
            new_audio = audio_repo.create(create_params)
            return new_audio

위의 함수는, 어떤 프로젝트에 1:N 관계에 있는 audio들의 index라는 컬럼 값을 1씩 증가 시키는 bulk update를 수행한 후, 새 audio를 만드는 로직을 갖고 있다. 기존의 audio들의 index 컬럼 값의 업데이트와 새로 만들어지는 audio는 비지니스 로직으로 하나의 연산으로 간주되므로, transaction 처리를 해주어야 한다. transaction.atomic context에서 에러가 감지되면, 변경사항은 DB에 적용되지 않고 rollback 될것이다.

Mysql의 transaction

당연하게도, Django의 transactoin은 Mysql의 transcation API를 호출하여 사용할 것이다.

Mysql의 transaction에 대하여 알아보자.

transaction
Transactions are atomic units of work that can be committed or rolled back. When a transaction makes multiple changes to the database, either all the changes succeed when the transaction is committed, or all the changes are undone when the transaction is rolled back.
Database transactions, as implemented by InnoDB, have properties that are collectively known by the acronym ACID, for atomicity, consistency, isolation, and durability.
출처: Mysql 공식문서, https://dev.mysql.com/doc/refman/8.0/en/glossary.html#glos_transaction

해석하자면,

transaction은 커밋되거나 롤백될 수 있는 하나의 작업 단위입니다. 트랜잭션이 데이터베이스를 여러 번 변경할 경우 트랜잭션이 커밋될 때 모든 변경사항이 성공하거나 트랜잭션이 롤백될 때 모든 변경사항이 실행 취소됩니다. InnoDB에 의해 구현된 데이터베이스 트랜잭션은 원자성, 일관성, 분리 및 내구성을 위해 ACID라는 약어로 총칭되는 속성을 갖습니다.

라는 뜻이다.

transaction을 만들 때 lock을 걸어서 데이터의 일관성을 지켜내고, rollback과 commit하는 일은 비용이 큰 작업들이다. 이에, trade-off 들로 다양한 lock 전략들이 나왔다.

Mysql은 이러한 전략들을 isolation level 로 나누어 두었다.

Mysql의 isolation level

  • READ COMMITTED
    • 성능을 위하여, transaction간의 보호를 어느정도 유연하게 가져가는 level
    • A transaction은 다른 B transaction이 commit하지 않은 데이터는 볼 수 없으나, B transaction이 A transaction이 만들어진 후, commit 했다면 해당 데이터를 읽을 수 있음
    • update, delete를 수행하는 transaction은 다른 transaction이 있다면 기다려야함
    • select는 기다리지 않고 바로 수행함
    • 잘못된 데이터(bad data)는 select하지 않지만, 시간에 따라 동일한 데이터가 나오지 않을 수 있음
  • READ UNCOMMITTED
    • transaction간의 보호를 최소화한 isolation level
    • query는 다른 transaction 을 기다리지 않는 전략을 취함
    • 다른 transaction에 의해, 커밋되지 않은 데이터를 포함할 수 있음
    • 사용에 매우 주의를 요함
    • 보통 insert, update, delete를 하지 않는 경우에만 사용
  • REPEATABLE READ (Default)
    • 쿼리된 row가 다른 transaction에 의해 변경되는 것을 방지
    • 한 transcation 내에서는, transaction이 시작된 상태의 snap shot 데이터를 사용
    • non-repeatable read는 막지만, phantom read는 막지않음
  • SERIALIZABLE
    • 가장 강력한 lock 전략을 사용
    • 현재 transaction이 읽혀진 데이터에 어떠한 insert나 변경도 허용하지 않음
    • 동일한 쿼리는 계속해서 같은 결과를 얻을 수 있음
    • 현재 트랜잭션이 시작된 이후 다른 트랜잭션에 의해 커밋된 데이터를 변경하려고 하면 현재 트랜잭션이 대기
    • SQL이 정한 표준이나, 실제로 이 정도로 strict할 필요는 없기 때문에 repeatable read를 default로 사용

phantom read:

  • 하나의 transaction 안에서 첫 query와 그 다음 동일한 쿼리의 결과가 다른 경우.
  • non-repeatable read와 달리, 첫 query의 row에 lock을 걸어도 다른 row가 수정되어 두번 째 query의 조건에 만족될 수 있기 때문에 방지하기 어려움
  • isolation level 중에서는 serializable read에서만 방지됨

2편에서는...

실제 여러 transaction이 동일한 데이터에 update와 insert를 하는 상황을 두고 어떤 방식으로 해결할지 고민해볼 예정이다.

참고자료

profile
궁금한 것을 궁금한 것으로 두지 말자.

0개의 댓글