책너두 (데이터 중심 애플리케이션 설계) 40일차 (~464p)

요약

  • 데이터베이스와 스트림의 나머지 부분에 대해 이해함.
    • 이벤트 소싱
      • 이벤트 로그에서 현재 상태 파생하기
      • 명령과 이벤트
    • 상태와 스트림 그리고 불변성
      • 불변 이벤트의 장점
      • 동일한 이벤트 로그로 여러 가지 뷰 만들기
      • 동시성 제어
      • 불변성의 한계
  • 스트림 처리에 대해 이해함.
    • 스트림 처리의 사용
      • 복잡한 이벤트 처리
      • 스트림 분석
      • 구체화 뷰 유지하기
      • 스트림 상에서 검색하기
      • 메시지 전달과 RPC

메모

이벤트 소싱

  • 이벤트 소싱은 도메인 주도 설계(DDD) 커뮤니티에서 개발한 기법으로, 애플리케이션 상태 변화를 변경 이벤트 로그로 저장함.
    • 이벤트 소싱과 변경 데이터 캡처는 비슷하지만, 추상화 레벨에서 차이가 있음.
  • 변경 데이터 캡처는 데이터베이스에서 저수준으로 변경 로그를 추출하며, 애플리케이션은 데이터베이스를 변경 가능한 방식으로 사용함.
    • 이벤트 소싱에서는 애플리케이션 로직이 이벤트 로그에 기록된 불변 이벤트를 기반으로 구축되며, 이벤트 저장은 추가만 가능함.
  • 이벤트 소싱은 데이터 모델링에 강력한 기법이며, 애플리케이션을 지속적으로 개선하는데 유리함.
    • 이벤트 소싱을 사용하면 상황 파악이 쉽고 디버깅에 도움이 되며 애플리케이션 버그를 방지함.
    • ex) "어떤 학생이 강의 신청을 취소했다"는 이벤트를 저장할 때 이벤트 소싱 접근법을 사용하면 새로 발생한 부수 효과를 기존 이벤트에서 쉽게 분리할 수 있음.
  • 이벤트 소싱은 연대기 데이터 모델과 유사하며, 이벤트 로그와 별 모양 스키마에서 발견한 사실 테이블 사이에도 유사점이 있음.
    • 이벤트 스토어 같은 특화된 데이터베이스는 이벤트 소싱을 지원하지만, 일반 데이터베이스나 로그 기반 메시지 브로커도 이런 방식으로 애플리케이션을 구축하는 데 사용할 수 있음.

이벤트 로그에서 현재 상태 파생하기

  • 이벤트 로그에서 현재 상태를 파생하기 위해, 이벤트 소싱을 사용하는 애플리케이션은 이벤트 로그를 사용자에게 보여주기에 적당한 애플리케이션 상태로 변환해야 함.
    • 이 변환 과정은 결정적이어야 함.
    • 변경 데이터 캡처와 마찬가지로 이벤트 로그를 재현하면 현재 시스템 상태를 재구성할 수 있음.
    • 그러나 로그 컴팩션은 다르게 처리해야 함.
  • 레코드 갱신용 CDC 이벤트는 레코드의 가장 새로운 버전을 보유하고, 기본키의 현재 값은 전적으로 기본키의 가장 최신 이벤트로 결정됨.
    • 이벤트 소싱에서는 이벤트를 상위 수준에서 모델링하며, 이벤트는 사용자 행동의도를 표현함.
    • 이 경우 뒤에 발생한 이벤트가 앞선 이벤트를 덮어쓰지 않으므로, 마지막 상태를 재구축하기 위해서는 이벤트의 전체 히스토리가 필요함.
  • 이벤트 소싱 애플리케이션은 일반적으로 이벤트 로그에서 파생된 현재 상태의 스냅숏 저장 메커니즘이 있어 전체 로그를 반복해서 재처리할 필요가 없음.
    • 이 메커니즘은 최적화에 불과하며, 이벤트 소싱 시스템은 모든 원시 이벤트를 영원히 저장하고 필요할 때마다 모든 이벤트를 재처리할 수 있어야 한다는 의도가 있음.

