[Study] DDIA: Chapter 1: 신뢰할 수 있고 확장 가능하며 유지보수 하기 쉬운 어플리케이션

이형걸·2025년 2월 12일
0

Study

목록 보기
1/5
post-thumbnail

전체 내용 정리

데이터 중심 애플리케이션을 생각하는 기본적인 방법 몇가지를 설명한다. → 3가지 신뢰성, 확장성, 유지보수성

오늘날의 많은 애플리케이션은 계산 중심(compute-intensive) 이 아닌 데이터 중심적(data-intensive)이다.

  • 데이터의 양, 데이터의 복잡도, 데이터의 변화 속도

데이터 중심적인 애플리케이션을 충족하는 여러가지 요구사항 중 신뢰성, 확장성, 유지보수성을 알아보자.

먼저 데이터 시스템에 대해 생각해보자.

데이터베이스, 큐, 캐시 등

왜 모두 데이터 시스템이라고 묶는가?

  1. 새로운 도구들이 많이 나오고 있고, 다양한 사용 사례에 최적화되어 있기 때문에 더이상 전통적 분류가 맞지 않는다. 분류간 경계가 흐려지고 있다.
  2. 많은 애플리케이션 단일 도구로는 더 이상 데이터 처리와 저장 모두를 만족시킬 수 없는 과도한 요구사항을 갖고 있다.
    • 작업(work)을 task 로 나누고 그것을 단일 도구로 배분하고 그 단일 도구들을 애플리케이션 코드로 연결한다.
    • ex) 하나의 서버(Spring Boot)에서 Redis(보통 캐시)도 사용하고, Kafka(메시지 큐)도 사용하고, MySQL(데이터베이스)도 사용한다.

기본적으로 좀 더 작은 범용 구성요소들로 새롭고 특수한 목적의 데이터 시스템을 만든다.

이제부터 개발자는 애플리케이션 개발자 뿐만이 아닌 데이터 시스템 설계자이기도 하다.

데이터 시스템이나 서비스를 설계할 때 까다로운 문제가 많이 생긴다.

여기서는 대부분의 소프트웨어 시스템에서 중요하게 여기는 신뢰성, 확장성, 유지보수성 의 3가지 관심사에 중점을 둔다.

신뢰성: Reliability

결함이 발생해도 시스템이 올바르게 동작한다는 의미

하드웨어 결함, 소프트웨어 오류, 인적 오류

  • 결함은 하드웨어와 소프트웨어 버그와 사람에게 있을 수 있다.
  • 내결함성 기술은 최종 사용자에게 특정 유형의 결함을 숨길 수 있게 해준다.

확장성: Scalability

부하가 증가해도 좋은 성능을 유지하기 위한 전략

부하 기술하기, 성능 기술하기, 부하 대응 접근 방식

  • 부하: 트위터의 홈 타임라인, 성능 측정 방법: 응답 시간 백분위로 예를 들어 설명했다.
  • 확장 가능한 시스템에서는 부하가 높은 상태에서 신뢰성을 유지하기 위해 처리 용량을 추가할 수 있다.

유지보수성: Maintainablilty

시스템에서 작업하는 엔지니어와 운영 팀의 삶을 개선하는 데 있다.

운용성: 운영의 편리함 만들기, 단순성: 복잡도 관리, 발전성: 변화를 쉽게 만들기

  • 좋은 추상화는 복잡도를 줄이고 쉽게 시스템을ㄹ 변경할 수 있게 하며 새로운 사용 사례에 적용하는 데 도움이 된다.
  • 좋은 운용성이란 시스템의 건강 상태를 잘 관찰할 수 있고 시스템을 효율적으로 관리하는 방법을 보유한다는 의미이다.

New) 새롭게 알게 된 점

결함(fault) 과 장애(failure) 의 차이점

결함(fault): 잘못될 수 있는 일, 사양에서 벗어난 시스템의 한 구성 오소

장애(failure): 사용자에게 필요한 서비스를 제공하지 못하고 시스템 전체가 멈춘 경우

따라서, 대개 결함으로 인해 장애가 발생하지 않게끔 내결함성 구조를 설계하는 것이 가장 좋다.

Netflix 의 카오스 몽키(Chaos Monkey)

