티스토리 뷰
데이터베이스 락 소개
데이터베이스 락(Database Lock)은 다중 사용자 환경에서 데이터 정합성(Consistency)과 무결성(Integrity)을 보장하기 위해 하나의 트랜잭션이 특정 데이터에 대한 접근을 제한하는 메커니즘입니다.
락을 사용하면 동시에 여러 사용자가 같은 데이터를 변경할 때 경쟁 조건(Race Condition)을 방지하고, 데이터 충돌을 예방할 수 있습니다.
데이터베이스 락의 종류
락 범위에 따른 분류
- 테이블 락(Table Lock)
- 데이터베이스의 전체 테이블을 잠그는 방식입니다.
- 동시성 처리가 줄어들지만, 대량의 데이터를 갱신할 때 유용할 수 있습니다.
- 사용 예: LOCK TABLES table_name WRITE; (MySQL)
- 행 락(Row Lock, Record Lock)
- 특정 행(Row)만 잠그는 방식입니다.
- 동시성을 높일 수 있지만, 다중 트랜잭션 환경에서 데드락(Deadlock) 발생 가능성이 있습니다.
- 사용 예: SELECT ... FOR UPDATE (MySQL, PostgreSQL)
- 페이지 락(Page Lock)
- 디스크의 특정 페이지(메모리 블록)를 잠그는 방식입니다.
- 일반적으로 데이터베이스 엔진 내부적으로 사용되며, 인덱스 페이지 등을 보호하는 데 활용됩니다.
- 스키마 락(Schema Lock)
- 테이블 구조(스키마)를 변경할 때 사용하는 락입니다.
- 사용 예: ALTER TABLE 실행 시 해당 테이블을 잠그는 경우
락 유형에 따른 분류
- 공유 락(Shared Lock, S Lock)
- 여러 트랜잭션이 동시에 데이터를 읽을 수 있도록 허용하지만, 쓰기 작업은 금지합니다.
- SELECT ... LOCK IN SHARE MODE (MySQL)
- 배타적 락(Exclusive Lock, X Lock)
- 한 트랜잭션이 데이터를 읽거나 수정하는 동안 다른 트랜잭션이 해당 데이터에 접근할 수 없도록 차단합니다.
- SELECT ... FOR UPDATE (MySQL, PostgreSQL)
- 의도적 락(Intent Lock, Intentional Lock)
- 계층적인 락 관리를 위한 메타 정보 역할을 합니다.
- 예를 들어, 특정 행(Row)을 잠그기 전에 먼저 테이블에 대해 Intent Lock을 설정하여 다른 트랜잭션이 충돌하지 않도록 합니다.
- 사용 예: Intent Shared Lock(IS), Intent Exclusive Lock(IX)
트랜잭션 격리 수준(Isolation Level)과의 관계
데이터베이스는 트랜잭션을 처리할 때 격리 수준을 설정할 수 있으며, 이와 함께 락을 활용합니다.
격리 수준 설명 락 적용 방식
Read Uncommitted | 다른 트랜잭션의 미완료 변경 사항을 읽을 수 있음 | 거의 락을 사용하지 않음 (Dirty Read 가능) |
Read Committed | 커밋된 데이터만 읽을 수 있음 | 공유 락(S Lock) 사용 |
Repeatable Read | 같은 트랜잭션 내에서는 동일한 데이터를 읽을 수 있음 | 행 락(Row Lock) 사용 |
Serializable | 완벽한 트랜잭션 격리 보장 | 테이블 수준 락 적용 |
데이터베이스 락의 문제점과 해결책
데드락(Deadlock)
- 두 개 이상의 트랜잭션이 서로 상대방의 락이 해제되기를 기다리면서 무한 대기 상태에 빠지는 현상입니다.
- 예제:
- 트랜잭션 A가 Row1을 잠그고, Row2를 기다림.
- 트랜잭션 B가 Row2를 잠그고, Row1을 기다림.
- A와 B는 서로가 락을 해제할 때까지 무한 대기 상태가 됨.
해결 방법
- 트랜잭션의 락 획득 순서를 일관되게 유지
- 트랜잭션 타임아웃 설정 (SET innodb_lock_wait_timeout = 10;)
- 데드락 감지(Deadlock Detection) 기능 사용 (SHOW ENGINE INNODB STATUS;)
락 경합(Lock Contention)
- 여러 트랜잭션이 동일한 자원을 동시에 잠그려고 할 때 성능이 저하되는 현상입니다.
- 락이 너무 자주 발생하거나, 오랫동안 유지되면 시스템이 병목(Bottleneck)에 걸릴 수 있음.
해결 방법
- 가능한 한 행(Row) 수준의 락을 사용 (테이블 락 대신)
- 락 유지 시간을 최소화하고, 트랜잭션을 짧게 유지
- 비관적 락(Pessimistic Lock) 대신 낙관적 락(Optimistic Lock) 사용 가능
팬텀 리드(Phantom Read)
- 같은 트랜잭션 내에서 반복 조회 시, 이전에 없던 데이터가 새로 추가되는 현상
- SERIALIZABLE 격리 수준을 사용하여 방지 가능하지만, 성능이 저하될 수 있음.
락 전략과 최적화 기법
비관적 락(Pessimistic Lock)
- 트랜잭션이 데이터를 읽을 때 바로 락을 걸어, 다른 트랜잭션의 접근을 차단하는 방식입니다.
- SELECT ... FOR UPDATE 또는 LOCK IN SHARE MODE 사용.
장점: 데이터 충돌이 방지됨.
단점: 동시성이 낮아지고, 성능이 저하될 수 있음.
낙관적 락(Optimistic Lock)
- 트랜잭션이 데이터를 읽을 때는 락을 걸지 않지만, 변경할 때 충돌을 감지하여 업데이트를 제한하는 방식입니다.
- 버전 필드(Versioning) 또는 타임스탬프 기반 업데이트 방식 사용.
사용 예 (버전 필드 적용)
UPDATE orders
SET amount = 100, version = version + 1
WHERE id = 1 AND version = 2;
장점: 동시성이 높아지고 성능이 향상됨.
단점: 데이터 충돌이 발생할 가능성이 있음.
주요 데이터베이스별 락 처리 방식
데이터베이스 주요 락 지원 방식
MySQL | SELECT ... FOR UPDATE, LOCK TABLES |
PostgreSQL | SELECT ... FOR UPDATE, MVCC 기반 락 |
Oracle | SELECT ... FOR UPDATE NOWAIT, DBMS_LOCK |
SQL Server | WITH (ROWLOCK, UPDLOCK, HOLDLOCK) |
결론
- 데이터베이스 락은 동시성 제어를 위한 필수적인 기능이지만, 잘못 사용하면 성능 문제가 발생할 수 있음.
- 테이블 락보다는 행 락을 활용하고, 락 유지 시간을 최소화하는 것이 중요함.
- 비관적 락과 낙관적 락을 적절히 선택하여 성능과 무결성을 조화롭게 관리하는 것이 필요함.
- 데드락을 예방하기 위해 트랜잭션 순서를 일관되게 유지하고, 적절한 타임아웃을 설정해야 함.
데이터베이스 락을 잘 활용하면 시스템의 안정성을 유지하면서도 높은 성능을 보장할 수 있습니다.