명령과 이벤트

  • 명령과 이벤트를 구분하는 것이 이벤트 소싱 철학의 핵심임.
    • 사용자 요청이 처음 도착했을 때, 이 요청은 명령임.
    • 명령은 실패할 수도 있는데, 특정 무결성 조건을 위반하면 실패함.
    • 애플리케이션은 먼저 명령이 실행 가능한지 확인하고, 무결성이 검증되면 명령은 지속성 있는 불변 이벤트가 됨.
  • ex) 사용자가 특정 사용자명으로 등록하거나 좌석을 예매하려 한다면, 애플리케이션은 사용자명이 이미 사용 중이거나 좌석이 이미 예약된 것인지 확인해야 함.
    • 확인이 성공하면, 이벤트가 생성됨.
    • 이벤트는 생성 시점에 사실(fact)이 되며, 사용자가 예약을 변경하거나 취소하더라도 이전 사실은 여전히 진실임.
  • 이벤트 스트림 소비자는 이벤트를 거절할 수 없음.
    • 따라서, 명령의 유효성은 이벤트가 되기 전에 동기식으로 검증해야 함.
    • 원자적으로 명령을 검증하고 이벤트를 발행하는 방법으로 직렬성 트랜잭션을 사용할 수 있음.
    • 또는, 좌석 예약 사용자 요청을 두 개의 이벤트로 분할하여 비동기 처리로 유효성 검사를 수행할 수 있음.

상태와 스트림 그리고 불변성

  • 상태와 스트림 그리고 불변성은 데이터 처리에서 중요한 개념임.
    • 일괄 처리에서 입력 파일의 불변성은 실험적 처리 작업을 수행하는 데 이점을 줌.
    • 불변성 원리는 이벤트 소싱과 변경 데이터 캡처를 강력하게 만듦.
  • 보통 데이터베이스는 애플리케이션의 현재 상태를 저장한다고 생각함.
    • 데이터베이스는 읽기에 최적화되어 있고 질의 처리에 편리함.
    • 상태의 본질은 변하는 것이며, 데이터베이스는 데이터 삽입, 갱신, 삭제를 지원한다. 이를 어떻게 불변성과 연결할 수 있을까?
  • 상태가 변할 때마다 해당 상태는 시간이 흐름에 따라 변한 이벤트의 마지막 결과임.
    • 상태를 바꾼 이벤트가 항상 존재함.
    • 변경 가능 상태와 추가 전용 불변 이벤트 로그는 동전의 양면처럼 서로 모순적임.
    • 모든 변경 로그는 시간이 지남에 따라 바뀌는 상태를 나타냄.
  • 수학적으로, 애플리케이션 상태는 시간에 따른 이벤트 스트림을 적분해서 구할 수 있고, 변경 스트림은 시간으로 상태를 미분해서 구할 수 있음.
    • 이런 비유는 제한적이지만, 데이터에 관한 생각의 출발점으로 충분히 유용함.
  • 변경 로그를 지속성 있게 저장하면 상태를 간단히 재생성할 수 있다.
    • 데이터 흐름을 추론하기 쉽게 하려면, 이벤트 로그를 레코드 시스템으로 생각하고 모든 변경 가능 상태를 이벤트 로그로부터 파생된 것으로 생각하면 됨.
  • 트랜잭션 로그는 데이터베이스에 적용된 모든 변경 사항을 기록함.
    • 로그는 고속으로 덧붙여지며, 덧붙이기가 로그를 변경하는 유일한 방법임.
    • 이런 측면에서 데이터베이스의 내용은 로그의 최근 레코드 값을 캐시하는 것임.
    • 로그 컴팩션은 로그와 데이터베이스 상태 사이의 차이를 메우는 한 가지 방법임.

