From 8f038931218367ccf434ee1034a7833813448cbf Mon Sep 17 00:00:00 2001 From: catnap421 Date: Fri, 15 Nov 2024 23:21:38 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feature:=201=EC=A3=BC=EC=B0=A8=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../\354\261\225\355\204\2601.md" | 59 ++++++++++++ .../\354\261\225\355\204\2602.md" | 94 +++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 "2024-designing-data-intensive-applications/1\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2601.md" create mode 100644 "2024-designing-data-intensive-applications/1\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2602.md" diff --git "a/2024-designing-data-intensive-applications/1\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2601.md" "b/2024-designing-data-intensive-applications/1\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2601.md" new file mode 100644 index 0000000..d99cf34 --- /dev/null +++ "b/2024-designing-data-intensive-applications/1\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2601.md" @@ -0,0 +1,59 @@ +# 챕터1 + +이 책에서 가장 중요하게 바라볼 관심사 세 가지 +- 신뢰성 +- 확장성 +- 유지보수성 + + +### 1. 신뢰성 +- 올바르게 동작함 +- 결함: 사양에서 벗어난 시스템의 한 구성요소 +- 장애: 사용자에게 필요한 서비스를 제공하지 못하고 시스템이 멈춘 경우 + +#### 하드웨어 결함 +- 시스템 장애율을 줄이기 위한 방법 + - 중복을 추가 + +#### 소프트웨어 오류 +- 하드웨어 결함은 서로간의 상관관계가 약함. +- 소프트웨어 오류는 예상하기가 어렵고 노드 간 상관관계가 존재함. + +#### 인적 오류 + +- `사람이 가장 많이 실수하는 장소에서 사람의 실수로 장애가 발생할 수 있는 부분을 분리하라.` + - 이게 가능한가? + +### 2. 확장성 +> [!note] +> 확장성을 논한다는 것은 "시스템이 특정 방식으로 커지면 이에 대처하기 위한 선택은 무엇인가?"와 "추가 부하를 다루기 위해 계산 자원을 어떻게 투입할까?" 같은 질문을 고려한다는 의미다. + + +##### 부하 기술하기 +부하를 기술하기 위해선 부하 매개변수를 잘 설정하는 게 중요함. +초당 요청 수, 데이터베이스의 읽기 대 쓰기 비율, 대화방의 동시 활성 사용자, 캐시 적중률 등이 될 수 잇음. 이러한 부하 매개변수의 크기와 종류에 따라 적절한 전략을 취하는 게 중요. + + +트위터 일화는 12장에서 다시 설명 예정 + + +#### 부하 대응 접근 방식 +- 수직 확장 +- 수평 확장 + +### 3. 유지보수성 +- 운용성 : 운영팀이 시스템을 원활게 운영할 수 있게 하라 +- 단순성: 단순해야한다. +- 발전성(변경 용이성 - 내가 지음): 변경이 쉬워야 한다. + +#### 운용성 +- 모니터링 제공 +- 예측 가능하게 동작하고 예기치 않은 상황을 최소화 +- 문서화 잘하기 + +#### 단순성: 복잡도 관리 +복잡도를 낮추는 최고의 방법은 `추상화` + +#### 발전성 +애자일하게 작업하기 위해 TDD, 리팩토링 등을 사용할 수 있음 + diff --git "a/2024-designing-data-intensive-applications/1\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2602.md" "b/2024-designing-data-intensive-applications/1\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2602.md" new file mode 100644 index 0000000..61772e7 --- /dev/null +++ "b/2024-designing-data-intensive-applications/1\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2602.md" @@ -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를 파악하는 경우가 있음. + + +[참고 공유]() \ No newline at end of file From 879dca1f3fa80625e3d3c499266dc49c63c66485 Mon Sep 17 00:00:00 2001 From: catnap421 Date: Sat, 30 Nov 2024 10:15:18 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feature:=203=EC=A3=BC=EC=B0=A8=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../\354\261\225\355\204\2603.md" | 56 ++++++++++++ .../\354\261\225\355\204\2604.md" | 86 +++++++++++++++++++ .../\354\261\225\355\204\2605.md" | 0 3 files changed, 142 insertions(+) create mode 100644 "2024-designing-data-intensive-applications/3\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2603.md" create mode 100644 "2024-designing-data-intensive-applications/3\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2604.md" create mode 100644 "2024-designing-data-intensive-applications/3\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2605.md" diff --git "a/2024-designing-data-intensive-applications/3\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2603.md" "b/2024-designing-data-intensive-applications/3\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2603.md" new file mode 100644 index 0000000..b43d705 --- /dev/null +++ "b/2024-designing-data-intensive-applications/3\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2603.md" @@ -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 참고 + + diff --git "a/2024-designing-data-intensive-applications/3\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2604.md" "b/2024-designing-data-intensive-applications/3\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2604.md" new file mode 100644 index 0000000..79c6598 --- /dev/null +++ "b/2024-designing-data-intensive-applications/3\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2604.md" @@ -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/) \ No newline at end of file diff --git "a/2024-designing-data-intensive-applications/3\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2605.md" "b/2024-designing-data-intensive-applications/3\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2605.md" new file mode 100644 index 0000000..e69de29 From eaed045d05fe80087b6ee15b2b9c9ce7795058ab Mon Sep 17 00:00:00 2001 From: catnap421 Date: Sat, 21 Dec 2024 10:28:36 +0900 Subject: [PATCH 3/3] =?UTF-8?q?feature:=205=EC=A3=BC=EC=B0=A8=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../\354\261\225\355\204\2605.md" | 95 +++++++++++++++++++ .../\354\261\225\355\204\2606.md" | 39 ++++++++ .../\354\261\225\355\204\2607.md" | 42 ++++++++ 3 files changed, 176 insertions(+) create mode 100644 "2024-designing-data-intensive-applications/5\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2606.md" create mode 100644 "2024-designing-data-intensive-applications/5\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2607.md" diff --git "a/2024-designing-data-intensive-applications/3\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2605.md" "b/2024-designing-data-intensive-applications/3\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2605.md" index e69de29..72aa773 100644 --- "a/2024-designing-data-intensive-applications/3\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2605.md" +++ "b/2024-designing-data-intensive-applications/3\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2605.md" @@ -0,0 +1,95 @@ +# 복제 + +- ! 복제의 모든 어려움은 복제된 데이터의 **변경** 처리에 있으며... + +복제에 있어 가장 인기있는 알고리즘 세 가지 +- 단일 리더 +- 다중 리더 +- 리더없는 + +#### 리더와 팔로워 + +리더, 팔로워라는 개념이 존재함 (master - slave) + +동기식 복제 +- 팔로워의 복제가 리더의 쓰기와 하나의 트랜잭션으로 묶여있어서, 최신 데이터 복사본을 지니고 있는 게 보장됨. +- 하지만 팔로워가 장애가 난 경우, 모든 쓰기 작업이 실패 + +반동기식 복제 +- 두 개의 팔로워가 존재하는데, 하나는 동기식, 하나는 비동기식으로 처리함. +- 동기식 팔로워가 죽을 경우, 비동기식 팔로워를 동기식으로 변경시킴 + - ? 어떻게 처리할까? +- 두 노드(리더와 하나의 동기 팔로워) 중 하나에는 최신 복사본이 있는 것을 보장함. + - ? 이거 당연한거 아닌가? 그리고 리더에는 복사본이 아니라 원본 데이터가 있는 거 아니야? + +비동기식 복제 +- 리더 기반 복제는 완전 비동기식으로 구성함 +- 리더가 잘못되면, 팔로워에 아직 복제되지 않은 쓰기들도 유실될 수 있음 + - ? 이렇게 됐을 때 리더가 괜찮아지면 복제되지 않은 데이터들은 어떻게 처리할까? +- 모든 팔로워가 잘못되더라도 리더가 쓰기 처리를 계속 할 수 있는 장점 + +#### 새로운 팔로워 설정 +복제 서버 수를 늘리거나 장애 노드의 대체를 위해 새로운 팔로워 설정이 필요함 +- ! 새로운 팔로워가 리더의 복제본을 정확히 가지고 있는 지 보장이 필요 + +복제 과정 +1. 리더의 스냅숏을 가져옴 +2. 새로운 팔로워 노드에 스냅숏을 복사함 +3. 리더에 연결해 스냅숏 이후에 발생한 로그들에 대해 요청함. 이 때 스냅숏이 정확한 위치를 알고있어야 하는데, postgresql에서는 로그 일련번호, mysql에서는 이진로그 좌표라 함 +4. 팔로워가 스냅숏 이후의 데이터(백로그)를 모두 처리했을 때, 따라잡았다고 표현 + +#### 노드 중단 처리 +장애로 인해 중단될 수도 있지만, 유지보수를 위해 중단될 수도 있음. 리더 기반 복제에서 고가용성 달성을 위해서 중단된 노드에 대한 대처가 필요함. + +##### 팔로워 장애: 따라잡기 복구 + + +##### 복제로그 구현 +- 구문 기반 복제 + - 간단하게 구현가능 + - insert, update, delete 문을 그대로 복제 + - 실상은 복제가 깨질 수 있는 다양한 사례가 있음 + - now() rand() 같은 비결정적 함수를 호출하는 경우 + - where 절이 있는 경우, 정확히 같은 순서로 실행되어야 하는데, 동시에 여러 트랜잭션이 수행되는 것을 제한함 + - 부수효과를 가진 구문(트리거, 스토어드 프로시저, 사용자 정의함수)이 결정적이지 않은 경우 +- 쓰기 전 로그 배송 + - 실제 DB에 쓰여지기 전 별도의 로그파일로 기록하는 것 + - 로그가 저수준으로 쓰여있어서, 데이터베이스의 물리적구조에 강하게 의존함 + - `페이지 1234의 오프셋 5678에 바이트 시퀀스 추가` 와 같이 작성됨. + - 데이터의 논리적 의미 파악이 어렵고, 유연성이 떨어짐. + - ! 복제프로토콜 허용의 문제점 + - 복제 로직의 복잡성이 증가함. 서로 다른 버전에서 고려되어야 하는 게 당연히 생김 + - 장애 발생 시 롤백이 어려워질 수 있음 +- 논리적(로우 기반) 로그 복제 + - Mysql에서 사용하는 방식으로, WAL 방식보다 고수준으로 작성되었고, 저장소 엔진 내부를 알 필요가 없기 때문에 하위호환성을 쉽게 유지할 수 있음 + - 애플리케이션에서 파싱하기가 더 쉽고, 이 덕분에 CDC를 활용하기에 좋음 +- 트리거 기반 복제 + - 이전엔 데이터베이스 자체적으로 구현되었다면, 이건 애플리케이션 코드를 사용함. + - 오버헤드가 크지만, 유연성을 잡기 위해 사용함. + +##### 복제 지연 문제 +- @ **쓰기 후 읽기 일관성** 보장이 필요함. +- @ 사용자가 시간이 거꾸로 흐르는 현상을 발견할 수 있다. + - 어떤 팔로워에서는 싱크가 돼서 코멘트가 보이다가, 두번째 질의는 싱크가 덜 된 팔로워로 요청이 가서 갑자기 코멘트가 사라짐 같은 케이스 +- @ 인과성의 위반 우려 + - 추후 파티셔닝에서 자세하게 설명함 + +**해결책** +- 자신이 쓴 내용 읽기 + - usecase를 분석해서 리더에서 읽는 경우를 따로 빼기 + - 마지막 갱신 시각을 찾아서 마지막 갱신후 1분 동안은 리더에서 모든 읽기를 수행하도록 하기 + - 이 방식은 따로 마지막 갱신 시각을 어딘가에 기록할 필요가 있음 + - 로컬 캐시를 달아서, 어떤 엔티티의 id에 대해서는 마지막 갱신이 언제인지 판단할 수도 있긴 하겠다. + - 클라에서 질의를 대기시키게 하기 +- 단조 읽기 + - 강한 일관성보다는 덜한 보장, 최종적 일관성보다는 더 강한 보장 방식 + - 각 사용자의 읽기가 항상 동일한 복제 서버에서 수행되게끔 하는 것 + - 이게 단순히 데이터베이스만 따질 게 아니라 경우에 따라 애플리케이션 서버까지 고려해야할 수도 있을 듯 + + +#### 다중 리더복제 +- ! 협업 편집 문제 +- 쓰기 충돌 문제 + +#### 리더없는 복제 +- Cassandra(카산드라) 가 이렇다고 함. 추후에 사용하게 되면 이 점을 잘알고 사용해야 할 듯 diff --git "a/2024-designing-data-intensive-applications/5\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2606.md" "b/2024-designing-data-intensive-applications/5\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2606.md" new file mode 100644 index 0000000..ea0f663 --- /dev/null +++ "b/2024-designing-data-intensive-applications/5\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2606.md" @@ -0,0 +1,39 @@ +# 파티셔닝 + +### 파티셔닝과 복제 +- 보통 각 파티션의 복사본을 여러 노드에 저장함 +- 이를 통해 **내결함성 확보** + +- $ 파티셔닝의 목적은 데이터 질의 부하를 노드 사이에 고르게 분배하는 것 +- @ skewed: 고르게 분배되지 않았을 경우 +- @ 핫스팟: 불균형하게 부하가 높은 파티션 + +### 키 - 값 데이터 파티셔닝 +- 키 범위 기준 + - 범위 스캔에 장점 + - 특정한 접근 패턴에 따라 핫스팟 유발 +- 키의 해시값 기준 + - 대부분 사용하는 패턴 + - 범위 스캔을 할수 없음 + +### 파티션 재균형화 +재균형화 중에도 기대하기를 원하는 최소 요구사항이 있음 +- 재균형화 후, 분하가 노드들 사이에 균등하게 분배되어야 한다. +- 재균형화 도중에도 데이터 베이스는 읽기 쓰기 요청을 처리할 수 있어야 한다. +- 재균형화가 빨리 실행되고 네트워크와 디스크 I/O 부하를 최소화할 수 있도록 노드들 사이에 데이터가 필요 이상으로 옮겨져서는 안된다. + +##### 재균형화 전략 +- 파티션 개수 고정 + - 파티션을 노드 대수보다 많이 만들고 각 노드에 여러 파티션을 할당하는 방식 + - 노드를 늘리게 되면, 각 노드들에서 파티션을 차출해서 옮기는 방식 + - 파티션 개수를 적절하게 해야하며, 파티션 크기 또한 적절하게 설정해야 한다. + - ! 키 범위 파티셔닝을 사용하는 경우엔 모든 데이터가 한 파티션에 저장될 수 있다. +- 동적 파티셔닝 + - 파티션의 크기가 설정된 값을 넘어서면 파티션을 두 개로 쪼개 각각에 원래 파티션의 절반 정도의 데이터가 포함되게 하는 방식 + - 파티션 개수가 전체 데이터 용량에 맞춰 조정됨 + - ! 데이터가 없을 때 즉, 시작할 때는 파티션이 하나라는 단점이 있음. 그래서 요청이 하나의 노드에서만 실행될 수 있음. +- 노드 비례 파티셔닝 + - 하나의 노드에 고정된 개수의 파티션을 할당하도록 하는 방식 + - 데이터 용량이 커지면서 파티션의 크기가 커질 수 있지만, 노드를 추가해 부하를 분산시킬 수 있음. + - 개별 파티션 크기가 상당히 안정적으로 유지된다고 함. +- diff --git "a/2024-designing-data-intensive-applications/5\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2607.md" "b/2024-designing-data-intensive-applications/5\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2607.md" new file mode 100644 index 0000000..838f342 --- /dev/null +++ "b/2024-designing-data-intensive-applications/5\354\243\274\354\260\250/\354\241\260\355\230\204\354\232\260/\354\261\225\355\204\2607.md" @@ -0,0 +1,42 @@ + +트랜잭션은 애플리케이션에서 몇 개의 읽기와 쓰기를 하나의 논리적 단위로 묶는 방법 + +#### ACID의 의미 +- 원자성(Atomicity) +- 일관성(Consistency) +- 격리성(Isolation) +- 지속성(Durability) + +###### 원자성 +여러 쓰기 묶음을 하나의 단위로 처리하도록 할 수 있는 능력 +어보트 능력이라고도 생각할 수 있음 + +###### 일관성 +일관성이란 단어는 다양한 의미로 사용되고 있으며, 실제 데이터베이스의 일관성을 설명하는 건 불변식을 의미하는데, 이는 데이터베이스의 능력이 아닌 애플리케이션의 능력이다. +일관성은 ACID에 속하지 않는다. + +###### 격리성 +동시성 문제와 연관있는 속성 +트랜잭션은 다른 트랜잭션을 방해할 수 없다. + +- @ 직렬성이라고도 표현하는데, 이에 대한 정의는 아래와 같다. + - 데이터베이스는 실제로는 여러 트랝개션이 동시에 실행됐다하더라도 트랜잭션이 커밋됐을 때의 결과가 트랜잭션이 **순차적으로** 실행됐을 때의 결과와 동일하도록 보장한다. + - ! 하지만 직렬성 격리는 성능 손해를 보기 때문에 실제로는 사용X + +###### 지속성 +트랜잭션이 성공적으로 커밋됐다면 하드웨어 결함이 발생하거나 데이터베이스가 죽더라도 트랜잭션에서 기록한 모든 데이터는 손실되지 않는다는 보장 +지속성을 보장하려면 데이터베이스는 트랜잭션이 성공적으로 커밋됐다고 보고하기 전에 **쓰기나 복제가 완료될 때까지 기다려야 한다.** +- ! 그렇기 때문에 완벽한 지속성 보장은 없다. + +#### 오류와 어보트 처리 +트랜잭션의 핵심 기능은 오류가 생기면 어보트되고 안전하게 재시도할 수 있다는 것 +하지만 모든 시스템이 이러한 철학을 따르지는 않음. +리더없는 복제에서는 "최선을 다하는" 원칙을 기반으로 훨씬 더 많은 일을 수행함. + +#### 완화된 격리 수준 +##### 커밋 후 읽기 (READ COMMITTED) + +가장 기본적인 수준의 트랜잭션 격리로 이 수준에서는 두 가지를 보장해 준다. + +데이터베이스에서 읽을 때 커밋된 데이터만 보게 된다(더티 읽기가 없음) +데이터베이스에 쓸 때 커밋된 데이터만 덮어쓰게 된다(더티 쓰기가 없음)