Using Distributed Locks for Inventory Transfers in WMS
12주차: WMS 재고 이관을 위한 분산 락 사용기
https://www.youtube.com/watch?v=3HCVD26zycM&list=PLgXGHBqgT2Tu7H-ita_W0IHospr64ON_a&index=23
🏷️ 개요
WMS(Warehouse Management System)는 물류센터 내 재고의 입고-보관-출고 전 과정을 관리하는 시스템입니다. 본 포스트에서는 우아한형제들이 WMS 시스템을 운영하면서 겪었던 동시성 문제를 해결해 나간 과정을 다룹니다. 특히 재고 이관 과정에서 발생한 할당 vs 취소 요청의 충돌을 어떻게 분산락과 상태 키를 활용해 해결했는지 그 과정을 상세히 풀어보려 합니다.
🏢 WMS와 물류센터 구조 이해
🏬 WMS란?
WMS는 Warehouse Management System의 약자로, 창고 내 재고의 입고, 저장, 출고를 관리하는 시스템입니다. 우아한형제들의 B마트 상품은 이 시스템을 통해 운영됩니다.
📦 물류 흐름 요약
중앙물류기지: 공급사로부터 상품이 입고됨
피패킹 센터(Picking + Packing): 지역 거점 물류센터, 실제 고객 주문이 처리되는 현장
재고 이관: 중앙물류기지 → 피패킹 센터로 재고가 이동
🔄 재고 이관 프로세스
📝 이관 요청서 생성
재고 이관의 시작은 이관 요청서를 생성하는 것입니다. 하나의 요청서에는 수십~수백 개의 상품이 포함될 수 있습니다.
🔍 재고 선점(할당)
이관을 위해선 특정 위치에 있는 재고를 미리 선점해야 함
이는 재고 할당 작업이라 불리며, 위치별로 흩어진 동일 상품 중에서 적절한 위치의 재고를 자동 선택하여 할당합니다.
⚠️ 장애 발생: 할당 vs 취소 충돌
💥 문제 상황
하나의 이관 요청서에 대해 두 명의 관리자가 동시에:
A: 할당 요청
B: 취소 요청
결과:
재고는 할당됨
상태는 '취소'로 덮어쓰기됨 ❌
🧪 원인 분석
할당: 분산락으로 보호됨 (이관요청서 ID 기준)
취소: 락 없이 상태 변경
즉, 락이 없던 취소 로직이 할당 상태를 덮어쓴 것이 문제였습니다.
🔐 1단계 개선: 분산락 추가
🛠️ 해결 방안 1: 취소에도 분산락 적용
할당과 취소 모두 동일한 분산락 키 사용 → 동시성 제어 성공
🧱 부작용
하나의 요청서에 여러 상품이 할당될 경우, 락이 중복되어 병렬 할당 불가
예: 사과는 할당되었으나, 케이크는 락 획득 실패 → 요청서 상태가 '부분 할당'으로 종료
🕐 2단계 개선: 락 대기
⏳ 해결 방안 2: 락 대기 허용
여러 상품 할당 시 락 획득을 대기
병렬은 불가능하지만, 결국 모두 할당됨
🐢 단점
상품 수가 많아질수록 전체 할당 시간 증가
100개 상품이면 100번 순차적으로 락 대기 → 성능 저하
🚀 3단계 개선: 상태 키 활용 + 병렬 처리
💡 핵심 아이디어
실제 재고 할당은 락 밖에서 처리
대신 요청 유형(
할당
,취소
)만 상태 키로 관리
⚙️ 구현 방식
요청 시작 시: 락 획득하여 상태 키 확인 및 설정
현재 상태와 동일한 유형이면: 유효시간 갱신하고 병렬 처리 허용
다르면: 예외 발생 → 이후 로직 중단
⛓️ 병렬 예시 흐름
사과 할당 요청 → 락 획득 → 상태를 '할당 중'으로 설정 → 락 해제 → 재고 할당 시작
케이크 할당 요청 → 락 획득 → 동일 상태 확인 → 병렬 처리 시작
두 재고 할당 완료 후 상태 변경 (부분 할당 → 할당)
🔄 상태 키 로직 상세 분석
🧾 상태 키 조회 로직
lockService.withLock(requestId, () -> {
String currentState = stateKey.get(requestId);
if (currentState == null || currentState.equals(requestType)) {
stateKey.set(requestId, requestType, TTL);
} else {
throw new ConflictException();
}
});
🤝 병렬 처리 가능 조건
동일 요청 유형끼리는 병렬 허용
상이한 요청 유형(할당 vs 취소)은 병렬 불가 → 예외 발생
📈 성능과 안정성 향상
✅ 동시성 문제 해결
상이한 유형 간의 충돌 제거 (락으로 보호)
동일 유형 병렬 처리 허용 (성능 개선)
📉 처리 시간 감소
분산락으로 전체 처리 병목 제거
다수 상품 병렬 할당 가능
📚 기술 키워드 설명
🔐 분산락 (Distributed Lock)
분산 시스템에서 동일한 자원에 대한 접근을 순차적으로 보장하기 위한 기술
Redis 기반으로 주로 구현 (ex. Redisson)
📌 상태 키(State Key)
특정 리소스의 상태를 관리하기 위한 키값
"재고이관요청서:ID" -> 할당중/취소중
형태로 저장
⚙️ 병렬 처리
작업을 동시에 수행하여 처리 시간을 단축시키는 방식
단, 상태 충돌 방지를 위해 사전 제어 필요
🧠 배운 점 & 인사이트
✅ 트랜잭션보다 락의 타이밍이 중요하다
DB 커밋 완료 전/후 락 설정 타이밍에 따라 결과가 완전히 달라짐
✅ 락은 범위와 목적에 따라 다르게 써야 한다
단순히 '락을 걸면 된다'가 아니라, 어디에, 언제, 무엇을 위해 락을 거는가가 중요
✅ 일관성과 성능은 트레이드오프다
1단계는 일관성에 집중 → 성능 저하
3단계는 병렬성 확보하면서도 일관성 유지 → 최적
✅ 상태 기반 동시성 제어 패턴은 확장성이 뛰어나다
단일 락 키 → 병렬 제약
상태 기반 접근 → 더 넓은 병렬성과 안전성 확보
느낀점
이번 개선 과정을 통해 분산 시스템에서의 동시성 제어는 단순한 트랜잭션 처리만으로는 부족하다는 것을 배웠고, 특히 요청 단위의 상태 일관성을 유지하기 위해 분산락과 상태 키를 조합해 사용하는 전략을 사용할 수 있다는 것을 배웠습니다. 동시에 병렬성과 정합성 사이의 트레이드오프를 조율하는 것이 시스템 설계의 핵심인것 같습니다. 도메인 지식이 부족해서 이해가 어려웠던 점이 아쉬웠습니다.
Last updated