분산 락(Distributed Lock)이란?
분산 락(Distributed Lock)이란 무엇일까요?
분산 시스템 환경에서는 여러 서버가 공유된 자원(데이터, 파일 등)에 동시에 접근하려고 할 때 데이터의 일관성과 무결성을 해치는 문제가 발생할 수 있습니다. 예를 들어, 여러 사용자가 동시에 상품 재고를 수정하려고 하면, 실제 재고와 데이터베이스의 값이 달라지는 문제가 생길 수 있습니다.
이러한 문제를 해결하기 위해 분산 락이 사용됩니다. 분산 락은 여러 서버에 분산된 시스템에서 공유 자원에 대한 동시 접근을 제어하는 동기화 메커니즘입니다. 즉, 특정 자원에 대해서는 한 번에 하나의 프로세스(또는 스레드)만 접근할 수 있도록 보장하여 데이터의 일관성을 유지합니다.
분산 락은 왜 필요할까요?
하나의 서버에서 동작하는 애플리케이션에서는 synchronized
키워드나 ReentrantLock
과 같은 자바의 동시성 제어 도구를 사용하여 스레드 간의 동시 접근을 제어할 수 있습니다. 하지만 서버가 여러 대로 확장된 분산 환경에서는 이러한 방식이 동작하지 않습니다. 각 서버는 자신만의 메모리(JVM)를 가지고 있기 때문에, 한 서버의 락은 다른 서버에 영향을 주지 못합니다.
따라서 분산 환경에서는 모든 서버가 공통으로 참조할 수 있는 외부의 락 관리 시스템이 필요합니다. 이것이 바로 분산 락의 역할입니다.
분산 락 구현 방법
분산 락을 구현하는 데는 여러 가지 방법이 있지만, 주로 다음과 같은 외부 시스템을 활용합니다.
1. Redis를 이용한 분산 락
Redis는 인-메모리 데이터 저장소로, 빠른 속도와 간단한 명령어(SETNX)를 제공하여 분산 락을 구현하는 데 널리 사용됩니다.
- SETNX (SET if Not eXists):
SETNX
명령어는 키가 존재하지 않을 때만 값을 설정합니다. 이 명령어의 원자성(atomic)을 이용하여 락을 구현할 수 있습니다.- 프로세스가 락을 획득하려면
SETNX lock_key "locked"
와 같은 명령어를 실행합니다. - 명령어가 성공하면(1을 반환), 락을 획득한 것입니다.
- 이미 다른 프로세스가 락을 가지고 있다면 명령어는 실패합니다(0을 반환).
- 프로세스가 락을 획득하려면
- 단점:
- 락 유실: 락을 획득한 클라이언트가 처리를 완료하고 락을 해제하기 전에 비정상적으로 종료되면, 락이 영원히 해제되지 않는 데드락(deadlock) 상태에 빠질 수 있습니다. 이를 방지하기 위해 락에 유효 시간(TTL, Time-To-Live)을 설정해야 합니다.
- Redis 단일 장애점(SPOF): 단일 Redis 인스턴스를 사용하면 해당 인스턴스에 장애가 발생했을 때 전체 시스템의 락 기능이 마비될 수 있습니다. 이를 해결하기 위해 Redis Sentinel이나 Redis Cluster와 같은 고가용성 구성을 사용해야 합니다.
2. ZooKeeper를 이용한 분산 락
Apache ZooKeeper는 분산 시스템의 코디네이션을 위해 설계된 서비스로, 분산 락을 구현하는 데 매우 안정적인 기능을 제공합니다.
- 영구 노드(Persistent Nodes)와 임시 노드(Ephemeral Nodes): ZooKeeper는 파일 시스템과 유사한 계층적인 ZNode 구조를 가집니다.
- 락을 표현하기 위해 특정 경로에 임시 노드(ephemeral node)를 생성합니다.
- 임시 노드는 해당 노드를 생성한 클라이언트의 세션이 활성 상태일 때만 존재합니다. 만약 클라이언트가 비정상적으로 종료되면, 세션이 끊어지고 해당 임시 노드는 자동으로 삭제됩니다. 이 특성 덕분에 데드락을 방지할 수 있습니다.
- 순차 노드(Sequential Nodes): ZooKeeper는 노드를 생성할 때 순번을 붙여주는 기능을 제공합니다. 이를 이용하면 모든 요청이 락을 기다리게 하는 것이 아니라, 순서대로 락을 획득하도록 하여 “Thundering Herd” 문제를 방지하고 공정한 락 획득 순서를 보장할 수 있습니다.
- 단점:
- 성능: Redis에 비해 상대적으로 느릴 수 있습니다. ZooKeeper는 데이터의 일관성과 안정성을 보장하기 위해 복잡한 프로토콜(ZAB)을 사용하기 때문입니다.
- 운영 복잡성: Redis에 비해 설치 및 운영이 더 복잡합니다.
분산 락 사용 시 고려사항
- 데드락 방지: 락을 획득한 클라이언트가 어떤 이유로든 락을 해제하지 못하는 상황을 반드시 방지해야 합니다. TTL 설정이나 ZooKeeper의 임시 노드와 같은 메커니즘이 필수적입니다.
- 성능: 분산 락은 시스템의 전체 성능에 큰 영향을 미칠 수 있습니다. 락을 획득하고 해제하는 과정이 빈번하게 발생한다면, Redis와 같이 빠른 솔루션이 적합할 수 있습니다.
- 안정성 및 고가용성: 락을 관리하는 시스템 자체가 장애의 원인이 되어서는 안 됩니다. Redis Cluster, ZooKeeper 앙상블(ensemble) 등 고가용성 구성이 필요합니다.
결론
분산 락은 분산 시스템에서 데이터의 일관성을 보장하기 위한 필수적인 도구입니다. 각 구현 방식은 장단점이 명확하므로, 시스템의 요구사항(성능, 안정성, 복잡성 등)을 신중하게 고려하여 적절한 방법을 선택해야 합니다.