본문 바로가기
backend

[Redis] Redis를 이용한 동시성 이슈 해결 실습 - Redlock

by BK0625 2025. 2. 20.
반응형

https://bk0625.tistory.com/145

 

[Redis] Redis를 이용한 동시성 이슈 해결 실습해보기 - SETNX 사용 방법

동시성 문제란?여러개의 프로세스나 스레드가 동시에 같은 리소스, 예를 들자면 데이터베이스나 캐시, 파일 등에 접근 할 때 예상 치 못한 데이터 충돌, 중복 실행, 무결성 문제가 발생하는 상황

bk0625.tistory.com

 

지난 포스팅에서 Redis의 SETNX를 활용해 동시성 이슈를 해결해보았다. 해당 방법은 쉽게 구현할 수 있지만 여러 문제가 발생할 수 있다.

 

SETNX 방식은 단일 Redis 노드에서만 동작한다. 즉 장애 발생 시 안전하지 않으며 여러 redis 인스턴스 활용 시 사용할 수 없다. 이런 멀티 노드 환경에서도 데이터의 정합성을 유지하고 동시성 문제를 해결하기 위해 Redlock 알고리즘을 사용하게 된다.

 

 

왜 Redlock이 필요한가?

일반적으로 단일 Redis 인스턴스를 이용한 락 (SETNX + TTL)은 쉽게 구현할 수 있지만 다음과 같은 문제점이 남는다.

  • 단일 장애점 (Single Point of Failure, SPOF) -> Redis가 다운되면 락이 사라져버림
  • 클라이언트가 락을 놓기 전에 Redis가 다운되면 락이 영원히 남게 된다.
  • 네트워크 지연이나 장애로 인해 락을 놓지 못하는 경우 동시성 문제가 발생

이를 해결하기 위해 다중 Redis 노드를 활용하여 안정성 높은 분산 락 알고리즘이 Redlock이라고 할 수 있다.

 

 

Redlock 동작 방식

  1. 여러 개의 Redis 노드에 동일한 락을 설정 
  2. 과반수 이상의 노드에서 락 획득 시 성공
  3. 일정 시간이 지나면 TTL 설정을 통해 자동을 해제
  4. 노드 장애가 발생하더라도 락을 유지할 수 있다.

따라서 Redlock은 다중 Redis 노드에서 락을 획득하기 때문에 단일 노드 장애에도 안전하며, TTL을 활용하여 자동 해제 하기 때문에 Deadlock이 방지되며 고가용성이 보장 된다.

 

 

예제 코드

 

1. 프로젝트 폴더 및 파일 구성

mkdir redis-redlock-example && cd redis-redlock-example
mkdir src
touch docker-compose.yml
touch src/index.js
touch Dockerfile

 

 

2. docker-compose.yml 작성

version: "3.8"

services:
  redis-node-1:
    image: redis:latest
    container_name: redis-1
    ports:
      - "6380:6379"

  redis-node-2:
    image: redis:latest
    container_name: redis-2
    ports:
      - "6381:6379"

  redis-node-3:
    image: redis:latest
    container_name: redis-3
    ports:
      - "6382:6379"

  app:
    build: .
    container_name: node-app
    depends_on:
      - redis-node-1
      - redis-node-2
      - redis-node-3
    environment:
      - REDIS_NODES=redis-node-1:6379,redis-node-2:6379,redis-node-3:6379
    volumes:
      - .:/usr/src/app
    command: ["node", "src/index.js"]

 

 

3. dockerfile 작성

# Node.js 이미지 사용
FROM node:18

# 작업 디렉터리 설정
WORKDIR /usr/src/app

# package.json 복사 및 패키지 설치
COPY package.json ./
RUN npm install

# 소스 코드 복사
COPY . .

# 실행 명령어
CMD ["node", "src/index.js"]

 

 

4. package.json 작성

npm init -y
npm install redlock ioredis

 

 

5. src/index.js 작성

import Redis from "ioredis";
import Redlock from "redlock";

// Redis 클러스터 노드 연결
const redisClients = [
    new Redis({ host: "redis-node-1", port: 6379 }),
    new Redis({ host: "redis-node-2", port: 6379 }),
    new Redis({ host: "redis-node-3", port: 6379 }),
];

// Redlock 인스턴스 생성
const redlock = new Redlock(redisClients, {
    driftFactor: 0.01, // 클럭 드리프트 고려 비율
    retryCount: 3, // 재시도 횟수
    retryDelay: 200, // 재시도 간격 (ms)
    retryJitter: 200, // 랜덤 지연시간 추가
});

async function processWithRedlock() {
    const lockKey = "my_distributed_lock";
    const ttl = 5000; // 락 유지 시간 (5초)

    try {
        // 락 획득
        const lock = await redlock.lock(lockKey, ttl);
        console.log("✅ Redlock 획득! 작업 수행 중...");

        // 작업 수행 (예: 3초 대기)
        await new Promise((resolve) => setTimeout(resolve, 3000));

        // 락 해제
        await lock.unlock();
        console.log("🔓 Redlock 해제 완료!");
    } catch (error) {
        console.log("🔒 다른 프로세스가 락을 잡고 있습니다. 다시 시도하세요.");
    }
}

// 테스트 실행
processWithRedlock();

 

 

6. 실행 및 테스트

docker-compose up —build

 

테스트 방법

  1. 터미널 2개 열기
  2. 각각의 터미널에서 다음 명령어 실행
    1. docker-compose run --rm app
  3. 첫번째 프로세스가 락을 잡고 실행 중일 때, 두 번째 프로세스는 락을 못 잡고 대기해야 한다.

 

소스코드는 해당 링크에 있다.

https://github.com/chobkyu/node-redis-distributed-lock

 

GitHub - chobkyu/node-redis-distributed-lock: nodejs, docker, redis를 사용한 분산 락 예제 코드

nodejs, docker, redis를 사용한 분산 락 예제 코드. Contribute to chobkyu/node-redis-distributed-lock development by creating an account on GitHub.

github.com

 

공부하면서 정리한 내용입니다. 모든 지적 감사히 받겠습니다:)

반응형