LostCatBox

hanghae-6week

Word count: 790Reading time: 4 min
2025/04/26 Share

6주차 발표 요약

  • 코드 리뷰
    • 코드 리뷰 -> 공동 목표 필요, 일관된 방향, 분리된 토픽으로 커밋단위로 리뷰
  • countdownlatch
    • 동시성 테스트툴
    • 사용법(excute and finally-> countDown() 필요)

6주차 발제

  • 6주차부터 대용량 트래픽부터는 커리어에 매우 도움이 많이 될것이다.
  • 5주차까지는 DB락 제어-> 낙관적락과 비관적락을 사용하는가.
    • 낙관적락은 충돌이 많이 발생하지않는경우, 빠른 피드백이 필요한 경우, 실패해도 되는경우
      • retry 관련 로직은 application에서 비즈니스 로직에 존재
    • 비관적락은 충돌이 많이 발생하는경우, 반드시 성공해야하는 경우
      • DB 에서 대기 및 실행, 성능적으로는 확실히 비관적락이 빠름.
  • 만약 DB가 여러개

이번주 목표

  • DB 트랜잭션 이상의 범위, 분산 환경에서 Lock을 적용할수있는 방법 학습
    (DB가 여러개라던지 MSA구조라던지)
  • 분산락 : DB에 의존하지않는 원자성 보장, 논리적 트랜잭션 구현

