Skip to content

레디스 분산락 블로그 포스팅 & 공부용 프로젝트

Notifications You must be signed in to change notification settings

Cheondongmin/redis_lock_practice

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

분산락 (Distributed Lock) 데모 프로젝트

Redis + Redisson 기반 분산락을 활용한 재고 관리 시스템 구현 예제

프로젝트 구조

src/main/kotlin/com/example/demo/
├── config/
│   ├── RedissonConfig.kt           # Redisson 클라이언트 설정
│   └── DataLoader.kt                # 초기 데이터 로딩
├── lock/
│   └── DistributedLockTemplate.kt   # 분산락 템플릿 (핵심)
├── domain/
│   └── Stock.kt                     # 재고 엔티티
├── repository/
│   └── StockRepository.kt           # 재고 리포지토리
├── service/
│   └── StockService.kt              # 재고 비즈니스 로직
└── controller/
    ├── StockController.kt           # 재고 관리 API
    └── ConcurrencyTestController.kt # 동시성 테스트 API

사전 준비

1. Redis 실행

Docker 사용 (권장)

docker run -d --name redis -p 6379:6379 redis:latest

로컬 설치

Redis 연결 확인:

redis-cli ping
# PONG 응답이 나와야 함

2. 프로젝트 빌드

./gradlew clean build

실행 방법

1. 애플리케이션 시작

./gradlew bootRun

서버가 http://localhost:8080 에서 실행됩니다.

2. H2 콘솔 접속 (선택사항)

브라우저에서 http://localhost:8080/h2-console 접속

  • JDBC URL: jdbc:h2:mem:testdb
  • Username: sa
  • Password: (비워두기)

API 테스트

기본 API

1. 재고 생성

curl -X POST http://localhost:8080/api/stock \
  -H "Content-Type: application/json" \
  -d '{"productName": "노트북", "quantity": 100}'

2. 재고 조회

curl http://localhost:8080/api/stock/1

3. 재고 차감 (분산락 사용)

curl -X POST http://localhost:8080/api/stock/1/decrease

4. 재고 차감 (락 없음 - 비교용)

curl -X POST http://localhost:8080/api/stock/1/decrease-no-lock

동시성 테스트 API

1. 분산락 사용 (100개 동시 요청)

curl -X POST "http://localhost:8080/api/test/fire-with-lock?stockId=1&threads=100"

예상 결과:

{
  "testType": "분산락 사용",
  "totalThreads": 100,
  "successCount": 100,
  "failCount": 0,
  "finalQuantity": 0,
  "durationMs": 2500
}
  • 재고가 정확히 100 감소

2. 분산락 없음 (Race Condition 발생)

curl -X POST "http://localhost:8080/api/test/fire-without-lock?stockId=1&threads=100"

예상 결과:

{
  "testType": "락 없음 (Race Condition 발생 가능)",
  "totalThreads": 100,
  "successCount": 100,
  "failCount": 0,
  "finalQuantity": 30,
  "durationMs": 800
}
  • Race Condition으로 인해 재고가 예상보다 적게 감소 (30~70 남음)

3. 재시도 로직 포함 분산락

curl -X POST "http://localhost:8080/api/test/fire-with-retry?stockId=1&threads=100"

예상 결과:

{
  "testType": "분산락 사용 (재시도 포함)",
  "totalThreads": 100,
  "successCount": 100,
  "failCount": 0,
  "finalQuantity": 0,
  "durationMs": 3200
}

핵심 코드 설명

1. DistributedLockTemplate

fun <T> lock(
    lockKey: String,
    waitTime: Long = 5,  // 락 획득 대기 시간 (초)
    leaseTime: Long = 3, // 락 자동 해제 시간 (초)
    block: () -> T
): T {
    val lock = redissonClient.getLock(lockKey)
    val acquired = lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS)

    if (!acquired) {
        throw IllegalStateException("Lock 획득 실패: $lockKey")
    }

    return try {
        block()
    } finally {
        if (lock.isHeldByCurrentThread) {
            lock.unlock()
        }
    }
}

2. 분산락 적용 예시

fun decreaseWithLock(stockId: Long) {
    val lockKey = "stock:lock:$stockId"

    lockTemplate.lock(lockKey) {
        decreaseStock(stockId)
    }
}

주요 파라미터 설명

waitTime

  • 락 획득을 기다리는 최대 시간
  • 이 시간 동안 락을 획득하지 못하면 실패
  • 기본값: 5초

leaseTime

  • 락을 자동으로 해제하는 시간
  • Deadlock 방지를 위한 안전장치
  • 기본값: 3초
  • 작업 시간보다 충분히 길게 설정

트러블슈팅

Redis 연결 실패

Unable to connect to Redis server

해결: Redis 서버가 실행 중인지 확인

docker ps | grep redis
# 또는
redis-cli ping

락 획득 실패

Lock 획득 실패: stock:lock:1

원인:

  1. waitTime이 너무 짧음 → 늘려보기
  2. leaseTime이 너무 짧아서 작업 완료 전에 락이 해제됨

해결: 파라미터 조정

lockTemplate.lock(lockKey, waitTime = 10, leaseTime = 5) {
    // ...
}

H2 데이터베이스 초기화

애플리케이션을 재시작하면 데이터가 자동으로 초기화됩니다. (ddl-auto: create 설정)

성능 비교

테스트 환경

  • 재고: 100개
  • 동시 요청: 100개
  • 스레드 풀: 32

결과 비교

구분 처리 시간 최종 재고 정확성
분산락 사용 ~2.5초 0 ✅ 100%
분산락 없음 ~0.8초 30~70 ❌ 30~70%
재시도 포함 ~3.2초 0 ✅ 100%

결론: 분산락 없이는 3배 빠르지만 데이터 정합성이 깨짐

실전 적용 시나리오

적합한 케이스

  • ✅ 재고 차감
  • ✅ 결제 승인
  • ✅ 좌석 예약
  • ✅ 쿠폰 발급
  • ✅ 중복 주문 방지

부적합한 케이스

  • ❌ 단순 읽기 작업
  • ❌ 긴 배치 작업 (락 타임아웃 위험)
  • ❌ 높은 처리량이 필요한 API (성능 저하)

주의사항

1. Deadlock 방지

  • leaseTime을 반드시 설정
  • 작업 시간보다 충분히 길게
  • 락 내부에서 Thread.sleep() 금지

2. 성능 최적화

  • 락 범위를 최소화
  • blocking I/O 금지
  • 빠른 작업만 락으로 보호

3. Redis 장애 대비

  • Fail Fast 전략 권장
  • 필요시 Fallback 로직 구현
  • 모니터링 필수

참고자료

About

레디스 분산락 블로그 포스팅 & 공부용 프로젝트

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages