[SQL] 트랜잭션이란

sql

목록 보기
3/6

데이터 베이스 시스템에서 트랜잭션은 데이터의 일관성과 무결성을 유지하는 데 중요한 역할을 한다.
트랜 잭션은 데이터베이스에 대한 일련의 쿼리를 하나의 단위로 묶어 실행하는 개념으로 특정 작업이 완료되거나, 아예 실행되지 않도록 보장하여 데이터 베이스에서의 오류나 실패 상황에서도 데이터의 일관성을 지킬 수 있는 개념이다. 앞으로 이에대해 간략하게 알아보고자 한다.


트랜잭션이란 ?

위에서도 얘기했지만 다시 말하자면 트랜잭션은 데이터 베이스에서 수행되는 작업의 "집합" 으로써 하나의 논리적인 단위로 처리된다.

즉 Atomic 하게 실행되어야 하는 SQL 들을 묶어서 하나의 작업처럼 처리하는 방법을 말한다.
여기서 Atomic 하게 실행되어야 한다는 것은 다음과 같은 뜻을 말한다.

  • 여러개의 SQL 들이 동시에 성공을 하던지, 동시에 실패를 해야지만 데이터의 정합성에 문제가 없는것을 이야기 한다.

조금 어려울 수 있는데 이를 가장 잘 표현할 수 있는것은 은행 계좌이체 가 가장 좋은 예시로 들 수 있다.
은행 계좌이체는 다음과 같이 2개의 Operation 으로 구성되어 있다.

  • 한 사람의 계좌로 부터 돈을 인출
  • 인출된 금액을 다른계좌로 입금

이 두개의 Operation 은 동시에 성공되거나, 동시에 실패되어야만 정상적으로 동작했다고 볼 수 있다.
즉 이 두개의 Operation은 하나의 Operation 처럼 동작을 하는 것을 Atomic 하다고 말한다.

트랜잭션의 특성

트랜잭션은 일반적으로 다음과 같은 특성을 가지고 이를 ACID 속성이라고 부르기도 한다.

  • Atomic(원자성) : 트랜잭션 내의 모든 작업이 성공적으로 완료되거나, 실패하는 작업이 하나라도 있다면 모든 작업을 취소하는 것을 말한다. 즉 트랜잭션은 "모두 또는 아무것도" 라는 원칙을 따른다.
  • Consistency(일관성) : 트랜잭션의 결과는 항상 일관되어야만 한다. 만약 트랜잭션이 진행되는 동안에 데이터베이스의 변경이 일어날 때 해당 트랜잭션은 변경된 후의 데이터베이스를 참고하는것이 아니라 변경되기 전의 데이터베이스를 참고하여야만 한다. 이러한 성질을 지킴으로써 일관된 결과를 얻을 수 있다.
  • Isolation(격리성) : 동시에 실행되는 쿼리들은 서로간의 영향을 끼쳐선 안된다. 각 트랜잭션은 독립적으로 실행되어야 한다. 즉 하나의 트랜잭션이 실행되어 완료되기 전까지 다른 트랜잭션은 이 연산에 끼어들 수 없다.
  • Durability(내구성) : 트랜잭션이 성공적으로 완료되면 그 결과는 영구적으로 저장되어야 한다 시스템 장애가 발생되더라도 데이터는 손상되어선 안된다.

이러한 특성을 참고하여 트랜잭션이 어떠한 상황에서 사용되고, 어떻게 사용되는지 간략하게 알아보면 다음과 같은 경우 사용되고, 사용되지 않아도 되는지 정리해보았다.

  • DDL/DML 중 레코드를 수정/추가/삭제 하는 것에만 유의미한 의미가 있다.
  • 즉 SELECT 에는 트랜잭션을 사용할 의미가 없다.
  • 다시말해 여러 Operation 을 실행하여 어떤 결과를 변경,추가와 같이 데이터를 쓸 때 트랜잭션을 사용하여야 한다.

트랜잭션 그래서 어떻게 사용하는데 ?

위에서 계좌 이체의 예시가 트랜잭션을 가장 잘 설명하는 예시라고 하였다. 이 예시를 바탕으로 어떻게 트랜잭션이 사용되는지 알아보자.

BEGIN;
A의 계좌로 부터 인출; -- Oper 1
B의 계좌로 입금; -- Oper 2
END (또는 COMMIT);