불변 이벤트의 장점

  • 불변 이벤트의 장점은 오랜 역사를 가지고 있다.
    • 회계사들은 수세기 동안 불변성을 이용해 금융 부기를 관리했음.
    • 거래가 발생하면 원장에 추가만 하는 방식으로 기록하며, 원장은 이벤트 로그로 볼 수 있음.
  • 회계사들은 실수가 발생하더라도 원장의 거래 내역을 지우거나 고치지 않음.
    • 대신 실수를 보완하는 거래 내역을 추가함.
    • 이러한 방식은 감사 기능에서 중요하며, 다른 시스템에서도 유용함.
  • 불변 이벤트 로그를 사용하면 문제 상황의 진단과 복구가 훨씬 쉬움.
    • 또한 불변 이벤트는 현재 상태보다 훨씬 많은 정보를 포함함.
    • ex) 쇼핑 웹사이트에서 고객이 장바구니에 항목을 추가하고 제거하는 경우, 이 정보는 이벤트 로그에 기록되지만 데이터베이스에서는 잃어버리는 정보임.
  • 이처럼 불변 이벤트는 감사, 진단, 복구, 그리고 추가적인 정보 제공 등 여러 가지 장점을 가지고 있음.

동일한 이벤트 로그로 여러 가지 뷰 만들기

  • 동일한 이벤트 로그로 여러 가지 뷰 만들기의 핵심 개념은 불변 이벤트 로그에서 가변 상태를 분리함으로써 다양한 읽기 전용 뷰를 만들 수 있다는 것임.
    • 이 방식은 한 스트림이 여러 소비자를 가질 때와 동일한 방식으로 작동함.
  • 이벤트 로그를 사용하면 기존 데이터를 새로운 방식으로 표현하는 새 기능을 추가할 때, 신규 기능용으로 분리한 읽기 최적화된 뷰를 구축할 수 있음.
    • 이로 인해 기존 시스템을 수정할 필요가 없고, 기존 시스템과 함께 운용 가능함.
  • 데이터를 쓰는 형식과 읽는 형식을 분리함으로써 다양한 읽기 뷰를 허용하면 상당한 유연성을 얻을 수 있음.
    • 이 개념을 명령과 질의 책임의 분리(command query responsibility segregation, CQRS)라고 부른다.
  • 정규화와 비정규화에 관한 논쟁은 의미가 거의 없어짐.
    • 읽기 최적화된 뷰는 데이터를 비정규화하는 것이 전적으로 합리적임.
    • 변환 프로세스가 뷰와 이벤트 로그 사이의 일관성을 유지하는 메커니즘을 제공하기 때문임.
  • 예로 들어, 트위터의 홈 타임라인은 읽기 최적화된 상태의 한 예임.
    • 홈 타임라인은 상당히 비정규화되어 있지만, 팬 아웃 서비스가 중복된 상태를 동기화하고 관리 가능한 상태로 유지함.

동시성 제어

  • 동시성 제어의 주요 단점은 이벤트 로그의 소비가 대개 비동기로 이뤄진다는 점임.
    • 사용자가 로그에 이벤트를 기록하고 이어서 로그에서 파생된 뷰를 읽어도 기록한 이벤트가 아직 읽기 뷰에 반영되지 않았을 가능성이 있음.
  • 해결책 중 하나는 읽기 뷰의 갱신과 로그에 이벤트를 추가하는 작업을 동기식으로 수행하는 방법임.
    • 이 방법을 쓰려면 이벤트 로그와 읽기 뷰를 같은 저장 시스템에 담아야 하며, 다른 시스템에 있다면 분산 트랜잭션이 필요함.
  • 이벤트 로그로 현재 상태를 만들면 동시성 제어 측면이 단순해짐.
    • 이벤트 소싱을 사용하면 사용자 동작에 대한 설명을 포함하는 이벤트를 설계할 수 있음.
    • 사용자 동작은 한 장소에서 한 번 쓰기만 필요함.
  • 이벤트 로그와 애플리케이션 상태를 같은 방식으로 파티셔닝하면, 간단한 단일 스레드로그 소비자는 쓰기용 동시성 제어가 필요하지 않음.
    • 단지 단일 이벤트를 한 번에 하나씩 처리함.
    • 파티션 내에서 이벤트의 직렬 순서를 정의하면 로그에서 동시성의 비결정성을 제거할 수 있음.
    • 한 이벤트가 여러 개의 상태 파티션에 영향을 준다면 더 많은 작업이 필요함.

