Database Transaction

데이터 베이스 트랜젝션에 관한 고찰

Posted by Start Bootstrap on March 25, 2020

트랜잭션이란 무엇입니까

데이터를 읽고 쓰고 바꾸고 삭제하는 과정은 서로 다른 과정이라고 할 수 있습니다. 하지만 시스템에서는 몇 가지 과정을 논리적(Logical)으로 묶을 필요가 있습니다. 이를 트랜잭션(Transaction) 이라고 합니다. 이 트랜잭션이라는 논리적 단위로 묶인 작업은 한 연산으로 취급이 됩니다. 한 연산으로 취급이 된다는 것의 의미는 전체가 성공(Commit) 하거나 아니면 실패(Rollback)하는 것을 말합니다. 이것의 가장 큰 목적은 부분적인 실패가 없도록 보장하는 것입니다.

실패가 없도록 하는 안전성 보장에는 어떤 방법으로 해야 하는지 그리고 어떤 기능을 제공하는지 이 기능을 사용하는 대에는 어떤 비용을 지불해야 하는지에 대해서 관심을 가져야 합니다. 특히 동시성 제어 분야를 다루는 것이 의미가 있습니다. 일반적인 경우에는 사실상 실패는 거의 없지만 한정된 데이터를 가지고 처리하려고 한다면(Race Condition 즉 자원에 대한 경쟁 상태) 문제가 발생할 수 있습니다. 이것은 흔히 계좌를 송금할때 예시를 많이 차용하긴 합니다만 이미 설명이 잘 되어 있으므로 언급하지는 않겠습니다.

실패가 없도록 하는 안전성 보장에는 어떤 방법으로 해야 하는지 그리고 어떤 기능을 제공하는지 이 기능을 사용하는 대에는 어떤 비용을 지불해야 하는지에 대해서 관심을 가져야 합니다. 특히 동시성 제어 분야를 다루는 것이 의미가 있습니다. 일반적인 경우에는 사실상 실패는 거의 없지만 한정된 데이터를 가지고 처리하려고 한다면(Race Condition 즉 자원에 대한 경쟁 상태) 문제가 발생할 수 있습니다. 이것은 흔히 계좌를 송금할때 예시를 많이 차용하긴 합니다만 이미 설명이 잘 되어 있으므로 언급하지는 않겠습니다.

간단한 예시를 통해서 확인해보겠습니다 트랜잭션을 시작하고 특정 조건을 만족할 경우, 혹은 실패 할경우 ROLLBACK 전부 성공할 경우 COMMIT까지 하는 개괄적인 흐름입니다.

        
                START(BEGIN) TRANSACTION;
                SELECT @A:=SUM(salary) FROM table1 WHERE type=1;
                UPDATE table2 SET summary=@A WHERE type=1;
                IF( @@row = 0 )
                    ROLLBACK TRANSACTION;
    
                COMMIT TRANSACTION;
        
    

안정성과 ACID

트랜잭션이 안전하게 수행된다는 보장을 하기 위한 성질을 정의했는데 그것을 ACID라 부릅니다. 1983년 안드레아스 로이테르와 테오 해르데르는 ACID라는 용어를 만들면서 이를 기술했다고 알려져 있씁니다. ACID란 원자성(Atomicity), 일관성(Consistency), 격리성(Isolation), 지속성(Durability)를 의미하는 약어 입니다. 이에 반해 ACID의 반대 용어로 BASE라는 말도 있습니다. 이는 가용성(Basically Available)과 유연성(Soft state) 마지막 일관성(Eventual consistency)를 지닌 다는 의미입니다. 이는 보통 트랜잭션을 제공하지 않는 NOSQL에서 많이 차용하는 개념입니다.

하지만 문제는 ACID의 정의는 데이터베이스 시스템에서 모호하고 확실히 성질을 제공하지 않는 다는 단점이 있습니다. 이는 BASE도 마찬가지 입니다. 물론 천재 지변이나 HW와 전기의 문제와 같은 경우에도 제공할 수 없지만 SW단의 문제에서도 마찬가지입니다.

