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 동작 방식
- 여러 개의 Redis 노드에 동일한 락을 설정
- 과반수 이상의 노드에서 락 획득 시 성공
- 일정 시간이 지나면 TTL 설정을 통해 자동을 해제
- 노드 장애가 발생하더라도 락을 유지할 수 있다.
따라서 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
테스트 방법
- 터미널 2개 열기
- 각각의 터미널에서 다음 명령어 실행
- docker-compose run --rm app
- 첫번째 프로세스가 락을 잡고 실행 중일 때, 두 번째 프로세스는 락을 못 잡고 대기해야 한다.
소스코드는 해당 링크에 있다.
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
공부하면서 정리한 내용입니다. 모든 지적 감사히 받겠습니다:)
'backend' 카테고리의 다른 글
[Server] Nginx (0) | 2025.03.26 |
---|---|
[Redis] Redis를 이용한 동시성 이슈 해결 실습해보기 - SETNX 사용 방법 (0) | 2025.02.20 |