불변성의 한계

  • 불변성에 의존하는 시스템은 많지만, 불변성의 한계도 존재함.
    • 다양한 데이터베이스와 버전 관리 시스템이 불변 데이터를 사용하지만, 영구적으로 모든 변화의 불변 히스토리를 유지하는 것은 어려울 수 있음.
    • 상대적으로 작은 데이터셋에서 매우 빈번히 갱신과 삭제를 하는 경우, 불변 히스토리가 감당하기 힘들 정도로 커지거나 파편화 문제가 발생할 수 있음.
    • 컴팩션과 가비지 컬렉션의 성능 문제도 고려해야 함.
  • 데이터가 모두 불변성임에도 관리상의 이유로 데이터를 삭제할 필요가 있는 상황이 있음.
    • 이런 경우, 이전 데이터를 삭제해야 한다는 또 다른 이벤트를 로그에 추가한다고 해결되지 않음.
    • 데이토믹(Datomic)에서는 이 기능을 적출(exicision)이라 부르고, 포씰 버전 관리 시스템에는 셔닝(shunning)이라는 비슷한 개념이 있음.
  • 데이터를 진짜로 삭제하는 작업은 많은 곳에 복제본이 남아 있기 때문에 어려움.
    • 저장소 엔진, 파일 시스템, SSD는 같은 장소에 데이터를 덮어 쓰기보다 주로 새로운 장소에 기록함.
    • 백업은 의도적으로 불변으로 만들어 사고로 인한 삭제나 손상을 방지함.
    • 그러므로 삭제는 데이터를 찾기 불가능하게 하는 문제가 아니라 찾기 어렵게 하는 문제임.
    • 그럼에도 때로는 법률과 자기 규제의 이유로 행동해야 할 때가 있음.

스트림 처리

  • 스트림 처리에 대해 설명하는 이 글에서는 크게 세 가지 선택지가 있음.
    1. 이벤트에서 데이터를 꺼내 데이터베이스나 캐시 검색 색인 또는 유사한 저장소 시스템에 기록하고 다른 클라이언트가 이 시스템에 해당 데이터를 질의함.
      • 저장소 시스템에 기록하는 것은 일괄 처리 워크플로의 출력과 동일한 스트리밍임.
    2. 이벤트를 사용자에 직접 보냄.
      • 예를 들어 이메일 경고나 푸시 알림을 전송하거나 이벤트를 시각화하는 실시간 대시보드에 이벤트를 스트리밍함.
      • 이 경우 사람이 스트림의 최종 소비자임.
    3. 하나 이상의 입력 스트림을 처리해 하나 이상의 출력 스트림을 생산함.
      • 스트림은 최종 출력에 이르기까지 여러 처리단계로 구성된 파이프라인을 통과할 수도 있음.
  • 이 글의 나머지 부분에서는 3번 선택지에 대해 설명함.
    • 스트림 처리자를 연산자(operator)나 작업(job)이라 부름.
      • 이것은 유닉스 프로세스, 맵리듀스 작업과 밀접한 관련이 있음.
      • 스트림 처리자는 읽기 전용 방식으로 입력 스트림을 소비해 추가 전용 방식으로 다른 곳에 출력을 씀.
  • 스트림 처리자를 파티셔닝하고 병렬화하는 양식은 맵리듀스, 데이터플로 엔진의 양식과 상당히 유사함.
    • 변환, 필터링과 같은 기본 매핑 연산도 동일하게 작동함.
  • 스트림 처리와 일괄 처리 작업의 가장 큰 차이는 스트림은 끝나지 않는다는 점임.
    • 이 차이로 인해 정렬 병합 조인을 사용할 수 없음.
    • 내결함성 메커니즘도 변경이 필요함.
    • 몇 년 동안 실행 중인 스트림 작업은 장애 발생 이후 처음부터 재시작하는 것은 비현실적임.