원자성(Atomicity)원자성은 더 이상 분해할 수 없는 작은 단위를 의미합니다. 예를 들면 컴퓨터 메모리에서는 데이터를 읽을 때 32bit에서는 한 번에 32bit만큼, 64bit에서는 한 번에 64bit만큼 읽을 수 있습니다. 즉 32bit 환경에서는 double 자료형과 같은 64bit는 메모리에서 32bit씩 두번 읽어서 처리하게 되므로 멀티 스레딩 환경에서 nolock이라면 중간 상태의 데이터를 볼 수도 있습니다. 따라서 처리가 원자적이라는 것은 이러한 중간 상태는 없는 처리 과정을 의미합니다. 데이터베이스에서는 이를 ROLLBACK과 COMMIT으로 제공합니다. 만약 일부 로직에서 결함이 생긴다면 전체를 반영하지 않으며(Rollback) 아무런 문제가 없어야 모든 연산을 반영(Rollback) 합니다. 따라서 데이터베이스 시스템은 단지 이 사실 하나만 확인해 주기 때문에 문제가 없다는 판단도 문제가 발생했을 때 재시도를 해야 하는 판단도 프로그래머의 손에 달려 있다고 할 수 있습니다.

일관성(Consistency)일관성이라는 것은 데이터에 관해서 어떠한 제약 조건이 항상 유효함을 의미합니다. 예를 들면 @AA와 @BB의 합이 항상 0 이상 100이하로 데이터가 관리가 되어야 한다는 제약 조건과 같은 것입니다. 혹은 @CC는 항상 unique해야 한다는 조건도 있을 수 있습니다.

격리성(Isolation)Race Condition같은 동시성 문제는 항상 신경 써야 하는 부분입니다. 조금 범위를 축소해서 데이터베이스 시스템에서는 트랜잭션들이 서로 격리된다는 것을 의미합니다. 따라서 트랜잭션은 동시에 실행된다 하더라도 격리 수준에 따라 경쟁 상태가 발생하지 않는 결과를 얻을 수 있습니다.

지속성(Durability)지속성은 안전한 데이터 저장소를 목적으로 합니다. 로그와 같은 복구 시스템이 존재합니다.

데이터베이스 일관성은 사실 데이터베이스에서도 조건을 설정할 수 있지만 일반적으로 좋은 현상은 아니라고 생각합니다. Application단에서 프로그래머가 잘못된 데이터를 쓰지 않도록 관리하는 것이 최선이고 DB는 제대로 막을 수 없기 때문에 실패를 시킬 수 있도록만 해놓는 것이 맞습니다. 실제 작업 중에 이 규칙을 지키지 않아서 문제가 발생한 것을 많이 확인했습니다. 정리하면 데이터 유효나 검증은 Application 역할이고 데이터베이스는 저장에 큰 의미가 있습니다.

격리 수준

동시성 버그의 가장 난해한 점 중 하나는 타이밍에 따라서 발생할 수도 있고 아닐 수도 있는 점이라는 생각이 듭니다. 그래서 일반적으로 재현하기가 어렵습니다. 데이터베이스 시스템은 트랜잭션을 격리시키는 방식으로 해당 문제를 발생시키지 않으려고 노력했습니다. 일반적으로 사용하는 격리 수준은 READ COMMITTED라 불리는 커밋된 데이터만 READ할 수 있다(Dirty Read 방지), 커밋된 데이터만 쓰기가 가능하다(Dirty Write 방지)가 있습니다.

동시성 버그의 가장 난해한 점 중 하나는 타이밍에 따라서 발생할 수도 있고 아닐 수도 있는 점이라는 생각이 듭니다. 그래서 일반적으로 재현하기가 어렵습니다. 데이터베이스 시스템은 트랜잭션을 격리시키는 방식으로 해당 문제를 발생시키지 않으려고 노력했습니다. 일반적으로 사용하는 격리 수준은 READ COMMITTED라 불리는 커밋된 데이터만 READ할 수 있다(Dirty Read 방지), 커밋된 데이터만 쓰기가 가능하다(Dirty Write 방지)가 있습니다.

이러한 정책은 다양한 잠금(Lock) 정책을 활용하기 때문에 다른 문제를 유발할 수도 있습니다. 예를 들면 Dead Lock과 관련된 문제거나 혹은 READ의 성능 저하와 같은 여러 가지 문제가 있습니다. 특히 이러한 현상을 통해 발생한 지연은 다른 서비스에서 까지도 미칠 수 있습니다. 이러한 문제에 관한 고찰은 READ UNCOMMITED와 같은 격리 수준이나 SNAPSHOT과 같은 것이 있습니다.(참조 : https://blog.codinghorror.com/deadlocked/)