고의적으로 결함을 유도함으로써 내결함성 시스템을 지속적으로 훈련하고 테스트해서 결함이 자연적으로 발생했을 때 올바르게 처리할 수 있다는 자신감을 높이는 방법

부하 매개변수(load parameter) (확장성)

확장성을 논의하기 위해선 무엇보다 현재 시스템의 현재 부하를 간결하게 기술해야 한다.

그래야 부하 성장 질문(부하가 2배가 되면 어떻게 될까?)를 논의할 수 있다.

  • 웹 서버의 초당 요청 수, 데이터베이스의 읽기 대 쓰기 비율, 대화방의 동시 활성 사용자(active user), 캐시 적중률 등
  • 트위터의 예시) 사용자당 팔로워의 분포가 팬 아웃 분포를 결정하기 때문에 핵심 부하 매개변수가 된다.

응답시간(response time)과 지연시간(latency)

  • 응답 시간(response time): 클라이언트가 요청을 보내고 응답을 받는 사이의 시간(네트워크 지연, 큐 지연 포함)
  • 지연 시간(latency): 요청이 처리되길 기다리는 시간, 서비스를 기다리며 휴지(latent) 상태인 시간

비슷한 것 같지만 동일하진 않다.

선두 차단(head-of-line blocking)

서버는 병렬의 소수 작업만 처리할 수 있기 때문에 (ex: cpu 코어 수 제한) 소수의 느린 요청 처리만으로도 후속 요청 처리가 지체된다.

응답 시간 계산법

  • 응답 시간은 단일 숫자가 아니라 측정 가능한 값의 분포로 생각해야 한다.
  • 사용자가 보통 얼마나 오랫동안 기다려야 하는지 알고 싶다면 중앙값이 좋은 지표다.
    • 중앙값은 단일 요청을 참고한다.
    • 사용자가 여러 개의 요청을 보내면 최소한 하나의 요청이 중앙값보다 느릴 확률이 50%보다 훨씬 높다.
  • 일반적으로 상위 백분위에서 사용하는 백분위는 95분위, 99분위, 99.9분위이다.
    • 요청의 95%, 99%, 99.9%가 특정 기준치보다 더 빠르면 해당 특정 기준치가 각 백분위의 응답 시간 기준치가 된다.

상위 백분위 응답시간(꼬리 지연 시간(tail latency))

  • 꼬리 지연 시간(tail latency)는 서비스의 사용자 경험에 직접 영향을 주기 때문에 중요하다.
  • 99.9분위 같은 최상위 백분위는 통제할 수 없는 임의 이벤트에 쉽게 영향을 받기 때문에 응답 시간을 줄이기가 매우 어렵다.

실전 백분위

  • 병렬로 호출하더라도 병렬 호출 중 가장 느린 호출이 완료되길 기다려야 하기 때문에 그 하나로 인해 전체 최종 사용자의 요청이 느리게 될 수 있다.
  • 서비스의 모니터링 대시보드에 응답 시간 백분위를 추가하려면 지속적으로 백분위를 효율적으로 계산할 필요가 있다.
    • 방법 1. 단순한 구현으로 시간 구간 내 모든 요청의 응답 시간 목록을 유지하고 1분마다 목록을 정렬
    • 방법 2. 상황에 따라 포워드 디케이(forward decay), T 다이제스트(t-digest), Hdr히스토그램(hdrHistogram) 같은 CPU와 메모리 비용을 최소로 하면서 좋은 백분위 근사치를 계산할 수 있는 알고리즘 사용
  • 응답 시간 데이터를 집계하는 올바른 방법은 히스토그램을 추가하는 것이다.

Difficulty) 어려웠거나 이해하지 못한 부분

팬 아웃(fan-out)

전자공학에서 빌려온 용어, 다른 게이트의 출력에 배속된 논리 게이트 입력의 수를 뜻한다고 한다.

트랜잭션 처리 시스템에서 하나의 수신 요청을 처리하는데 필요한 다른 서비스 요청의 수를 설명하기 위해 사용

→ 여기서는 용어를 더 이해하기 어려웠던 것 같다. 좀 더 원론적으로 교과서처럼 설명해주는 듯 하다.