스트림 처리의 사용

  • 스트림 처리는 다양한 상황에서 사용되며, 주로 모니터링 목적으로 활용되어 왔다. 몇 가지 예시는 다음과 같다
    • 사기 감시 시스템은 신용카드 사용 패턴이 기대치를 벗어나는지 확인하여 도난으로 의심되면 결제를 막는다.
    • 거래 시스템은 금융시장의 가격 변화를 감지하여 특정 규칙에 따라 거래를 실행함.
    • 제조 시스템은 공장의 기계 상태를 모니터링하다 오작동을 발견하면 문제를 빨리 규명함.
    • 군사 첩보 시스템은 잠재적 침략자의 활동을 추적해 공격 신호가 있으면 경보를 발령함.
  • 이러한 애플리케이션들은 복잡한 패턴 매칭상관 관계 규명이 필요함.
    • 시간이 지남에 따라 다양한 용도로 스트림 처리를 활용하는 사용자들이 나타나기 시작했다. 이 글에서는 이러한 애플리케이션들을 간략히 비교하고 대조함.

복잡한 이벤트 처리

  • 복잡한 이벤트 처리(complex event processing, CEP)는 1990년대에 이벤트 스트림 분석을 위해 개발된 방법으로, 특정 이벤트 패턴을 검색하는 애플리케이션에 적합함.
    • CEP는 정규 표현식과 유사한 방식으로 스트림에서 특정 이벤트 패턴을 찾는 규칙을 설정할 수 있음.
  • CEP 시스템은 이벤트 패턴을 설명하는데 SQL 같은 고수준 선언형 질의 언어나 그래픽 사용자 인터페이스를 사용할 때가 많음.
    • 처리 엔진은 입력 스트림을 소비하면서 필요한 매칭을 수행하는 상태 기계를 내부적으로 유지함.
    • 매치를 발견하면 엔진은 복잡한 이벤트(complex event)를 방출함.
  • CEP 엔진은 데이터베이스와 비교했을 때 역할이 반대로 바뀜.
    • 데이터베이스는 데이터를 영구적으로 저장하고 질의를 일시적으로 처리함.
    • 반면, CEP 엔진은 질의를 오랜 기간 저장하고 지속적으로 들어오는 이벤트가 질의를 통과하며 이벤트 패턴에 매칭되는 질의를 찾음.
  • 복잡한 이벤트 처리 구현에는 에스퍼(Esper), IBM 인포스피어 스트림(IBM InfoSphere Streams), 아파마(Apama), 팁코 스트림베이스(TIBCO StreamBase), SQL스트림(SQLstream) 등이 있음.
    • 분산 스트림 처리자인 쌈자(Samza)는 스트림에 선언형 질의를 하는 SQL도 지원함.

스트림 분석

  • 스트림 분석은 스트림 처리의 다른 영역으로, CEP와의 경계는 불분명하나 일반적으로 분석은 연속한 특정 이벤트 패턴을 찾는 것보다 대량의 이벤트를 집계하고 통계적 지표를 뽑는 것에 초점을 맞춤.
    • 이러한 분석의 예로는 이벤트 빈도 측정, 이동 평균 계산, 이전 시간간격과 현재 통계값의 비교 등이 있음.
  • 이런 통계는 고정된 시간 간격 기준으로 계산하며, 집계 시간 간격을 윈도우(window)라 함.
    • 스트림 분석 시스템은 확률적 알고리즘을 사용할 때가 있으며, 이는 근사 결과를 제공하지만 메모리 사용량이 적음.
  • 스트림 분석에 사용되는 다양한 분산 스트림 처리 오픈 소스 프레임워크로는 아파치 스톰(Apache Storm), 스파크 스트리밍(Spark Streaming), 플링크(Flink), 콩코드(Concord), 쌈자(Samza), 카프카 스트림(Kafka Streams) 등이 있음.
    • 호스팅 서비스로는 구글 클라우드 데이터플로(Google Cloud Dataflow)와 애저 스트림 분석(Azure Stream Analytics)이 있음.

