Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

데이터 중심 애플리케이션 설계 1주차, 3주차, 5주차 정리 #135

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# 챕터1

이 책에서 가장 중요하게 바라볼 관심사 세 가지
- 신뢰성
- 확장성
- 유지보수성


### 1. 신뢰성
- 올바르게 동작함
- 결함: 사양에서 벗어난 시스템의 한 구성요소
- 장애: 사용자에게 필요한 서비스를 제공하지 못하고 시스템이 멈춘 경우

#### 하드웨어 결함
- 시스템 장애율을 줄이기 위한 방법
- 중복을 추가

#### 소프트웨어 오류
- 하드웨어 결함은 서로간의 상관관계가 약함.
- 소프트웨어 오류는 예상하기가 어렵고 노드 간 상관관계가 존재함.

#### 인적 오류

- `사람이 가장 많이 실수하는 장소에서 사람의 실수로 장애가 발생할 수 있는 부분을 분리하라.`
- 이게 가능한가?

### 2. 확장성
> [!note]
> 확장성을 논한다는 것은 "시스템이 특정 방식으로 커지면 이에 대처하기 위한 선택은 무엇인가?"와 "추가 부하를 다루기 위해 계산 자원을 어떻게 투입할까?" 같은 질문을 고려한다는 의미다.


##### 부하 기술하기
부하를 기술하기 위해선 부하 매개변수를 잘 설정하는 게 중요함.
초당 요청 수, 데이터베이스의 읽기 대 쓰기 비율, 대화방의 동시 활성 사용자, 캐시 적중률 등이 될 수 잇음. 이러한 부하 매개변수의 크기와 종류에 따라 적절한 전략을 취하는 게 중요.


트위터 일화는 12장에서 다시 설명 예정


#### 부하 대응 접근 방식
- 수직 확장
- 수평 확장

### 3. 유지보수성
- 운용성 : 운영팀이 시스템을 원활게 운영할 수 있게 하라
- 단순성: 단순해야한다.
- 발전성(변경 용이성 - 내가 지음): 변경이 쉬워야 한다.

#### 운용성
- 모니터링 제공
- 예측 가능하게 동작하고 예기치 않은 상황을 최소화
- 문서화 잘하기

#### 단순성: 복잡도 관리
복잡도를 낮추는 최고의 방법은 `추상화`

#### 발전성
애자일하게 작업하기 위해 TDD, 리팩토링 등을 사용할 수 있음

Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# 챕터2 데이터 모델과 질의 언어

> [!note]
> 내 언어의 한계는 내 세계의 한계를 의미한다.
> - 루트비히 비트겐슈타인, 논리-철학 논고 -


데이터 모델은 소프트웨어가 어떻게 작성되었는지 뿐만 아니라 `문제를 어떻게 생각해야 하는지`에 대해서도 영향을 미친다.

## 데이터 모델
- 관계명 모델
- 문서 모델
- 그래프 기반 데이터 모델


관계형 데이터 모델의 목표
- 비즈니스 데이터 처리
- 트랜잭션 처리
- 일괄 처리

문서 모델의 목표
- 뛰어난 확장성
- 관계형 보다 폭넓은 특수 질의 가능
- 관계형 스키마의 제한 및 동적인 표현력을 위함

객체 관계형 불일치
- JSON으로 표현되는 데이터와 실제 데이터 모델간의 불일치로 발생하는 불편함이 존재
- 특히 관계형 모델에서 이게 두드러지며, 문서 모델에서는 이게 크지 않은 편.
- (데이터 부호화 형식으로 JSON이 가지는 문제도 존재하는 데 이건 4장에서 설명)

