분산시스템

etcd와 redis의 분산락 비교

gototech 2025. 4. 15. 09:32

안녕하세요! 오늘은 제가 지난 2년간 실제 프로젝트에서 맞닥뜨렸던 분산 락(Distributed Lock) 이슈와 Redis, etcd 사용 경험을 공유해볼까 합니다. 특히 이 두 시스템을 각각 언제 써야 효과적인지, 제 삽질 경험을 바탕으로 이야기해볼게요.

왜 분산 락이 필요했는지

우리 팀이 처음 분산 락을 고려하게 된 건 꽤 아픈 경험 때문이었어요. 어느 날 아침, 배치 작업이 중복으로 실행되어 일부 고객에게 같은 이메일이 두 번 발송되는 사고가 있었거든요.

알고보니 여러 인스턴스가 스케일 아웃되면서 같은 배치 작업을 동시에 처리했던 거였습니다. 이런 일이 또 발생하지 않도록 급하게 분산 락 솔루션을 찾아보기 시작했죠.

분산 락이 필요한 대표적인 상황들을 제 경험에서 꼽자면:

  1. 중복 처리 방지 - 우리 팀의 경우 결제 처리나 정산 배치 같은 작업이 두 번 실행되면 큰일 나는 상황이었어요.
  2. 동시 업데이트 문제 - 여러 서버에서 동시에 같은 데이터를 수정하려고 할 때, 특히 "읽고-수정하고-쓰기" 패턴에서 발생하는 데이터 정합성 문제가 있었습니다.
  3. 순차 처리 보장 - 금융 거래는 순서가 정말 중요하잖아요. 이런 작업들이 뒤섞이지 않도록 하는 장치가 필요했습니다.
  4. 마스터 선출 - 특정 작업은 한 서버에서만 실행해야 했는데, 어떤 서버가 담당할지 정하는 간단한 방법이 필요했어요.

Redis로 분산 락 구현 - 빠르고 간단하다!

첫 번째로 시도한 건 Redis였어요. 이미 캐싱용으로 사용 중이었기 때문에 추가 인프라 없이 바로 적용할 수 있었거든요.

구현은 정말 간단했어요

 
// 락 획득 시도
SET lock_key unique_value NX PX 10000  // 10초 타임아웃으로 설정

// 락 해제 (LUA 스크립트로 안전하게)
if redis.call("get", KEYS[1]) == ARGV[1] then
    return redis.call("del", KEYS[1])
else
    return 0
end

좋았던 점

  • 속도가 미쳤어요 - 초당 수만 건의 락 획득/해제가 가능했습니다.
  • 5분만에 구현 완료 - 정말 단순해서, 프로토타입을 빠르게 만들 수 있었어요.
  • 기존 인프라 활용 - 새로운 시스템을 도입할 필요가 없었죠.

나타난 문제점

한 달 정도 잘 쓰다가 문제가 발생했어요. 네트워크 일시 단절 상황에서 두 서버가 모두 같은 리소스에 대한 락을 획득하는 일이 벌어진 거죠. 

특히 우리 서비스처럼 사용자 돈과 관련된 부분에서는 이런 일관성 문제가 치명적이었어요. Redlock 알고리즘을 고려해봤지만, 구현 복잡도가 높아 다른 대안을 찾게 되었습니다.

etcd 도입 - 안정성이 필요했어요

결국 etcd로 갈아타기로 결정했어요. 구현은 조금 복잡해졌지만, 안정성이 크게 향상되었습니다.

제가 작성한 etcd 락 코드 일부(go버전)

 
go
// 10초 리스(lease) 생성
resp, err := cli.Grant(ctx, 10)
if err != nil {
    log.Fatal("리스 생성 실패:", err)
}
leaseID := resp.ID

// 트랜잭션으로 락 획득 시도
txn := cli.Txn(ctx).
    If(clientv3.Compare(clientv3.CreateRevision("lock_key"), "=", 0)).
    Then(clientv3.OpPut("lock_key", "value", clientv3.WithLease(leaseID))).
    Else()

txnResp, err := txn.Commit()
if err != nil {
    log.Fatal("트랜잭션 실패:", err)
}