REDIS를 사용한 분산락

  • 한번 락 획득후 실행

    • try lock -> SETNX-> key값 세팅, 기존 값 존재시 false
    • unlock -> DEL -> key값에 대한 값 지움
  • 이를 활용해,

    • simple lock

      • 락 획득 여부 = redis 에서 key 로 확인
        if (락 획득) {
            try {
              로직 실행
            } finally {
              lock 제거
            }
        } else {
          throw Lock 획득 실패 예외
        }
        
        1
        2
        3
        4
        5
        6

        - 단점: retry가 없음

        - spin lock

        -
        재시도 횟수 = 0 while (true) { 락 획득 여부 = redis 에서 key 로 확인 // SETNX key "1" if (락 획득) { try { 로직 실행 } finally { lock 제거 } break; } else { 재시도 횟수 ++ if (재시도 횟수 == 최대 횟수) throw Lock 획득 실패 예외 시간 지연 ( 대기 ) } }
        1
        2
        3
        4
        5
        6

        - 단점: while true기때문에 불필요한 io많이생김

        - pub/sub 등의 방식으로 redis를 활용

        -
        락 획득 여부 = redis 에서 lock 데이터 에 대한 subscribe 요청 및 획득 시 값 반환 // 특정 시간 동안 Lock 에 대해 구독 if (락 획득) { 로직 실행 } else { // 정해진 시간 내에 Lock 을 획득하지 못한 경우 throw Lock 획득 실패 예외 }
        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

        - redis가 Lock 획득에 대한 대기열 제공해줌

        - 레디스의 락에 대해서는 반드시 하위 양식을 따르자

        - 락획득 -> 트랜잭션 시작-> 비즈니스 로직(조회 등등) -> 트랜잭션 종료 -> 락해제



        ## redis는 구현

        - aop로 분산락을 구현해볼것이다.
        - 레디스의 락에 대해서는 반드시 하위 양식을 따르자
        - 락획득 -> 트랜잭션 시작-> 비즈니스 로직(조회 등등) -> 트랜잭션 종료 -> 락해제



        ## kafka messaging

        - 메시지 큐와 같이 순서 보장이 가능한 장치를 이용해 동시성 이슈 해결가능(순서 대로 처리됨으로 경쟁하지않으니까)
        - 반드시 토픽의 key가 같아야지만 동일한 patition로 들어가므로 순서보장됨



        ## redis에대해서 다시 생각해볼것
        Redis 등을 활용한 외부 Resource 를 통해 불필요한 DB Connection 까지 차단 가능하다. 하지만 관리주체가 DB + Redis 와 같이 늘어남에 따라 다양한 문제 파생으로 이어진다. 또한 Lock 의 관리주체가 다운되면 서비스 전체의 Down 으로 이어질 수 있는 문제가 있다.

하지만 Redis 의 높은 원자성을 활용해 프로세스 처리단위에 대한 동일한 Lock 을
여러 인스턴스에 대해 적용할 수 있으므로 매우 효과적일 수 있고, DB 의 Conection 이나
오래 걸리는 I/O 에 대한 접근 자체를 차단할 수 있으므로 DB 에 가해지는 직접적인 부하를
원천 차단할 수 있으므로 효과적이다.

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

- 현업에서는 master-slave 2개로 클러스터 구성하는 것이 바람직함.
- 실무에서는 메모리 부족 등등으로 인해, 갑자기 redis가 꺼질 수 있음

- master-slave 클러스터 반드시 개발해보기



### redis 분산락 예시에서 문제점 찾아보기

- 문제점이 있는 분산락 예시

```java
@Transactional
public void charge(Long userId, BigDecimal point) {
RLock lock = redissonClient.getLock("userChargeLock")

try {
if(lock.tryLock() == true) {
User user = userRepository.findById(userId)
user.charge(point)
} else {
throw new LockAccruedFailedException();
}
} finally {
lock.unlock();
}
}

@Transactional
public void pay(Long userId, BigDecimal point) {
RLock lock = redissonClient.getLock("userPayLock")

try {
if(lock.tryLock() == true) {
User user = userRepository.findById(userId)
user.pay(point)
} else {
throw new LockAccruedFailedException();
}
} finally {
lock.unlock();
}
}
  • 문제점

    • 트랜잭션과 락의 순차보장이 실패해요.
    • 충전과 결제가 동시에 수행 가능해요.(getLock({lock-name})) 다름

caching

  • 적은 부하로 API 응답을 빠르게 처리하기 위해 캐싱을 사용

  • 데이터를 임시로 복사해두는 Storage 계층

  • Server Caching 전략

    • application level 메모리 캐시
    • 애플리케이션의 메모리에 데이터를 저장해두고 같은 요청에 대해 데이터를 빠르게 접근해 반환함으로서 API 성능 향상 달성

caching Strategy

  • 캐싱 전략이 중요함.
    • expiration(사용자 입장에서는 지연발생)(TTL 세팅)
      • 캐시 데이터의 유통기한을 두는 방법
      • Lifetime 이 지난 캐시 데이터의 경우, 삭제시키고 새로운 데이터를 사용가능하게 함
    • Eviction(정책에 따라 명시적으로 캐시지우고, 채움 -> 사용자 입장에서는 지연거의없음)
      • 캐시 메모리 확보를 위해 캐시 데이터를 삭제
      • 명시적으로 캐시를 삭제시키는 기능
      • 특정 데이터가 Stale 해진 경우 ( 상한 경우 ) 기존 캐시를 삭제할 때도 사용

6주차 발제 추가정보

  • 애플리케이션 메모리 캐시와 Redis 캐시, 어떤 걸 언제 선택해야 하나요?
    • 강의 다시 보기
    • testContainer를 통해 redis를 따로 띄워서 test진행하기
  • 쿠버네틱스 docker안에서 redis캐시를 두는것을 추천함.
    • aws등등을 사용하게되면 네트워크 비용이큼. 정책도 한정됨, 자유도낮음
  • 외부시스템(redis) 를 적용하고 테스트를 진행할 때, 주의해야할 것들이 혹시 있을까요?
  • 레디스 카프카 사용하는이유?
    • Valkey 벌키도

https://zoom.us/rec/play/8mDY1MGFyCCqFPTQQ1po0wvqoGhOum2p_tslBuigGzC0qtk2Uksg1twEQVPABh-UwYf8w_Rjx_0G-X7S.W12YRIV2TCP0fQun?accessLevel=meeting&canPlayFromShare=true&from=share_recording_detail&continueMode=true&componentName=rec-play&originRequestUrl=https%3A%2F%2Fzoom.us%2Frec%2Fshare%2FHWlMYOMUsM_lpk5RHuXUMDGvjATCba9NLMgLSvoSUNxR3OrFNDHk4EoqmfUAB9ES.KB9p1cY43kX8w3Xt

CATALOG
  1. 1. 6주차 발표 요약
  2. 2. 6주차 발제
    1. 2.1. 이번주 목표
    2. 2.2. REDIS를 사용한 분산락
  3. 3. caching
  4. 4. caching Strategy
  5. 5. 6주차 발제 추가정보