다대일과 다대다 관계
- 참조를 통해서 저장하는게 해당 값의 변화에 유연하게 대응가능함
- 관계형 모델에서는 특히나 이렇게 참조값을 가지는 게 잘 표현되는데 반해, 문서 모델에서는 조인에 대한 지원이 약함.
- 혹은 애플리케이션 레벨에서 조인을 완성해야함.
- 허나 [이러한 케이스](https://medium.com/musinsa-tech/%EB%AC%B4%EC%8B%A0%EC%82%AC-%EC%84%B1%EC%9E%A5%EA%B3%BC-%ED%95%A8%EA%BB%98-%EA%B1%B0%EB%8C%80%ED%95%B4%EC%A0%B8%EC%98%A8-600%EC%A4%84%EC%A7%9C%EB%A6%AC-%EC%BF%A0%ED%8F%B0-%EC%BF%BC%EB%A6%AC%EC%99%80%EC%9D%98-%EC%95%84%EB%A6%84%EB%8B%A4%EC%9A%B4-%EC%9D%B4%EB%B3%84-e689d7d932b5)처럼 요새는 DB에 복잡한 쿼리를 하기보단, 애플리케이션에서 계산을 하도록 하는 게 유행이라, 이게 큰 단점처럼 다가오진 않는 듯함.

문서 모델에서의 스키마 유연성
- Schema on Write <-> Schema on Read = 정적타입과 동적타입
- 모든 레코드가 다른 구조라면 스키마리스, 같은 구조라서 예상 가능하다면 스키마가 유리하다.


데이터 지역성
> [!note]- 스패너에 대한 설명
> Spanner는 Google에서 개발한 분산형 글로벌 데이터베이스 시스템으로, 높은 가용성과 일관성을 유지하면서 확장성을 제공합니다. Spanner의 독특한 기능 중 하나는 SQL을 지원하면서도 전 세계적으로 분산되어 운영되는 데이터베이스라는 점인데요, 이를 위해 특별한 데이터 모델과 스키마 설계 방법을 제공합니다.
> 당신이 언급한 "스패너 부모 테이블 내에 테이블의 로우를 교차 배치"는 Spanner의 지역성 특성 및 데이터 배치 전략을 설명하는 것입니다. 이를 이해하기 위해서는 Spanner의 몇 가지 핵심 개념을 알아야 합니다.
>
> 1. **테이블 간의 지역성 힌트**: Spanner에서는 부모-자식 관계(외래 키 관계)가 있는 테이블 간의 데이터가 물리적으로 함께 저장될 수 있도록 지역성 힌트를 제공합니다. 이를 통해 관련 데이터가 동일한 위치나 노드에 함께 저장되므로, 쿼리의 효율성을 높이고 지연 시간을 줄일 수 있습니다.
>
> 2. **Interleaved Table (교차 배치 테이블)**: Spanner에서 자식 테이블의 행을 부모 테이블의 행과 교차(interleaved)하여 저장할 수 있습니다. 이는 특정 부모 행에 연관된 자식 행들이 같은 물리적 위치에 저장되어 접근 성능이 향상되는 방식입니다. 데이터를 이러한 방식으로 배치하면, 데이터 액세스 시 필요한 디스크 I/O가 감소하고 네트워크 시간이 줄어드는 장점이 있습니다.
>
> 3. **스키마 정의**: 이러한 교차 배치는 스키마 정의 시 명시적으로 지정됩니다. 예를 들어, 자식 테이블을 정의할 때 "INTERLEAVE IN PARENT" 구문을 사용하여 특정 부모 테이블과의 관계를 나타냅니다.
>
> 이러한 기능으로 인해 Spanner는 대규모 분산 환경에서도 높은 성능을 유지하면서 트랜잭션 일관성을 보장하고, 데이터의 물리적 배치를 최적화하여 관련 데이터에 대한 접근을 빠르게 수행할 수 있습니다.


## 데이터를 위한 질의 언어

- 선언형
- 알고자 하는 데이터의 패턴, 즉 결과의 조건과 데이터 반환 형태만 제공하면 됨.
- 명령형
- 특정 순서로 특정 연산을 수행하게끔 컴퓨터에 지시

### 그래프형 데이터 모델


복잡한 다대다 관계를 표현하는 데 있어 좋음. 특히 노드 간의 weight를 줌으로써 단순한 연결 관계 뿐만이 아니라 관계의 정도를 표현도 가능하다.

#### 속성 그래프

각 정점은 다음과 같은 구성요소로 구성됨
- 고유한 식별자
- 유출 간선 집합
- 유입 간선 집합
- 속성 컬렉션(키-값 쌍)

각 간선은 다음과 같은 요소로 구성됨
- 고유한 식별자
- 간선이 시작하는 정점(꼬리 정점)
- 간선이 끝나는 정점(머리 정점)
- 두 정정 간 관계 유형을 설명하는 레이블
- 속성 컬렉션(키-값 쌍)

다른 유형의 관계에 서로 다른 레이블을 사용하면 단일 그래프에 다른 유형의 정보를 저장하면서도 데이터 모델을 깔끔하게 유지 가능

#### SQL의 그래프 질의
SQL에서도 그래프 데이터를 표현할 수 있는데, 그렇다면 SQL을 사용해도 그래프 질의할 수 있을까?
정답은 '예'지만 약간 어렵다.

SQL에서 가변 순회 경로에 대한 질의 개념은 `재귀 공통 테이블 식(with recursive 문)`을 사용해서 표현가능하다.
> [!note]- 나의 경우엔
> POI라는 서비스를 만들고 있는데, 많은 Tree 구조를 가짐. 그렇다보니 recursive문을 사용해서 최상위 root를 파악하는 경우가 있음.


[참고 공유](<https://dnb.s3.ap-northeast-2.amazonaws.com/2024/Amazon+Neptune%EC%9D%84+%ED%99%9C%EC%9A%A9%ED%95%9C+%EB%8F%99%EC%9D%BC+%EC%82%AC%EC%9A%A9%EC%9E%90+%EC%B6%94%EC%A0%95%EC%8B%9C%EC%8A%A4%ED%85%9C+%EA%B5%AC%EC%B6%95(%EC%A1%B0%EC%9E%AC%ED%98%B8-%EB%8B%B9%EA%B7%BC%EB%A7%88%EC%BC%93).pdf>)
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# 저장소와 검색

로그 : (append-only) 연속된 추가 전용 레코드

색인은 읽기 성능을 높여주는 대신, 쓰기 성능에 대해 트레이드오프를 지닌다.

#### 해시 색인
키를 데이터 파일의 바이트 오프셋에 매핑해 인메모리 해시 맵을 유지하는 전략
- ? 그림 3-1에서 바이트오프셋은 결국 키에 해당하는 부분이 시작하는 오프셋을 저장하는건데, 어떻게 키와 밸류를 구분할까?
- $ 아하! 콤마를 통해 키 밸류를 구분한다고 치자. 그러면 특정 키의 시작부분에 대해서 알고 있으면, 다른 바이트오프셋이 없는 영역까지 읽어들이거나 해서 밸류를 파악할 수 있지!

각 키의 값이 자주 갱신되는 상황에 적합

하지만 추가만 한다면 결국 디스크 공간은 부족해지기 마련
- ! 그래서 특정 크기의 세그먼트로 로그를 나누는 방식이 좋은 해결책
- ! 또한 세그먼트 파일들에 대해 컴팩션(중복된 키를 버리고 각 키의 최신 갱신 값만 유지하는 것)을 시도하는 것도 가능

**추가만 하는 로그 방식 vs 새로운 값으로 덮어쓰는 방식**
- 추가와 세그먼트 병합은 순차적인 쓰기 작업이기 때문에 무작위 쓰기보다 훨씬 빠르다
- 세그먼트 파일이 추가 전용이나 불변이면 동시성과 고장 복구는 훨씬 간단하다.
- 오래된 세그먼트 병합은 시간이 지남에 따라 조각화되는 데이터 파일 문제를 피할 수 있다.

- @ 내가 생각했을 때, 이벤트 소싱과 같은 것을 적용한다고 했을 때 항상 추가하는 방식은 그 용량?에 대한 걱정을 했었던 것 같다. 그거에 대해서 컴팩션을 활용해보는 것도 방법일 듯하다.

##### 해시 색인의 제약사항
- 해시 테이블은 메모리에 저장해야 하므로 키가 너무 많으면 문제가 된다.
- 해시 테이블은 범위 질의에 효율적이지 않다.

### SS테이블과 LSM 트리
해시테이블에서 한가지 요구사항을 추가하기
- 키 값 쌍을 키로 정렬해보기
- 이렇게 키로 정렬된 형식을 **정렬된 문자열 테이블(Sorted String Table) = SS테이블**이라고 함

##### 장점
- SS 테이블은 병합정렬 알고리즘과 유사한 방식으로 컴팩션을 수행하여, 컴팩션할때도 정렬된 형식의 병합 세그먼트 파일을 만들어낸다.
- 특정 키를 찾기 위해 코든 키의 색인을 유지할 필요가 없다. 키 간의 오프셋을 비교하여 그 사이에 있음을 유추할 수 있을테니까.

LSM(Log-Structured Merge-Tree) 트리의 기본 개념
- SS테이블에서 세그먼트를 병합하고 정렬하는 알고리즘 수행 방법
- 백그라운드에서 연쇄적으로 SS테이블을 지속적으로 병합하는 것
- 블룸필터를 활용해 해당 키가 데이터베이스에 존재하지 않는 지 파악하도록 하기도 함


### B 트리
B트리는 4KB 크기의 고정 크기 **블록** 또는 **페이지**로 나누고, 한 번에 하나의 페이지에 읽기 또는 쓰기를 한다.

한 페이지는 여러 키위 하위 페이지의 참조를 포함한다.

[B 트리를 잘 설명해주는 사이트](https://planetscale.com/blog/btrees-and-database-indexes)


### LSM 트리의 장단점

p.86 ~ 87 참고


Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# 부호화와 발전


### 형식
JSON, XML, 이진부호화

부호화에 있어 조직 내에서만 사용하는 지 조직 바깥까지 사용하는 지에 대해 설명하는 게 흥미로웠음.
조직 외부에선 그래도 사람이 읽기 쉬운 형태를 쓰게 되는 편이고, 조직 내부에선 좀 더 파싱이 빠른 형식을 사용하게 되는(=성능이 뛰어난) 경향이 있는 듯함. 그 이유는 설득이 쉬워서.

이진부호화 라이브러리
- Apache Thrift
- Protocol Bufferes

##### 필드 태그와 스키마 발전

이진부호화에서는 필드를 이름으로 구별하는 게 아닌 숫자(=필드 태그)를 사용하고 있어서, 부호화할 때 데이터 해석에 중요하다. 필드 이름을 전혀 참조하고 있지 않아서, 스키마에서 이름 변경은 가능하다.

필드를 추가하기 위해선 상위 호환성과 하위 호환성을 고려하는 게 중요하다.
상위 호환성(예전 코드가 새로운 데이터를 읽는 능력)은 예전 코드가 인식할 수 없는 태그에 대해선 무시하도록 처리하고 있어서 해결 가능하다.
하위 호환성(새로운 코드가 예전 데이터를 읽는 능력)은 새로운 필드를 optional로 추가하거나, 기본 값을 갖도록 한다.

필드 삭제 방식은 역으로 접근하면 된다.

##### 데이터 타입과 스키마 발전
데이터 타입을 변경하는 건 위험하다.

#### 쓰기 스키마와 읽기 스키마
아브로는 필드 태그가 존재하지 않으며, 읽기 스키마와 쓰기 스키마가 동일하지 않더라도 호환 가능하면 된다는 아이디어에서 출발한다. 데이터를 읽을 때 아브로 라이브러리는 쓰기 스키마와 읽기 스키마를 함께 살펴본 다음 쓰기 스키마에서 읽기 스키마로 데이터를 변환해 그 차이를 해소한다.

그러면 쓰기 스키마를 어떻게 알 수 있을까? 쓰기 스키마를 매번 데이터에 같이 쓰는건 데이터 절약의 의미가 사라진다.
- 많은 레코드가 있는 대용량 파일
- 쓰기 스키마를 포함시켜서 데이터를 쓴다. 많은 레코드가 동일한 스키마로 작성된 것일테니, 쓰기 스키마를 같이 보내는 게 이득이다.
- 개별적으로 기록된 레코드를 가진 데이터베이스
- 데이터베이스에는 다양한 쓰기 스키마를 가졌을 수 있다. 이 경우 버전번호를 포함하고 데이터베이스에 스키마 버전 목록을 유지하도록 하는 것이다.
- 네트워크 연결을 통해 레코드 보내기
- 두 프로세스가 양방향 네트워크 연결을 통해 통신할 때 연결 설정에서 스키마 버전 합의를 할 수 있다. 이후 연결을 유지하는 동안 합의된 스키마를 사용한다.

#### 동적 생성 스키마
아브로는 관계형 스키마로부터 아브로 스키마를 상당히 쉽게 생성할 수 잇다. 각 데이터베이스 테이블에 맞게 레코드 스키마를 생성하고 각 칼럼은 해당 레코드의 필드가 된다. 데이터베이스의 칼럼 이름은 아브로의 필드 이름에 매핑된다.

데이터베이스 스키마가 변경되면, 갱신된 데이터베이스 스키마로부터 새로운 아브로 스키마를 생성하고 새로운 아브로 스키마로 데이터를 내보내도록 한다.

- @ 지금 brand_branch 테이블에 직접 접근해서 읽고 있는데, 그러면 lp팀에선 해당 테이블의 컬럼에 대해 쉽게 변경할 수 없게되고, poi 입장에서도 강결합되는구나. 좀 더 공부해봐야겠지만, 이런 상황을 해결할 수 있는 실마리를 얻음


#### 스키마의 장점
- 스키마 언어는 JSON 보다 더 간단하며 더 자세한 유효성 검사 규칙을 지원한다.
- 스키마 발전에 있어 스키마리스 또는 읽기 스키마 JSON 데이터베이스가 제공하는 것과 동일한 종류의 유연성을 제공하며, 도구 지원도 더 잘 보장한다.

#### 데이터플로 모드
발전성은 한 번에 모든 것을 변경할 필요 없이 시스템의 다양한 부분을 독립적으로 업그레이드해 변경 사항을 쉽게 반영하는 능력이다. 호환성은 데이터를 부호화하는 하나의 프로세스와 그것을 복호화하는 다른 프로세스 간의 관계다.

호환성을 지키면서 데이터를 전달하는 방법에 대해 알아보자.

##### 데이터베이스를 통한 데이터 플로
데이터베이스에 기록하는 프로세스는 데이터를 부호화하고 데이터베이스에서 읽는 프로세스는 데이터를 복호화한다.

데이터베이스에 데이터를 쓰는 읽은 `미래의 자신에게 메시지를 보내는 일`처럼 생각할 수도 있다.

- ! 새로운 버전의 애플리케이션이 기록한 데이터를 예전 버전의 애플리케이션이 갱신하는 경우 주의하지 않으면 데이터가 유실될 수 있다. (p.133 그림 참고 - 하나의 row 전체를 업데이트하는 경우 발생할 수 있음)



#### 서비스를 통한 데이터플로: REST와 RPC

클라이언트와 서버간의 데이터 부호화는 서비스 AAPI의 버전 간 호환이 가능해야 한다.

##### RPC의 문제

RPC모델은 원격 네트워크 서비스 요청을 같은 프로세스 안에서 특정 프로그래밍 언어의 함수나 ㅔ서드를 호출하는 것과 동일하게 사용가능하게 해준다(이런 추상화를 `위치 투명성` 이라고 한다.)

이러한 아이디어는 좋아보이지만, 근본적으로 로컬 함수호출과 네트워크 호출은 다르기 때문에, 이러한 접근법은 좋지 못하다. (네트워크 호출의 불안정성을 가정하고 설계되어 있지 않다는 이야기)

하지만 차세대 RPC 프레임워크는 이러한 사실을 분명히하고 있따. 실패할지도 모르는 작업을 위한 대비책이 설계에 녹아있다.

#### 메시지전달 데이터플로

비동기 메시지 전달 시스템, 메시지 브로커

#### 분산 액터 프레임워크

- ? 액터 모델이 무엇인지 감이 잘 안옴...

액터 모델은 단일 프로세스 안에서도 메시지가 유실될 수 있다고 이미 가정하기 때문에 위치 투명성은 RPC보다 액터 모델에서 더 잘 동작한다.

[액터 모델에 대한 설명](https://pegasuskim.wordpress.com/2015/12/23/actor-model-%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC/)
Loading