if !txnResp.Succeeded {
    fmt.Println("락 획득 실패 - 이미 사용 중")
    return
}

fmt.Println("락 획득 성공!")

etcd가 주는 안정성

etcd는 Raft 합의 알고리즘을 사용해서 클러스터의 모든 노드가 동일한 상태를 유지하도록 보장합니다. 이건 정말 중요한 특징이었어요. 네트워크 파티션이 발생해도 데이터 일관성을 유지할 수 있었거든요.

또한 etcd의 트랜잭션 기능을 사용하면 "이 키가 없을 때만 생성하라"와 같은 조건부 연산을 원자적으로 수행할 수 있어요. 이건 안전한 락 구현에 정말 중요했습니다.

무엇보다 좋았던 건 Watch 기능이었어요. 락이 해제되면 바로 알림을 받을 수 있어서, 락을 기다리는 다른 프로세스가 불필요하게 폴링할 필요가 없었습니다.

기타 유용했던 etcd 기능들

사실 처음엔 분산 락만을 위해 도입했지만, 나중엔 다른 기능들도 활용하게 되었어요:

  • 설정 관리 - 애플리케이션 설정을 etcd에 저장하고, 변경 사항을 실시간으로 모든 서버에 적용할 수 있었습니다.
  • 서비스 디스커버리 - 마이크로서비스 간 통신에서 서비스 등록 및 발견용으로 활용했어요.
  • 리더 선출 - 특정 작업을 담당할 "마스터" 서버를 선출하는 데 사용했습니다.

실제로 어떤 걸 선택해야 할까?

두 시스템을 모두 실 서비스에 적용해본 결과, 선택 기준은 꽤 명확했습니다:

Redis가 적합한 경우

  • 속도가 중요할 때 - 정말 빠른 락이 필요하다면 Redis가 최고예요.
  • 단순한 구현이 필요할 때 - 빠르게 구현하고 싶다면 Redis가 좋습니다.
  • 락 유지 시간이 짧을 때 - 몇 초 이내의 짧은 락이라면 Redis로도 충분해요.
  • 이미 Redis가 있다면 - 새로운 시스템 도입 없이 바로 시작할 수 있어요.

etcd가 적합한 경우

  • 데이터 정합성이 중요할 때 - 저희처럼 금융 관련 서비스라면 etcd를 강추합니다.
  • 장기 실행 락이 필요할 때 - 몇 분, 몇 시간 지속되는 락이라면 etcd가 안정적이에요.
  • 네트워크 불안정에 대비해야 할 때 - etcd는 네트워크 파티션에 강합니다.
  • 분산 시스템 기능이 더 필요할 때 - 설정 관리, 서비스 디스커버리 등을 함께 사용하려면 etcd가 좋아요.

저는 결국 etcd를 선택했어요

저는 금융 서비스, 특히 입금/출금 처리 시스템을 담당하고 있어서 데이터 일관성과 안정성이 최우선이었습니다. 처음엔 Redis의 단순함에 끌렸지만, 결국 etcd의 안정성이 우리 서비스에 더 중요하다고 판단했어요.

특히 Redis에서 발생했던 네트워크 순단 시 락 중복 획득 문제가 etcd에서는 없었던 점이 결정적이었습니다. 속도는 약간 희생했지만, 당연히 신뢰성이 높아야하는 시스템이였기에 etcd를 선택했어요!

다음 글에서는 Spring 환경에서 etcd를 활용한 분산 락 구현 예시를 좀 더 상세하게 공유해볼게요.

마무리

분산 락 시스템 선택은 결국 트레이드오프의 문제입니다. 높은 처리량과 단순함을 원한다면 Redis, 강한 일관성과 안정성을 원한다면 etcd가 더 적합할 것입니다.

 

저같은경우 금융&입금쪽에서 일하고있어 신뢰성이 매우 높아야하기에 etcd를 채택하게 되었습니다.

다음 글은 스프링에서 구현한 etcd 예시를 보겠습니다

'분산시스템' 카테고리의 다른 글

카프카의 특징  (1) 2025.05.06
etcd를 이용한 분산락 예시(java)  (1) 2025.04.16
CAS(Compare and Swap) 알고리즘에 관하여  (0) 2025.04.15