이처럼 BEGIN ~ END 사이에 트랜잭션에 사용할 쿼리 명령어를 넣어주어 트랜잭션을 사용할 수 있다
이렇게 사용하면 Oper1 , Oper2 는 하나의 Operation 처럼 동작하여 하나라도 실패가 일어날경우 원래 상태로 돌아가고, 성공한다면 성공한 결과로 데이터가 수정된다.

여기서 END 는 COMMIT 과 동일하므로 사용자의 취향에 따라 선택적으로 사용하면 된다.

실패한다면 원래 상태로 돌아간다고 하였는데 이를 ROLLBACK 이라 말한다.

트랜잭션의 커밋 모드

autocommit 설정으로 사용되며 MySQL, PostgresSQL 등 기본적으로 이들은 true가 기본값으로 세팅되어 있다. 그렇기 때문에 보통 로컬에서 위와 같은 데이터베이스를 설치하고 INSERT 같은 쿼리를 사용하면 바로바로 테이블에 결과가 반영된 것을 봐왔을 것이다.

이 값은 설정값에 따라 데이터 베이스 시스템에 반영하는 시점이 달라지는데 각 설정값에 따라 동작하는 경우는 다음과 같다.

  • autocommit=True 인 경우
    모든 레코드들이 수정/삭제/추가 작업이 기본적으로 바로 데이터베이스에 쓰여지고 이를 Commit 된다고 한다.
    만약 특정 작업을 트랜잭션으로 묶고 싶다면 BEGIN - END(COMMIT)/ROLLBACK 으로 처리.
    트랜잭션으로 묶는경우 END(또는 COMMIT) 이 실행될 때 까지 결과를 반영하지 않고 기다렸다가 해당 명령어가 정상적으로 실행되면 그 때 트랜잭션의 결과를 반영시킨다.
    ( 단, 다른 세션에서 볼 때만 이처럼 보이고, 내가 돌리고 내가 보면 중간중간 바뀌는 결과를 볼 수 있다. 즉 ! Physical Table 까지는 변경하지 않고, Staging Table로 현재 세션에서 사용하는 임시테이블에서만 우선 변경이 일어난다.)
  • autocommit=False 인 경우
    모든 레코드들이 수정/삭제/추가 작업이 Commit 이 호출될 때 까지 커밋되지 않음.

Python 에서 트랜잭션을 사용하는 경우.

Python 에서 DB와 관련된 작업을 하는 경우 트랜잭션을 사용하는 경우 어떻게 사용하는게 좋은지 한번 알아보자.

아래와 같은 예시가 있다 해보자!

...
try:
	cur.execute('create_sql')
    cur.execute('COMMIT;')
except Exception as e;
	cur.execute('ROLLBACK')

이와 같은 형태로 try~except 구문을 이용하여 Commit 또는 Rollback 을 구분하여 사용한다.
다만 위 코드처럼 사용하는 경우 에러가 발생하여 Exception 이 발생하면 당연히 ROLLBACK 명령어를 통해 롤백되겠지만, 어떤 에러가 발생했는지 외부에서 알 수 있는 방법이 없다.
따라서 Python 에서 트랜잭션을 사용할 때 좀 더 좋은 형태는 다음과 같이 사용하는 것을 추천한다.

...
try:
	cur.execute('create_sql')
    cur.execute('COMMIT;')
except Exception as e;
	cur.execute('ROLLBACK')
    raise

이처럼 raise 를 사용하는 방법이다.
ETL을 관리하는 입장에서는 어떤 에러가 감춰지는 것보단 명확하게 드러나는게 좋기 때문에 raise를 호출함으로써 프로그램이 실패되는 걸 인지할 수 있게하여 이를 관리하는 사람에게 에러를 명확하게 전달해주는 것이 중요하다.
( 이런 실패가 되는경우 Slack, Email 과 같은 수단을 사용하여 Alert 을 제공할 수 있다. )


트랜잭션은 데이터 베이스의 안정성과 신뢰성을 보장하는 핵심 개념임을 알아봤다.
트랜잭션은 ACID 특성을 통해 데이터의 일관성,무결성을 유지하며 오류 발생시에도 안전하게 원래 상태로 되돌릴 수 있게 해준다. 데이터 베이스를 다루는 업무를 하게 되면 이런 개념을 적절하게 사용한다면 안정적으로 데이터 베이스 시스템을 운영할 수 있을 것 같다.

profile
살아남기 위해 끄적이는 블로그 : 생존법

0개의 댓글