728x90

주문을 받아와 처리를 하다보면  재고 처리를 위해 동시성 제어가 필요하다.

 

아래는 여러가지 방법을 간략하게 정리해보고  내가 선택한 방식 이유 와 장점을 정리했다.

 

 

1. Redis 큐를 이용한 직렬화 방식

 

Redis 리스트 자료구조를 활용해 큐를 만들어 처리 

큐의 FIFO 를 이용해  LEFT PUSH   이후  RIGHT POP을 통하여 메시지를 하나씩 꺼내 처리 

 

장점  -  비즈니스 무결성 보장, 고속 Redis 기반 처리로 성능 우수, DLQ 이용 실패한 큐의 재전송 용이

 

단점- Redis 장애 시 전체 시스템 영향 가능성, Redis TTL 설정 누락 시 큐가 무한히 쌓일 의험, 운영 중 큐/워크 스케쥴러 튜닝 필요


2.Synchronized, ReentrantLock (단일 인스턴스 JVM 내 락)

 

JAVA 자체  락을 통해 Critical Section을 직렬화

단일 서버 환경에 적합, Spring boot 단독 서버에서 간단하게 사용가능

 

장점 - 구현이 단순하고 추가 인프라 필요 X, JVM 내에서 완벽하게 락을 보장

단점 - 서버 인스턴스가 늘어나면 무용지물 (인스턴스별 메모리 영역 분리),  클러스터 환경에서는 동작 불가

 

현재 구조에 맞지 않음(멀티 서버 구조) 


3. 데이터베이스 기반의 락

 

비관적 락(Pessimistic Lock)

데이터를 SELECT FOR UPDATE 형태로 가져와 락을 건 채 트랜잭션을 유지

동시에 접근하는 모든 트랜잭션은 대기하게됨

 

//ex
@Lock(LockModeType.PESSIMISTIC_WRITE)
Order findByOrderNo(String orderNo);

 

장점 - 경쟁이 매우 치열하거나 순서 보장이 중요한 데이터에 적합, DB 레벨에서 무조건 직렬화 보장

단점 - 트랜잭션 지연 발생, 데드락 발생 위험

 

 

낙관적 락(Optimistic Lock)

레코드를 가져올 땐 락 없이 처리

저장 시점에 version필드 등을 비교해 충돌 여부 판단

 

//ex

@Version
private Long version;


order.setQty(10);
orderRepository.save(order); // version mismatch 시 예외 발생

 

장점 - 충돌이 적은 환경에서 효율적 (대부분의 웹 시스템에 적합), 락을 잡지 않으므로 성능 유리

단점 - 충돌 발생 시 재시도 로직 필요, 실시간성이 요구되는 시스템에는 부적합


4. Redis 분산 락 (Redisson / Lettuce)

Redis SETNX 또는 Lua 스크립트를 활용해 락 키를 생성하고 만료 시간을 설정

멀티 인스턴스 환경에서 유일한 자원 접근 보장

 

//ex

RLock lock = redissonClient.getLock("lock:account:" + accountId);
if (lock.tryLock(5, 10, TimeUnit.SECONDS)) {
    try {
        // 주문 처리
    } finally {
        lock.unlock();
    }
}

 

장점 - 클러스트 환경에서의 락 보장,TTL 및 자동 만료 설정 가능

단점 - 락 획득 실패 시 로직 설계가 필요, Redis 장애 시 전체 처리 지연 가능

*락 키는 "lock:business-key" 형태로 설계하여 중복 방지


5. Kafka 파티셔닝 기반 직렬화

Kafka의 파티셔닝 기능을 활용해 동일한 accountId 가 항상 같은 Partition으로 전송되도록 설정

Consumer는 파티션 단위로 순차 소비

 

//ex
//consumer는 파티션 내부에서는 메시지 순서보장
ProducerRecord<String, String> record = new ProducerRecord<>("topic", accountId, payload);

 

장점 - 높은 확장성과 처리량( 수평 확장에 최적화), 분산환경에서도 순차 보장 가능

단점 - Kafka 운영 복잡도 증가(broker, topic, consumer group 관리), 장애 복구나 DLQ 재처리에 별도 설계 필요

 

* 이벤트 기반 마이크로서비스, 대용량 메시징 시스템


 

방법 환경 순서 보장 분산 처리 성능 복잡도

Redis 큐 분산 ★★★★☆ ★★☆☆☆
ReentrantLock 단일 ★★★☆☆ ★☆☆☆☆
Pessimistic Lock DB 트랜잭션 ★★☆☆☆ ★★☆☆☆
Optimistic Lock DB, 낮은 충돌 환경 ✖(충돌 가능성) ★★★★☆ ★★☆☆☆
Redis 분산 락 멀티 인스턴스 ★★★☆☆ ★★★☆☆
Kafka 파티션 분산 이벤트 ✔(파티션 내) ★★★★★ ★★★★☆

 

 

현재 작업중인 서버를 고려해 1번 Redis 큐를 활용한 방법을 선택했따.

 

  • 외부 API 호출 기반의 처리 구조
  • 계정 단위 직렬화 처리 필요
  • 장애 발생 시 유연한 재처리 체계 필요
  • 복잡하지 않으면서도 확장 가능한 구조 지향

 

결과적으로 Redis 큐를 중심으로 구성한 주문 처리 워커 및 DLQ 재처리 시스템은 고립된 처리 단위를 보장하면서, 비즈니스의 신뢰성을 유지하고, 운영 편의성과 확장성까지 확보할 수 있는 적절한 선택이지 않나 싶다.

 

728x90

'코드 > dev' 카테고리의 다른 글

DDL 작업 중 복제지연 원인과 해결 방안  (3) 2025.05.22
도커(Docker) 명령어  (0) 2025.04.22
도커(Docker) 컨테이너(Container)  (1) 2025.04.21
Bastion  (0) 2025.04.13
처리율 제한 알고리즘  (0) 2025.04.04

+ Recent posts