Post

[Database] 트랜잭션과 데이터베이스 락(DB Lock)에 대해서

트랜잭션(Transaction)에 대해서?

‘트랜잭션’이란 하나의 논리적 기능을 수행하기 위한 일련의 연산들을 말한다. 예를 들자면, 은행 시스템에서 사용자 A가 사용자 B에게 돈을 이체하는 과정은 여러 단계의 연산으로 구성된다. 이러한 연산들이 모두 포함된 전체 과정을 ‘트랜잭션’이라 한다.

트랜잭션 ACID 이란?

  • 원자성(Atomicity): 트랜잭션 내의 모든 연산은 하나의 단위로 간주된다. 모두 완전히 실행되거나, 아무것도 실행되지 않아야 한다. 예를 들어, 은행 이체 과정에서 돈을 출금하는 작업과 입금하는 작업이 모두 성공해야 하며, 하나라도 실패하면 전체 트랜잭션이 취소된다.
  • 일관성(Consistency): 트랜잭션이 데이터베이스의 일관된 상태를 유지해야 한다. 트랜잭션 전후에 데이터베이스의 모든 규칙과 제약조건이 충족되어야 한다. 예를 들어, 계좌 잔액은 결코 음수가 될 수 없다.
  • 격리성(Isolation): 동시에 여러 트랜잭션이 실행될 때, 각 트랜잭션은 서로 독립적으로 실행되어야 한다. 한 트랜잭션의 중간 결과가 다른 트랜잭션에 영향을 주어서는 안된다. 예를 들어, 두 개의 트랜잭션이 동시에 같은 데이터에 접근할 때, 이들은 서로의 작업에 영향을 받지 않아야 한다.
  • 지속성(Durability): 트랜잭션이 성공적으로 완료되면, 그 결과는 영구적으로 데이터베이스에 반영됩니다. 시스템에 장애가 발생하더라도, 완료된 트랜잭션의 결과는 유지되어야 한다.

트랜잭션 격리 수준 - Isolation level

격리 수준이 낮을수록 동시성은 높아지지만, 데이터 불일치의 위험이 증가한다. 반대로 격리 수준이 높으면 데이터 일관성은 좋아지지만 동시성이 감소한다.

  • READ UNCOMMITTED(커밋되지 않은 읽기)

    가장 낮은 격리 수준으로, 다른 트랜잭션에서 아직 커밋되지 않은 데이터를 읽을 수 있다. 이 수준에서는 “Dirty Reads”라는 현상이 발생할수 있는데, 이는 한 트랜잭션이 아직 커밋되지 않은 다른 트랜잭션의 변경 사항을 읽는 것을 의미한다. 이는 데이터 일관성에 문제를 일으킬 수 있다.

  • READ COMMITTED(커밋된 읽기)

    대부분의 데이터베이스 시스템에서 기본으로 설정되는 격리 수준이다. 이 수준에서는 트랜잭션이 커밋된 데이터만 읽을 수 있다. 이는 “Dirty Reads”문제를 방지하지만, “Non-repeatable Reads” 현상은 여전히 발생할 수 있따. 이는 한 트랜잭션 내에서 같은 데이터를 두 번 읽었을 때, 다른 트랜잭션이 해당 데이터를 변경하여 다른 결과를 가져오는 현상이다.

  • REPEATABLE READ(반복 가능한 읽기)

    이 수준에서는 한 트랜잭션 내에서 데이터를 여러 번 읽더라도 항상 동일한 데이터 세트를 보장한다. 이는 “Non-repeatable Reads”문제를 방지한다. 하지만, 이 수준에서는 “Phantom Reads”라는 문제가 발생할 수 있는데, 이는 한 트랜잭션 내에서 일관된 조회를 수행하는 동안 다른 트랜잭션이 새로운 데이터를 삽입하거나 삭제할 때 발생한다.

  • SERIALIZABLE(직렬화 가능)

    가장 높은 격리 수준으로, 트랜잭션들이 마치 순차적으로 실행되는 것처럼 보장한다. 이 수준에서는 “Phantom Reads”문제도 해결된다. 하지만, 이러한 엄격한 격리는 동시성을 크게 감소시킬 수 있으며, 성능 저하의 원인이 될 수 있다.

데이터베이스 연결 구조

image

  • 사용자가 클라이언트를 사용해서 데이터베이스 서버에 접근하기 위해, 클라이언트는 데이터베이스 서버에 연결을 요청하고 커넥션을 가진다. 이때 데이터베이스 서버는 내부에 세션이라는 것을 생성하여 앞으로 해당 커넥션을 사용한 모든 요청은 해당 세션을 통해서 실행하게 된다. 즉, 클라이언트를 통해 SQL문을 전달하면 현재 커넥션에 연결된 세션이 SQL을 실행한다.
  • 세션은 트랜잭션을 시작하고, 커밋 또는 롤백을 통해 트랜잭션을 종료한다. 그리고 이후에 새로운 트랜잭션을 다시 시작할 수 있다.
  • 사용자가 커넥션을 닫거나, 또는 DBA가 세션을 강제로 종료하면 세션은 종료된다.

트랜잭션 사용법

데이터베이스에서 트랜잭션을 사용하는 것은 SQL문을 이용하여 여러 데이터베이스 연산을 하나의 논리적 단위로 묶는 과정을 포함한다. 트랜잭션을 사용하면 여러 작업을 모두 완료하거나, 하나라도 실패할 경우 전체 작업을 취소(롤백)할 수 있다. 이를 통해 무결성을 유지할 수 있다.

START TRANSACTION

  • ‘START TRANSACTION’ 명령은 명시적으로 새로운 트랜잭션을 시작한다. 이 명령이 실행되면, 이후에 실행되는 모든 쿼리는 같은 트랜잭션의 일부가 된다.
  • 이 명령은 개별 트랜잭션을 제어하고자 할 때 사용된다. 사용자가 명시적으로 ‘COMMIT’ 또는 ‘ROLLBACK’을 호출하기 전까지는 변경사항이 데이터베이스에 반영되지 않는다.
  • ‘START TRANSACTION’을 사용하는 경우, 각 트랜잭션은 분명하게 정의된 시작점을 가지며, 명시적인 종료점 (commit, rollback)이 필요하다.

SET AUTOCOMMIT FALSE

  • 데이터베이스 세션에 대한 오토커밋 모드를 끄는 것이다. 이 설정이 적용되면, 명시적으로 ‘START TRANSACTION’을 호출하지 않아도, 모든 개별 SQL문은 자동으로 트랜잭션의 일부가 된다.
  • 이 설정은 세션 레벨에서 적용되며, ‘SET AUTOCOMMIT TRUE’로 다시 설정하거나 세션을 종료할 때까지 유지된다.
  • 이 모드에서는 ‘COMMIT’ 또는 ‘ROLLBACK’을 사용하여 트랜잭션을 명시적으로 종료해야 한다. 그렇지 않으면 모든 변경 사항이 세션 종료 시에 자동으로 롤백된다.

💡 ‘START TRANSACTION’은 특정 트랜잭션을 시작하는데 사용되는 반면, ‘SET AUTOCOMMIT FALSE’는 데이터베이스 연결 세션의 동작 방식을 변경하여 기본적으로 모든 쿼리를 트랜잭션으로 처리하도록 한다.

1
2
3
4
5
set autocommit false;

inset ~ 작업

commit; // 수동 커밋

데이터베이스에서 락(Lock)에 대해서

데이터베이스에서 락(Lock)은 동시에 여러 트랜잭션이 실행될 때 데이터의 일관성과 무결성을 유지하기 위해 사용되는 메커니즘이다. 락은 특정 데이터 레코드나 데이터베이스의 일부분에 대한 접근을 제한하여, 한 번에 하나의 트랜잭션만 해당 데이터를 수정하거나 조회할 수 있게 한다.이는 데이터 충돌과 비일관성을 방지하는 데 필수적이다.

데이터베이스 락의 종류

공유 락 (Shared Locks)

  • 여러 트랜잭션이 동시에 데이터를 읽을 수 있게 해준다.
  • 다른 트랜잭션이 해당 데이터를 읽는 것은 가능하지만, 수정은 불가능하다.

배타 락 (Exclusive Lock)

  • 데이터를 수정할 때 사용된다.
  • 이 락이 걸린 데이터는 다른 어떤 트랜잭션도 접근할 수 없다. (읽기, 쓰기 모두 불가)

데이터베이스 락 예시

