일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 스프링 부트와 JPA
- 이것이자바다
- Java의정석
- java
- 자바공부
- 자바의정석 연습문제
- 항해플러스 백엔드
- 항해플러스 회고
- JPA
- 불친절한SQL프로그래밍
- REGEXP_SUBSTR
- inflearn
- Secure Coding
- 제네릭
- 시큐어코딩
- 자바의정석
- 스프링 핵심원리
- Python
- 인프런
- Numpy
- 항해플러스 백엔드 7기
- 스프링MVC
- 자바연습문제
- 항해플러스
- 스프링입문
- 스프링
- Spring
- 김영한
- 서블릿
- 불친절한 SQL 프로그래밍
- Today
- Total
Continuous Challenge
[항해플러스 7기 백엔드] 6주차 회고 - 주요 기능 별 Locking 방법 비교/분석, Redis 를 사용한 분산락(RedissonLock) 도입 본문
[항해플러스 7기 백엔드] 6주차 회고 - 주요 기능 별 Locking 방법 비교/분석, Redis 를 사용한 분산락(RedissonLock) 도입
응굥 2025. 1. 29. 01:586주차에는 진행중인 이커머스 서비스 프로젝트의 주요 기능(주로 동시성 제어가 필요한 기능) 분석 및 각 기능별로 적합한 Locking 방법을 찾기 위해 도입 및 비교/분석해보는 시간을 가졌습니다.
자세한 내용은 이전 글에 작성해 두었습니다. (아래 링크 첨부)
이번 글은 -습니다 투가 되어버렸네..
각 랑킹방식의 개념 및 구현 방법
1. 낙관적 락 (Optimistic Lock)
- 낙관적 락은 데이터 충돌이 자주 발생하지 않을 것이라고 가정하고 설계된 동시성 제어 방식입니다.
장점
- 락을 걸지 않아 데이터베이스 락 오버헤드가 적습니다.
- 동시성 문제가 적은 시스템에서 효율적으로 동작합니다.
단점
- 충돌이 빈번한 시스템에서는 OptimisticLockException이 자주 발생할 수 있습니다.
- 재시도 로직을 추가로 구현해야 할 수 있습니다.
구현방법
1) 엔티티에 @Version 추가 (낙관적 락 관리용 필드)
- @Version 필드는 JPA가 관리하며, 데이터 변경 시마다 자동으로 증가합니다.
- 데이터 충돌이 감지되면 JPA가 OptimisticLockException을 던집니다.
2) 서비스 로직에 낙관적 락 예외 발생 시 재시도 로직 및 복구 로직을 구현합니다.
- @Retryable
- Spring Retry의 기능으로, 특정 예외 발생 시 메서드를 자동으로 재시도하도록 설정합니다.
- 재시도 횟수, 간격, 대상 예외 등을 설정할 수 있습니다.
- @Recover
- @Retryable로 재시도를 모두 실패한 경우 실행되는 복구 메서드입니다.
- 실패한 상황에 대한 대체 로직을 제공하거나 예외를 처리할 수 있습니다.
- @Recover 메서드는 반드시 재시도 대상 메서드와 같은 클래스에 있어야 합니다.
주문 로직 내 상품 재고 차감 시 낙관적 락을 사용하고자 하였지만, 동시에 발생된 요청에 대해 낙관적락은 최초의 요청(커밋)에 대해서만 허용하는 이슈가 있었다. 이를 재시도 로직을 통해 보완 가능하지만 재시도 로직으로 인한 성능 저하가 발생합니다.
또한 재고 차감은 동시성 충돌의 발생 가능성이 낮은 기능으로 판단하기 어려웠기에 (한정 판매 상품 등의 이벤트 발생 가능) 락킹 방식을 낙관적 락에서 비관적 락으로 변경하였습니다.
낙관적 락은 충돌 가능성이 낮고, 읽기 작업이 많은 환경에서 사용하기에 적합하다고 판단했습니다.
2. 비관적 락 (Pessimistic Lock)
- 비관적 락은 데이터 충돌이 자주 발생할 것이라고 가정하고, 데이터에 대해 락(lock)을 걸어 다른 트랜잭션이 접근하지 못하게 합니다.
- 데이터의 무결성을 보장하기 위해 데이터베이스 레벨에서 락을 사용합니다.
- 동시성 문제를 방지하기 위해 데이터 조회나 수정 시 즉시 락을 설정합니다.
장점
- 락을 설정하여 트랜잭션 간 충돌을 원천적으로 차단합니다.
- 동시 수정 시 데이터 일관성을 유지할 수 있습니다.
단점
- 트랜잭션이 오래 유지될 경우, 다른 트랜잭션이 대기 상태에 빠지며 성능이 저하될 수 있습니다.
- 여러 트랜잭션이 교차로 락을 대기하면 데드락(교착 상태)이 발생할 위험이 있습니다.
- 트랜잭션이 많아질수록 병렬 처리 효율성이 감소합니다.
구현방법
1) QueryDSL 을 사용해 비관적 락으로 LockMode를 설정합니다.
- JPQL 을 사용하여 구현할 수도 있습니다.
@Query("SELECT p FROM ProductInventory p WHERE p.id = :productId")
@Lock(LockModeType.PESSIMISTIC_WRITE)
Optional<ProductInventory> findWithLock(@Param("productId") Long productId);
- 다른 조회 쿼리에 QueryDSL을 적용하기도 하였고 네이티브 쿼리 사용을 지양하고자 QueryDSL을 사용하였습니다.
사용자의 포인트 충전 및 차감, 주문 시 상품 재고 차감 기능에서 동시성 제어가 가능하며 구현 복잡도가 낮은 비관적 락을 사용하였습니다.
3. 분산락 (Distributed Lock)
- 분산 락은 여러 서버(또는 노드) 간에 공유 자원의 동시성 문제를 해결하기 위해 사용되는 메커니즘입니다.
- 클러스터 환경에서 여러 프로세스가 동일한 자원(예: 데이터베이스, 파일 시스템)에 접근할 때, 하나의 프로세스만 자원에 접근하도록 보장합니다.
- 주로 분산 시스템에서 데이터 일관성과 무결성을 유지하기 위해 사용됩니다.
장점
- 분산 환경에서 데이터 일관성 보장. 여러 노드에서 자원 동시 접근을 제어.
- 확장성. 락 관리가 중앙 집중형 시스템을 사용하지 않고도 가능.
- 유연성: 다양한 도구(Redis, ZooKeeper 등)를 사용해 구현 가능.
단점
- 성능 오버헤드. 락 관리에 추가적인 네트워크 요청이 필요.
- TTL 관리 복잡성. 락의 TTL이 너무 짧으면 조기 해제되고, 너무 길면 자원 점유가 과도해질 수 있음.
- 락 관리 도구(Redis, ZooKeeper 등)가 다운되면 시스템 장애 발생 가능.
구현 방법
1) 락과 트랜잭션 순서의 중요성
- 락 획득 → 트랜잭션 실행 → 락 해제 순서로 수행해야 데이터 무결성을 보장할 수 있습니다.
- 락을 획득하지 않은 상태에서 트랜잭션을 실행하면, 다른 프로세스가 동시에 동일한 자원에 접근할 수 있어 데이터 충돌이 발생합니다.
- 락 해제 시점이 적절하지 않으면 데드락(Deadlock)이나 병목(Bottleneck) 현상이 발생할 수 있습니다.
트랜잭션의 시작 전과 후에서 Lock 획득 및 해제가 실행될 수 있도록 하는 방안으로 AOP를 사용하였습니다.
AOP를 사용하는 이유
(1) 횡단 관심사(Cross-Cutting Concern)의 분리
- 락 관리와 트랜잭션 순서는 비즈니스 로직에 독립적인 공통 로직입니다.
- AOP를 사용하면 락 획득, 트랜잭션 실행, 락 해제를 분리하여 핵심 비즈니스 로직에만 집중할 수 있습니다.
(2) 코드 중복 제거
- AOP를 활용하면 락 관리 로직을 공통 모듈로 구현하고, 각 비즈니스 메서드에 횡단 관심사를 적용할 수 있어 코드 중복을 줄이고 유지 보수를 간소화합니다.
(3) 오류 처리와 재시도 로직 통합
- Redis 락의 TTL(Time-To-Live) 설정, 트랜잭션 실패 시 롤백 및 락 해제 같은 예외 상황 처리 로직을 AOP에 통합하여 일관성 있는 오류 처리를 보장합니다.
(4) 시스템 안정성 향상
- AOP로 락과 트랜잭션 순서를 중앙에서 관리하면, 특정 비즈니스 로직에서의 누락 또는 실수를 방지할 수 있습니다.
- 분산 락의 중요한 작업 흐름을 관리하는 데 효과적입니다.
1) AOP 어노테이션 정의
- value: SpEL 표현식을 사용하여 동적 락 키를 생성.
- waitTime & leaseTime: 락 획득 및 유지에 필요한 시간을 설정.
2) Redisson을 활용한 분산 락 구현 + Spring AOP
- Redisson은 Redis 기반의 고수준 추상화 라이브러리로, 분산 락을 포함한 여러 동기화 도구를 제공합니다.
- @Order(Ordered.HIGHEST_PRECEDNCE) : 가장 높은 우선순위를 가지도록 설정해 @Transactional 보다 항상 먼저 실행될 수 있도록 합니다. (락과 트랜잭션의 실행 순서 보장)
- 위에서 정의한 AOP 어노테이션(@DistributedLock)을 락 AOP의 pointcut으로 지정
- SpEL 표현식을 사용하여 락 키 생성
- 락을 생성한 후 실제 비즈니스 로직 실행
- 실제 비즈니스 로직이 종료되면 락을 해제
3) 비즈니스 로직에 적용
- AOP 어노테이션 적용. value 값으로 락의 key 값을 전달 (SpEL 표현식)
동시성 충돌 발생 가능성이 가장 높을 것이라고 예상한 선착순 쿠폰 발급 기능에 동시성 제어가 가능하고, 성능이 가장 우세했던 분산 락을 사용하였습니다.
이번 주차 과제를 통해 도입 전 테스트를 통한 비교의 중요성을 알 수 있었습니다. 이론적인 관점으로만 접근하였을 때와는 달리 실제 도입하였을 때 생각하지 못했던 이슈가 발생하거나 예상하지 못한 결과가 도출될 수 있기에 실제 업무에 적용하기 전에 테스트를 통한 비교가 꼭 필요하겠다는 생각을 하였습니다.
'Study > 항해플러스 7기' 카테고리의 다른 글
[항해플러스 7기 백엔드] 6주차 - 주요 기능(포인트 충전/차감, 상품 재고 차감, 선착순 쿠폰 발급)에 대한 동시성 제어 방식 비교 (0) | 2025.01.21 |
---|---|
[항해플러스 7기 백엔드] 5주차 회고 - 서버 구축하기 (Filter, Interceptor, Exception) (feat. 추천인코드) (2) | 2025.01.18 |
[항해플러스 7기 백엔드] 4주차 회고 - 서버 구축하기 (구현 시작!) (0) | 2025.01.12 |
[항해플러스 7기 백엔드] 3주차 회고 - 서버 구축하기 (설계부터~) (2) | 2025.01.05 |
[항해플러스 7기 백엔드] 2주차 회고 (2) - 트랜잭션 격리수준, 공유 락과 배타 낙관적 락과 비관적 락 (0) | 2024.12.30 |