개요
데이터베이스에서는 동시성 문제를 해결하기 위해 여러 트랜잭션이 동시에 데이터를 처리할 때 락(Lock)이라는 개념이 필수적으로 사용됩니다.
특히 PostgreSQL에서는 MVCC(Multi-Version Concurrency Control) 방식을 사용해 데이터를 효율적으로 관리하지만, 특정 상황에서는 자동으로 락이 적용되기도 하고, 명시적으로 락을 걸어야 할 때도 있습니다.
이번 글에서는 PostgreSQL에서 락이 언제 자동으로 적용되는지, 그리고 명시적으로 적용해야 하는 상황을 정리해보겠습니다.
MVCC란❓
MVCC(Multi-Version Concurrency Control)는 데이터베이스 관리 시스템에서 동시성 제어를 위해 사용되는 기술 중 하나로, 여러 트랜잭션이 동시에 데이터에 접근할 때 일관성과 성능을 보장하는 중요한 방법입니다.
PostgreSQL, MySQL, Oracle과 같은 주요 DBMS에서 MVCC를 채택하고 있습니다.
MVCC의 기본 개념
MVCC는 트랜잭션의 격리성을 유지하면서 여러 트랜잭션이 동시에 같은 데이터에 접근할 수 있게 합니다.
이를 위해 각 트랜잭션이 데이터의 스냅샷(버전)을 사용하여 트랜잭션이 시작된 시점의 데이터에 접근할 수 있게 하고, 동시에 다른 트랜잭션에서 데이터 변경을 수행할 수 있도록 합니다.
즉, MVCC는 데이터의 여러 버전을 유지하여, 읽기와 쓰기 작업이 서로 차단되지 않도록 동시성을 지원합니다. 읽기 작업은 과거의 데이터 버전을 참조하고, 쓰기 작업은 새로운 버전을 생성하는 방식으로 충돌을 피합니다.
자동/명시적으로 적용되는 락
일부 락은 특정 작업에서 자동으로 적용됩니다.
자동으로 적용되는 락
Row-Level Locking(행 수준 락)
- 트랜잭션이 특정 행을 수정하려고 할 때, 그 행에 대해 자동으로 락이 걸립니다.
- 예를 들어, UPDATE, DELETE, INSERT 작업이 수행될 때 해당 행은 다른 트랜잭션에 의해 수정될 수 없도록 자동으로 락이 걸립니다.
Table-Level Locing(테이블 수준 락)
- DDL (Data Definition Language) 명령어를 사용할 때, 테이블 수준에서 자동으로 락이 적용됩니다.
- 예를 들어, ALTER TABLE과 같은 작업을 할 때는 테이블에 락이 걸립니다.
명시적으로 락을 적용해야 하는 경우
단순 조회
SELECT * FROM your_table WHERE id = 1 FOR UPDATE;
- 단순 조회 작업(SELECT)은
기본적으로 락을 걸지 않습니다. - 하지만 조회된 행을 수정할 예정이라면 명시적으로 락을 걸어야 합니다. 이때 SELECT FOR UPDATE를 사용하여 읽는 동안 다른 트랜잭션이 해당 행을
수정하지 못하게 할 수 있습니다.
LOCK TABLE
LOCK TABLE your_table IN EXCLUSIVE MODE;
- 특정 테이블에 대한 락을 명시적으로 설정하고 싶다면 LOCK 명령어를 사용할 수 있습니다.
- 예를 들어, 테이블에 대한 SHARE 또는 EXCLUSIVE 락을 설정하여 동시성을 제어할 수 있습니다.
락의 종류
배타적 락(Exclusive Lock)
- 정의
- 배타적 락은 수정이나 삭제와 같은 쓰기 작업(UPDATE, DELETE) 시 사용됩니다.
- 특징
- 배타적 락이 걸리면, 다른 트랜잭션은 해당 데이터에 대한 어떠한 작업(읽기/쓰기)도 불가능합니다.
- 예시
- UPDATE나 DELETE 문이 실행될 때 PostgreSQL은 해당 행에 배타적 락을 겁니다.
UPDATE users SET balance = balance + 100 WHERE id = 1;
위 UPDATE 문은 id가 1인 사용자의 데이터에 배타적 락을 걸고, 트랜잭션이 완료될 때까지 다른 트랜잭션이 해당 데이터를 읽거나 수정할 수 없습니다.
공유 락(Shared Lock)
- 정의
- 공유 락은 읽기 작업에 사용됩니다.
- 트랜잭션이 데이터를 읽는 동안 다른 트랜잭션도 데이터를 읽을 수는 있지만, 수정은 불가능합니다.
- 특징
- 여러 트랜잭션이 동시에 공유 락을 걸 수 있지만, 데이터에 대한 수정은 불가능합니다.
- 예시
- SELECT FOR UPDATE 문을 사용하면 해당 데이터에 공유 락이 걸리며, 다른 트랜잭션이 데이터를 수정하려고 할 때는 대기 상태에 들어갑니다.
SELECT * FROM users WHERE id = 1 FOR UPDATE;
위 쿼리는 id가 1인 사용자의 데이터를 읽기 전용으로 잠그지만, 수정이나 삭제는 다른 트랜잭션이 공유 락을 해제하기 전까지 불가능합니다.
락 획득 과정
UPDATE와 배타적 락 획득
UPDATE 문이 실행되면 PostgreSQL은 해당 데이터를 수정하기 위해 먼저 배타적 락을 획득하려고 시도합니다.
- 과정
- 트랜잭션 A가 UPDATE 문을 실행하면, PostgreSQL은 해당 행(row)에 대해 배타적 락을 걸고, 다른 트랜잭션이 그 데이터를
읽거나 수정하지 못하도록 막습니다. - 락을 획득한 트랜잭션 A는 데이터를 업데이트하고, 트랜잭션이 끝나면 락을 해제합니다.
- 만약 트랜잭션 B가 동일한 데이터를 수정하려고 시도하면, 트랜잭션 A가 락을 해제할 때까지 대기합니다.
- 트랜잭션 A가 UPDATE 문을 실행하면, PostgreSQL은 해당 행(row)에 대해 배타적 락을 걸고, 다른 트랜잭션이 그 데이터를
공유 락과 SELECT FOR UPDATE
SELECT FOR UPDATE는 공유 락을 사용하여 데이터를 조회할 때 사용됩니다. 이때, 다른 트랜잭션이 해당 데이터에 접근하려고 하면 쓰기 작업은 대기 상태에 들어갑니다.
- 과정
- 트랜잭션 A가 SELECT FOR UPDATE로 데이터를 읽고 있으면, 해당 데이터에 공유 락이 걸립니다.
- 트랜잭션 B는 동일한 데이터에 대해 UPDATE를 시도할 경우, 트랜잭션 A가 공유 락을 해제할 때까지 대기합니다.
락과 성능 고려사항
락을 사용하면 데이터 무결성을 보장할 수 있지만, 트랜잭션 간의 충돌로 인해 성능 저하가 발생할 수 있습니다.
특히, 트랜잭션이 오래 유지되면 다른 트랜잭션이 대기해야 하므로 락 경쟁(Lock Contention)이 발생할 수 있습니다.
이러한 문제를 줄이기 위해서는 트랜잭션을 짧게 유지하고, 필요한 최소한의 데이터에만 락을 거는 것이 중요합니다.
'Database' 카테고리의 다른 글
[DB] DB Lock이란 무엇일까❓ : 데이터 무결성을 지키는 다양한 DB Lock알아보기 (0) | 2024.08.21 |
---|