[데이터베이스] 트랜잭션과 4가지 성질, ACID, 격리수준

DevHwan·2023년 4월 6일
0

트랜잭션은 데이터베이스에서 수행하는 작업의 논리적인 최소 단위를 의미한다.

즉 데이터베이스에서 하나의 작업이 실행되기 위해서는 하나 이상의 SQL 문이 실행되어야 하며, 이러한 SQL문들이 성공적으로 수행되어야 해당 작업이 완료되었다고 볼 수 있다.

트랜잭션은 기본적으로 4가지 성질인 ACID를 항상 만족해야 한다. 이 4가지 성질은 다음과 같다.

  • 원자성 (Atomicity) : 트랜잭션은 원자적으로 수행되어야 한다. 트랜잭션 내의 모든 작업 (SQL)은 전부 실행되거나 전부 실행되지 않아야 한다. 하나의 작업이라도 실패하는 경우에는 트랜잭션 전체가 롤백되어 이전 상태로 돌아간다.
  • 일관성 (Consistency) : 트랜잭션 실행 전과 실행 이후의 데이터베이스의 무결성 제약 조건이 항상 유지되어야 한다.
  • 고립성(Isolation): 여러 개의 트랜잭션이 동시에 실행될 경우, 각각의 트랜잭션은 서로 간섭없이 독립적으로 실행되어야 한다..
  • 지속성(Durability): 트랜잭션이 성공적으로 완료되면 그 결과는 영구적으로 저장되어야 한다.

트랜잭션은 하나의 논리적인 작업이므로, 이를 통해 데이터베이스의 일관성과 무결성을 보장할 수 있다. 또한 트랜잭션이 여러 개 동시에 실행되는 경우에, 충돌을 방지하고 데이터 무결성을 보장하기 위해 고립성을 유지할 필요가 있다.

HOW?

여러 개 트랜잭션이 동시에 실행되는 경우 → 동시성으로 인해 발생하는 문제를 어떻게 해결하고 고립성을 유지할 수 있을까?

바로 트랜잭션 격리수준을 설정하여 데이터베이스의 무결성을 보장시킬 수 있다.

다른 방법도 있는가?? → 당연히 있다.

락, 데드락, 데이터베이스 스키마 등을 활용하여 해결할 수 있다. 이번 글에서는 트랜잭션 격리수준을 통해 해결하는 것을 알아본다.

트랜잭션 격리수준

트랜잭션 격리수준은 동시성 문제가 발생할 때, 해당 접근들을 어떻게 처리할 지에 대한 설정들이다. 근본적으로는 데이터베이스마다 기본적으로 설정이 되어 있고, 사용자가 원하면 SQL을 통해서 격리수준을 변경할 수 있다.

격리 수준은 크게 4가지 단계로 분류할 수 있다.

  • Read Uncommitted
  • Read Committed
  • Repeatable Read ( MySQL )
  • Serializable

단계가 심화될수록 트랜잭션 간의 고립 상태가 심해지고, 데이터베이스 자체의 성능은 낮아지는 특징이 있다. 대부분의 데이터베이스는 따라서 "READ COMMITTED" 또는 "REPEATABLE READ” 격리수준을 선호한다.

Read Uncommitted

가장 낮은 격리 수준으로, 다른 트랜잭션에서 변경하고 있는 데이터를 읽을 수 있다.

해당 과정에서 다른 트랜잭션이 아직 커밋하지 않은 데이터를 읽어들이는 현상이 발행할 수 있다. 이것을 Dirty Read이라고 한다.

Dirty Read 현상은 데이터의 일관성을 해치고 데이터베이스의 무결성을 헤치는 원인이 된다. 이러한 현상을 방지하기 위해서 더 높은 격리수준을 사용해야 한다.

Read Committed

다른 트랜잭션이 커밋한 데이터만 읽을 수 있는 격리레벨이다. 이전과 달리 트랜잭션이 적용되는 동안 사용자가 해당 데이터에 접근할 수 없다. → 해당 데이터에 접근하려고 하면 트랜잭션 중이기 때문에 트랜잭션이 시작되기 전의 값을 읽어간다.

중간 단계 중 하나의 격리레벨로 일반적인 데이터베이스에서 많이 사용되는 표준이다.

그러나 해당 레벨에서도 문제가 발생한다. 바로 한 트랜잭션 내에서 같은 쿼리를 두 번 이상 실행할 때, 발생할 수 있다. 첫 번째 쿼리와 두 번째 쿼리의 결과값이 동일하지 않을 수 있는 문제이다. 첫 번째 쿼리 발생시점 이후에 다른 트랜잭션이 해당 데이터 값을 변경완료하면 두 번째 쿼리 발생시점에 해당 데이터 값이 변경된 상태로 조회되는 것이다. 분명 같은 쿼리를 두 번 한 트랜잭션 내부에서 작동하였으나 결과값은 다르게 조회되는 현상이다. 해당 현상을 Non-repeatable Read라고 한다.

이전과 마찬가지로 이러한 현상을 방지하기 위해서 더 높은 격리수준을 사용해야 한다.

REPEATABLE READ

드디어 MySQL에서 사용하는 트랜잭션 격리수준까지 왔다.

해당 격리수준에서는 트랜잭션이 시작될 때의 상태에서 읽은 데이터를 트랜잭션이 끝날 때까지 항상 같은 값으로 유지하는 격리수준이다. 다른 트랜잭션에서 새로운 데이터를 추가하거나 수정하더라도 트랜잭션 내에서 읽은 데이터가 변경되지 않는다.

즉 트랜잭션 범위에서 조회한 데이터 내용이 여러 번의 SQL에서 항상 동일함을 보장한다.

그러나, 진짜 놀랍게도 여기서도 문제가 발생한다. 이전에는 동일한 데이터 값에 대해 다른 트랜잭션에서 변경하였을 때, 다른 값이 조회되는 문제였지만 이번에는 아예 새로운 레코드를 삽입했을 때 발생한다. ( 수정, 삭제하는 경우에도 발생가능 )

한 트랜잭션에서 동일한 조회쿼리를 두 번 이상 발생했을 때 ( 전체조회 ) , 다른 트랜잭션에서 레코드를 새로 삽입하게 되면 시점에 따라 레코드가 추가로 보이거나 보이지 않는 현상이 발생한다. 해당 현상을 Phantom Read라고 한다.

SERIALIZABLE

마지막 단계까지 도달했다.

크게 설명할 내용이 없다. 한 트랜잭션에서 사용하는 ( 수정, 삽입, 변경 ) 데이터에 다른 트랜잭션이 절대로 접근할 수 없다. 가장 엄격한 격리수준으로 트랜잭션을 순차적으로 실행하여 격리를 완전 보장한다.

이러한 격리수준의 반대효과로 데이터베이스의 성능상 이슈가 발생할 수 있다.

CS 관점에서는 세마포어가 떠오르는 격리수준이다..

profile
달리기 시작한 치타

0개의 댓글