낙관적 락(Optimistic Locking)란?
낙관적 락(Optimistic Locking)은 데이터베이스나 멀티스레드 환경에서 여러 사용자가 동일한 데이터를 수정할 때 발생할 수 있는 충돌을 관리하는 방법 중 하나입니다. 낙관적 락은 충돌이 자주 발생하지 않을 것이라고 가정하고, 충돌을 감지하여 처리하는 방식으로 동작합니다. 이 방식은 보통 비전 번호(Versioning)나 타임스템프를 이용해 구현됩니다.
낙관적 락 작동 원리
- 데이터 조회: 사용자가 데이터를 조회할 때 해당 레코드의 현재 버전 정보를 함께 가져옵니다. 이 버전은 레코드가 수정될 때마다 증가하거나 변경됩니다.
- 데이터 수정: 사용자가 데이터를 수정하려고 할 때, 수정하려는 레코드의 현재 버전과 사용자가 처음 조회할 때 가져온 버전 정보를 비교합니다.
- 검증: 만약 사용자가 조회한 이후에 다른 사용자가 해당 데이터를 수정해서 버전 정보가 변경되었다면, 충돌이 발생했다고 판단하고 예외를 발생시킵니다. 이때 수정이 실패하고 트랜잭션이 롤백됩니다.
- 수정 성공: 만약 버전 정보가 동일하다면, 수정이 성공적으로 이루어지며, 데이터베이스는 해당 레코드의 버전 번호를 증가시킵니다.
낙관적 락이 사용되는 경우는?
- 충돌이 적을 것으로 예상되는 상황: 여러 사용자가 동시에 데이터를 수정하는 일이 드물 경우 낙관적 락이 효율적입니다. 충돌이 발생하지 않으면 데이터베이스 락을 잡을 필요 없이 처리할 수 있어 성능이 좋습니다.
- 버전 관리를 통한 충돌 해결: 예를 들어, User 엔티티에 version 필드가 있다고 가정할 때, 데이터 수정 시 version 값이 변경되었는지 비교하여 충돌을 감지합니다.
비관적 락(Perssimistic Locking)이란?
비관적 락(Pessimistic Locking)은 데이터베이스나 멀티스레드 환경에서 여러 사용자가 동시에 동일한 데이터를 수정할 때 발생할 수 있는 충돌을 예방하기 위해, 미리 락을 걸어 충돌을 방지하는 방법입니다. 비관적 락은 충돌이 자주 발생할 것으로 예상되는 환경에서 사용됩니다.
작동 원리
비관적 락은 데이터를 조회하거나 수정할 때 다른 사용자가 해당 데이터에 접근하지 못하도록 락을 설정합니다. 이로 인해 데이터를 수정하는 동안에는 다른 트랜잭션이 그 데이터를 읽거나 수정할 수 없습니다. 주요 흐름은 다음과 같습니다:
1. 데이터 조회: 사용자가 데이터를 조회할 때, 데이터를 수정할 의도가 있다면 해당 데이터에 대해 락을 걸고 다른 트랜잭션이 접근하지 못하도록 합니다. 이 락은 주로 쓰기 락(write lock) 또는 독점 락(exclusive lock)이라고 부릅니다.
2. 락 유지: 락이 걸린 동안 다른 트랜잭션은 해당 데이터에 접근할 수 없으며, 락을 획득한 트랜잭션이 완료될 때까지 대기하거나 실패하게 됩니다.
3. 데이터 수정: 락을 획득한 사용자가 데이터를 수정합니다. 이때 수정된 데이터는 다른 트랜잭션이 접근할 수 없으므로 충돌을 방지할 수 있습니다.
4. 락 해제: 트랜잭션이 완료되면 락이 해제되어 다른 트랜잭션이 데이터를 조회하거나 수정할 수 있습니다.
예시로 들자면
비관적 락은 충돌이 빈번하게 발생하는 상황에서 자주 사용됩니다. 예를 들어, 은행의 계좌 잔액을 업데이트하는 트랜잭션에서 두 사용자가 동시에 잔액을 변경하려는 경우, 비관적 락을 통해 한 사용자가 잔액을 수정하는 동안 다른 사용자는 접근할 수 없도록 합니다.
SELECT * FROM account WHERE id = 1 FOR UPDATE;
위 SQL 문은 id가 1인 계좌 데이터를 조회하고 수정할 예정이므로 FOR UPDATE를 통해 해당 계좌에 쓰기 락을 설정합니다. 이 트랜잭션이 완료되기 전까지 다른 사용자는 이 데이터를 수정할 수 없습니다.
비관적 락의 종류
- 읽기 락(Shared Lock, S-lock): 여러 트랜잭션이 데이터를 읽을 수 있지만, 데이터를 수정하려는 트랜잭션은 락이 걸릴 수 없습니다. 즉, 여러 사용자가 동시에 데이터를 읽을 수는 있지만, 수정하려는 사용자는 대기해야 합니다.
- 쓰기 락(Exclusive Lock, X-lock): 데이터를 읽거나 수정하려는 트랜잭션이 락을 설정하여 다른 트랜잭션이 해당 데이터를 읽거나 수정하지 못하게 합니다.
비관적 락의 장단점
장점
- 충돌 예방: 미리 락을 걸기 때문에 동시에 데이터를 수정하는 과정에서 발생할 수 있는 충돌을 방지할 수 있습니다.
- 데이터 무결성 보장: 동시 접근으로 인해 발생할 수 있는 데이터 일관성 문제를 미리 차단합니다.
단점
- 성능 저하: 락을 걸면 다른 트랜잭션들이 해당 데이터에 접근하지 못하고 대기해야 하기 때문에 시스템의 성능이 저하될 수 있습니다. 특히, 트랜잭션이 오래 지속되거나, 락이 걸린 상태에서 대기 시간이 길어지면 병목 현상이 발생할 수 있습니다.
- 교착 상태(Deadlock) 발생 가능성: 두 트랜잭션이 서로 다른 리소스를 락을 걸고 상호 대기하는 상태가 되면, 교착 상태가 발생할 수 있습니다.
분산 락(Distributed Lock)이란?
분산 락(Distributed Lock)은 분산 시스템 환경에서 여러 노드(서버)가 동일한 자원에 접근하여 데이터를 수정하거나 읽을 때, 데이터의 일관성과 무결성을 보장하기 위해 사용하는 락 메커니즘입니다. 일반적인 락이 단일 시스템 내에서만 동작하는 것과 달리, 분산 락은 여러 서버나 프로세스가 서로 다른 물리적 위치에 있는 상황에서도 자원의 동시성을 제어할 수 있도록 설계됩니다.
분산 시스템에서는 여러 서버가 병렬로 처리 작업을 수행하는데, 동일한 자원을 여러 서버가 동시에 접근할 경우 데이터 충돌이나 일관성 문제가 발생할 수 있습니다. 분산 락은 이러한 문제를 방지하기 위해 특정 자원에 대해 서로 다른 노드들이 동시에 접근하지 못하도록 제한하는 역할을 합니다.
분산 락의 필요성
분산 시스템에서 각 서버는 독립적으로 실행되며 네트워크를 통해 자원에 접근합니다. 이 과정에서 발생할 수 있는 문제는 다음과 같습니다:
- 데이터 일관성 문제: 여러 서버가 동일한 자원을 동시에 업데이트하면 데이터가 꼬일 수 있습니다.
- 동시성 제어: 한 서버가 자원을 업데이트하는 동안 다른 서버는 해당 자원에 접근해서는 안 됩니다.
- 멀티노드 환경: 여러 노드에서 동일한 자원에 접근할 때, 단일 서버에서 락을 거는 방식으로는 관리할 수 없습니다.
분산 락의 주요 구현 방법
분산 락을 구현하는 방법은 여러 가지가 있습니다. 대표적으로 Redis, Zookeeper, Consul 등과 같은 도구를 사용하여 분산 락을 관리할 수 있습니다.
1. Redis 기반 분산 락 (Redlock 알고리즘)
Redis는 인메모리 데이터 저장소로 주로 캐시로 사용되지만, Redlock 알고리즘을 통해 분산 락을 구현할 수 있습니다. 이 방식은 다중 Redis 인스턴스를 이용해 분산 환경에서 락을 관리합니다.
- 락 획득: 노드는 여러 Redis 인스턴스에 동일한 키로 락을 설정하려 시도합니다. 이를 성공적으로 설정하면 해당 자원에 대한 락을 획득하게 됩니다.
- TTL(Time To Live): Redis에 설정된 락은 유효 시간(TTL)을 가지며, 이 시간이 지나면 자동으로 해제됩니다. 이는 서버가 갑작스럽게 다운되거나 네트워크 문제로 락을 해제하지 못하는 상황을 방지합니다.
- 락 해제: 작업이 완료되면 노드는 Redis에 락 해제 명령을 보내 락을 해제합니다.
2. Zookeeper 기반 분산 락
Zookeeper는 분산 시스템의 자원 관리 및 동기화를 위한 서비스입니다. Zookeeper는 ZNode라는 특수한 노드를 이용해 락을 관리합니다.
- Ephemeral ZNode: Zookeeper에서는 일시적인 노드(Ephemeral ZNode)를 생성하여 락을 구현합니다. 노드는 락을 얻기 위해 Zookeeper에 ZNode를 생성하며, 락이 해제되면 해당 ZNode가 삭제됩니다.
- 락 획득: 하나의 노드만 ZNode를 생성할 수 있으며, 나머지 노드는 대기 상태로 전환됩니다. 락을 가진 노드가 작업을 마치면 ZNode를 삭제하고, 다음 노드가 락을 획득합니다.
- 자동 해제: 락을 가진 노드가 죽거나 네트워크가 끊기면 Ephemeral ZNode가 자동으로 삭제되어 다른 노드가 락을 획득할 수 있게 됩니다.
3. Consul 기반 분산 락
Consul은 서비스 디스커버리와 설정 관리를 제공하는 분산 시스템 툴입니다. Consul은 **세션(session)**을 사용하여 분산 락을 구현합니다.
- 세션 생성: 노드는 특정 키에 대해 세션을 생성하고 락을 시도합니다.
- 락 획득: 세션이 성공적으로 생성되면 해당 노드는 자원에 대한 락을 얻습니다.
- 세션 유지: 락을 유지하기 위해 세션은 Consul 서버와 주기적으로 heartbeat를 주고받습니다.
- 세션 만료: 노드가 다운되거나 heartbeat를 놓치면 세션이 만료되고 락이 해제됩니다.
분산 락의 특징
- TTL 설정: 분산 락을 사용할 때는 보통 TTL(Time To Live)을 설정해 락이 일정 시간이 지나면 자동으로 해제되도록 합니다. 이는 서버가 갑작스레 다운되었을 때 락이 영구적으로 걸려 자원을 사용할 수 없게 되는 문제를 방지합니다.
- 락 해제: 락을 획득한 노드는 작업이 끝난 후 명시적으로 락을 해제해야 합니다. 만약 노드가 죽거나 네트워크 오류가 발생하면 TTL을 통해 자동 해제가 이루어집니다.
- 정확한 시간 동기화: 분산 시스템에서 락을 다룰 때 각 서버 간의 시간 차이가 문제가 될 수 있습니다. 이 때문에 Redlock과 같은 알고리즘은 서버 간 시간을 최대한 동기화하고 일관되게 유지하는 것을 중요하게 생각합니다.
분산 락의 장점
- 데이터 일관성 보장: 여러 서버가 동일한 자원에 동시에 접근하는 것을 방지하여 데이터 일관성과 무결성을 보장합니다.
- 다중 노드 간 동시성 제어: 여러 노드에서 자원을 동시에 사용할 때 동시성 문제를 해결할 수 있습니다.
분산 락의 단점
- 복잡성 증가: 단일 시스템에서의 락보다 구현이 복잡하며, 네트워크 장애나 노드 간 통신 실패 등으로 인해 예상치 못한 문제가 발생할 수 있습니다.
- 성능 저하: 락을 걸고 해제하는 과정에서 네트워크 지연이 발생할 수 있어 시스템 성능에 영향을 미칠 수 있습니다.
'Spring' 카테고리의 다른 글
Spring boot 버전 정리 (1) | 2024.12.02 |
---|---|
Transactional 관심사의 분리를 통해 로그 기록 남기기 (0) | 2024.10.08 |
단위테스트, 통합테스트 (1) | 2024.10.01 |
Fetch Type:LazyLoading vs EagerLoading (0) | 2024.09.30 |
Error 메시지(enum 활용) (3) | 2024.09.13 |