상황: 두 트랜잭션이 동시에 같은 은행 계좌에 접근하려 한다. 하나는 계좌의 잔액을 조회하고, 다른 하나는 잔액을 수정하려고 한다.

  • 트랜잭션 A (조회): 은행 계좌의 잔액을 조회하기 위해 공유 락을 요청합니다. 조회 중에는 다른 트랜잭션이 해당 계좌의 잔액을 조회하는 것이 가능합니다.
1
2
3
4
5
6
7
START TRANSACTION; -- 트랜잭션 A 시작

SELECT balance FROM accounts WHERE account_id = '123' FOR SHARE; -- 공유 락 요청

-- 여기서 다른 작업을 수행할 수 있습니다.

COMMIT; -- 트랜잭션 A 종료
  • 트랜잭션 B (수정): 동시에, 은행 계좌의 잔액을 변경하기 위해 배타 락을 요청합니다. 하지만 트랜잭션 A가 공유 락을 이미 획득했기 때문에, 트랜잭션 B는 배타 락을 획득할 수 없습니다. 트랜잭션 B는 트랜잭션 A가 락을 해제할 때까지 대기해야 합니다.
1
2
3
4
5
6
7
START TRANSACTION; -- 트랜잭션 B 시작

SELECT balance FROM accounts WHERE account_id = '123' FOR UPDATE; -- 배타 락 요청

UPDATE accounts SET balance = balance + 100 WHERE account_id = '123'; -- 잔액 변경

COMMIT; -- 트랜잭션 B 종료

데이터베이스의 동시성과 일관성 유지

‘FOR SHARE’는 다른 사용자가 데이터를 동시에 읽을 수 있게 하면서 데이터 수정을 방지하고, ‘FOR UPDATE’는 데이터를 수정하는 동안 다른 사용자의 접근을 제한하여 데이터 일관성을 보장한다.

FOR SHARE

  • ‘FOR SHARE’는 주로 읽기 작업에 사용되는 공유 락을 설정한다.
  • 해당 절을 사용하면, 해당 트랜잭션은 지정된 데이터를 읽을 수 있지만, 데이터를 변경할 수는 없다.
  • 동시에 다른 트랜잭션들도 같은 데이터를 읽을 수 있다.
  • 그러나 ‘FOR UPDATE’와 같은 베타 락을 요청하는 다른 트랜잭션이 있을 경우, 그 트랜잭션이 완료되고 락이 해제될 때까지 기다려야 할 수 있다.

FOR UPDATE

  • ‘FOR UPDATE’는 데이터를 변경하기 위한 배타 락을 설정한다.
  • 해당 절을 사용하는 트랜잭션은 해당 데이터에 대해 읽기와 쓰기 작업을 모두 수행할 수 있다.
  • ‘FOR UPDATE’를 사용하면, 다른 트랜잭션이 해당 데이터에 대해 ‘FOR SHARE’ 또는 ‘FOR UPDATE’를 사용하여 접근하는 것이 제한된다. 즉, 데이터를 변경하려는 트랜잭션은 다른 트랜잭션들로부터 해당 데이터를 독점적으로 보호한다.
  • ‘FOR UPDATE’는 데이터를 변경하기 전에 데이터의 일관성을 유지하고 동시성 문제를 방지하기 위해 사용된다.

동시성 (Concurrency)

동시성은 여러 사용자나 트랜잭션이 데이터베이스에 대해 동시에 작업을 수행할 수 있도록 하는 능력을 의미한다. 이를 통해 다수의 사용자가 데이터베이스 시스템을 공유할 수 있으며, 시스템의 성능과 처리량이 크게 향상된다.

  • 중요성: 데이터베이스가 동시에 여러 요청을 처리할 수 없다면, 사용자는 작업을 완료하기 위해 서로 차례를 기다려야 할 것이다. 이는 시스템의 응답 시간을 낮추고 전반적인 성능을 저하시킨다.

일관성 (Consistency)

일관성은 데이터베이스가 항상 일관된 상태에 있어야 한다는 원칙이다. 이는 데이터베이스의 모든 변경이 일련의 규칙(예시: 데이터 무결성 제약조건)을 따라야 함을 의미한다.

  • 중요성: 일관성 없이 데이터가 변경되면, 데이터베이스는 부정확하거나 모순된 정보를 포함하게 된다. 예를 들어, 은행 시스템에서 잔액이 음수가 되지 않도록하는 규칙은 중요한 일관성 조건이다.
This post is licensed under CC BY 4.0 by the author.