구체화 뷰 유지하기

  • 구체화 뷰 유지하기는 데이터베이스 변경에 대한 스트림을 사용해 캐시, 검색 색인 데이터 웨어하우스 등의 파생 데이터 시스템이 원본 데이터베이스의 최신 내용을 따라잡게 할 수 있음.
    • 이러한 접근법은 다른 뷰를 만들어 효율적으로 질의할 수 있게 하고 기반 데이터가 변경될 때마다 뷰를 갱신함.
  • 이벤트 소싱에서 애플리케이션 상태는 이벤트 로그를 적용함으로써 유지되며, 구체화 뷰로 볼 수 있음.
    • 스트림 분석 시나리오와 달리, 일반적으로 어떤 시간 윈도우 내의 이벤트만 고려하기에는 충분치 않음.
    • 구체화 뷰를 만들기 위해서는 잠재적으로 임의의 시간 범위에 발생한 모든 이벤트가 필요함.
  • 제한된 기간의 윈도우에서 동작하는 일부 분석 지향 프레임워크의 가정과 이벤트를 영원히 유지해야 할 필요성은 서로 상반되지만, 이론적으로는 어떤 스트림 처리자라도 구체화 뷰를 유지하는 데 사용할 수 있음.
    • 쌈자(Samza)와 카프카 스트림(Kafka Streams)은 카프카의 로그 컴팩션 지원을 기반으로 구체화 뷰 유지 용도로 사용할 수 있음.

스트림 상에서 검색하기

  • 스트림 상에서 검색하기는 복수 이벤트로 구성된 패턴을 찾는 CEP 외에도 전문 검색 질의와 같은 복잡한 기준을 기반으로 개별 이벤트를 검색해야 하는 경우가 있음.
  • ex) 미디어 모니터링 서비스는 미디어 아웃렛에서 새 기사와 방송 피드를 구독하고 관심 있는 회사나 상품 또는 주제를 언급하는 뉴스를 검색함.
    • 사전에 검색 질의를 설정하면 꾸준히 스트림으로 들어오는 뉴스 항목을 이 질의에 매칭함.
    • 이런 기능이 있는 웹사이트도 있으며, 엘라스틱서치의 여과(percolator) 기능은 이런 류의 스트림 검색을 구현하는 데 사용 가능한 선택지 중 하나임.
  • 전통적인 검색 엔진과 달리 스트림 검색은 질의를 먼저 저장하고 문서를 질의를 지나가면서 실행함.
    • 가장 간단한 사례로 모든 질의에 대해 모든 문서를 테스트할 수 있음.
    • 이 과정을 최적화하기 위해 문서뿐만 아니라 질의도 색인할 수 있음.
    • 그러면 매칭될지도 모르는 질의 집합을 줄일 수 있음.

메시지 전달과 RPC

  • 메시지 전달과 RPC는 서비스 간 통신 메커니즘으로 사용할 수 있으며, 이러한 시스템은 메시지와 이벤트에 기반을 두지만 일반적으로 스트림 처리자로 생각하지 않음.
    • 액터 프레임워크와 스트림 처리의 차이점은 다음과 같음
      • 액터 프레임워크는 주로 동시성을 관리하고 통신 모듈을 분산 실행하는 메커니즘임.
        • 반면 스트림 처리는 기본적으로 데이터 관리 기법임.
      • 액터 간 통신은 주로 단기적이고 일대일임.
        • 반면 이벤트 로그는 지속성이 있고 다중 구독이 가능함.
      • 액터는 임의의 방식으로 통신할 수 있음.
        • 그러나 스트림 처리자는 대개 비순환 파이프라인에 설정됨.
  • 유사 RPC 시스템과 스트림 처리 사이에 겹치는 영역이 있음.
    • ex) 아파치 스톰에는 분산 RPC(distributed RPC) 기능이 있어 이벤트 스트림을 처리하는 노드 집합에 질의를 맡길 수 있음.
    • 또한, 액터 프레임워크를 이용한 스트림 처리도 가능함.
    • 그러나 액터 프레임워크는 대부분 장애 상황에서 메시지 전달을 보장하지 않기 때문에 처리에 내결함성을 보장하지 못함.

댓글

Designed by JB FACTORY