→ 가상 면접 사례로 배우는 대규모 시스템 설계 기초 1권의 11장 뉴스 피드 시스템 설계 에서는 팬 아웃(fan-out)을 단순히 포스팅 전송하는 것. 즉 어떤 사용자의 새 포스팅을 그 사용자의 친구 관계에 있는 모든 사용자에게 전달하는 과정이라고 설명한다.

  • 쓰기 시점에 팬 아웃(fanout-on-write) 모델 (push 모델이라고도 함)
    • 포스팅이 완료되면 바로 해당 사용자의 캐시에 해당 포스팅을 기록
    • 친구가 많은 사용자의 경우(피드 갱신에 많은 시간 소요, 핫키(hotkey) 문제), 서비스를 자주 이용하지 않는 사용자의 경우(컴퓨팅 자원 낭비)에 문제가 된다.
  • 읽기 시점에 팬 아웃(fanout-on-read) 모델 (pull 모델이라고도 함)
    • 피드를 읽어야 하는 시점에 뉴스 피드를 갱신. 요청 기반(on-demand) 모델
    • 뉴스 피드를 읽는데 많은 시간이 소요될 수 있다.
  • 대부분의 사용자에 대해서는 push 모델 사용. 친구나 팔로워가 아주 많은 사용자의 경우 pull 모델 사용.

따라서, 본 책의 트위터 사례의 경우, 개별 사용자의 홈 타임라인 캐시를 유지하고 팔로워 각자의 홈 타임라인 캐시에 새로운 트윗을 삽입한다 라고 했으므로 push 모델을 설명하는 듯 하다.


Amendment) 추가 내용

비 프로덕션 샌드박스(Sandbox)

라이브 환경에 영향을 미치지않고 소프트웨어나 애플리케이션을 테스트하고 개발할 수 있는 제어된 환경, 실제 데이터를 사용해 안전하게 살펴보고 실험할 수 있지만 실제 사용자에게는 영향이 없는 환경

→ git 에서의 branch 분리 전략?(Develop, QA, Stage, Production) 중에 Stage 에 해당되는 것 아닌가?

→ 실제로 이전 회사에서 거의 똑같은 개념으로 해당 Stage 서버를 운용함.

우발적 복잡도(accidental complexity)를 제거하기 위한 최상의 도구는 추상화다. (유지보수성)

우발적 복잡도. 이름 그대로 의도하지 않은 복잡성을 뜻한다.

이 말을 듣고 java 의 JDBC, JPA, Spring Data JPA 가 생각났다.

초기에 애플리케이션 코드에서 java 로 MySQL 과 같은 DB 를 연결하기 위해서는 수많은 반복되고 복잡한 코드들이 필요했다.

하지만, 이를 문제 여긴 실력 있는 개발자분들이 우발적 복잡도를 제거하기 위해 좋은 추상화(interface 등)인 JDBC, JPA, Spring Data JPA 를 개발한 것이다.

책의 예시에서는 ‘기계 언어, CPU 레지스터, 시스템 호출을 추상화(숨긴) 시킨 것이 고수준 프로그래밍 언어이다’ 라고 했다.

즉 우리 개발자들이 흔히 사용하는 java 언어도 사실 우발적 복잡도를 제거한 추상화인 것이다.

public class Main {
    public static void main(String[] args) {
        // 두 정수의 덧셈
        int a = 10;
        int b = 20;
        int sum = a + b;
        
        // 결과 출력 (실제 내부적으로는 시스템 호출을 통해 콘솔에 출력됨)
        System.out.println("Sum: " + sum);
    }
}

위 코드를 보면,

  • 변수 선언이나 연산은 CPU의 레지스터나 메모리의 특정 주소에 데이터를 저장하고 처리하는 작업을 필요로 하지만, Java에서는 이러한 세부 사항을 전혀 신경쓰지 않습니다.
  • System.out.println() 메서드를 호출하면, 내부적으로 JVM이 해당 호출을 운영체제의 표준 출력 장치에 연결된 시스템 호출로 변환합니다.

따라서 이러한 애플리케이션 코드에서도 interface 를 통해서 추상화를 적절히 이용하고, 시스템 간에서도 interface 화를 통해 우발적 복잡도를 줄여나가야 할 것이다.

profile
현명하고 성실하게 살자

0개의 댓글