독서

[데이터 중심 애플리케이션 설계] 5장

오렌지색 귤 2023. 11. 17. 19:28
반응형

핵심 내용

 

1. 복제의 용도

고가용성

연결이 끊긴 작업 : 네트워크 중단이 있어도 애플리케이션이 계속 동작

지연 시간

확장성

 

2. 복제에 대한 주요 접근 방식

단일 리더 복제 : 클라이언트는 모든 쓰기를 단일 노드로 전송하고 리더는 데이터 변경 이벤트 스트림을 다른 복제 서버로 전송한다. 읽기는 모든 복제 서버가 수행할 수 있지만 팔로워의 읽기는 오래된 값일 수 있다

다중 리더 복제 : 클라이언트는 각 쓰기를 여러 리더 노드 중 쓰기를 받아들일 수 있는 노드로 전송한다. 리더는 데이터 변경 이벤트 스트림을 다른 리더와 모든 팔로워 노드로 전송한다

리더 없는 복제 : 클라이언트는 각 쓰기를 여러 노드로 전송한다. 클라이언트는 오래된 데이터를 감지하고 이를 바로잡기 위해 병렬로 여러 노드에서 읽는다

 

3. 복제 지연 해결 방법

쓰기 후 읽기 일관성 : 사용자는 자신이 제출한 데이터를 항상 볼 수 있어야 한다

단조 읽기 : 사용자가 어떤 시점에 데이터를 본 후에는 예전 시점의 데이터는 나중에 볼 수 없다

일관된 순서로 읽기 : 사용자는 인과성이 있는 상태의 데이터를 봐야 한다. 예를 들어 질문과 그에 대한 답을 순서에 맞게 봐야 한다

 

 

배운 내용

 

1. 동기식 복제, 비동기식 복제

리더 기반 복제에서 모든 복제가 동기식인 상황은 비현실적이므로 팔로워 하나는 동기식으로 하고 나머지는 비동기식으로 하기도 한다. 적어도 두 노드(리더와 하나의 동기 팔로워)에 데이터의 최신 복사본이 있는 것을 보장하며 이런 설정을 반동기식이라 한다.

다만, 보통 리더 기반 복제는 완전히 비동기식으로 구성한다. 쓰기가 유실될 수 있어 내구성은 약하지만, 많은 팔로워가 있거나 지리적으로 분산돼있는 경우에는 비동기식 복제를 널리 사용한다.

 

2. 장애 복구

팔로워 장애는 매우 쉽게 복구할 수 있다. 팔로워의 로컬 디스크에 보관된 로그에서 처리한 마지막 트랜잭션을 알아낸 후, 리더에 연결이 끊어진 동안 발생한 데이터 변경을 모두 요청한다.

 

리더의 장애를 처리하는 일은 까다롭다. 자동 장애 복구에서는 리더가 장애인지 판단하고 새로운 리더를 선택한 후, 새로운 리더 사용을 위해 시스템을 재설정해야 한다.

그러나 상당히 많은 오류가 발생 가능하다. 새로운 리더가 이전 리더의 쓰기를 일부 수신하지 못하는 경우에 단순히 폐기하는 방법은 내구성을 약화시키며, 특히 데이터베이스 외부의 다른 저장소 시스템과 쓰기 작업이 연관된 경우 특히 위험하다. (깃허브 사고: 레디스에 PK 저장하고 있어 불일치 문제 발생) 또한 스플릿 브레인 현상이 발생하기도 하며, 적절한 타임아웃을 판단하기도 어렵다.

이런 이유로 일부 운영팀은 수동으로 장애 복구를 수행하기도 한다.

 

3. 복제 지연 해결 방법

자신이 쓴 내용 읽기

1) 사용자 본인이 수정한 내용을 읽을 때는 팔로워가 아닌 리더에서 읽는다

2) 마지막 갱신 기록을 찾아 적절한 시간 이후에는 팔로워로부터 읽는다

3) 클라이언트에서도 가장 최근 쓰기의 타임스탬프를 기억하고, 아직 최신 내용이 아닌 경우에 대기를 할 수 있다

4) 리더가 제공해야 하는 모든 요청은 리더가 포함된 데이터센터로 라우팅돼야 한다

5) 디바이스 간 쓰기 후 읽기 일관성을 제공하려면, 마지막 갱신 타임스탬프는 중앙집중식으로 관리하고, 다양한 데이터센터 중에서도 사용자 디바이스의 요청을 동일한 데이터센터(홈 광대역, 셀룰러 데이터 네트워크 등)로 먼저 라우팅해야 한다

 

단조 읽기

1) 각 사용자의 읽기가 항상 동일한 복제 서버에서 수행되게 한다

2) 복제 서버가 고장나면 재라우팅할 필요가 있다

 

일관된 순서로 읽기 (파티셔닝된 데이테베이스에서 발생)

1) 서로 인과성이 있는 쓰기가 동일한 파티션에 기록되도록 한다

 

4. 토폴로지

원형 토폴로지, 별 모양 토폴로지, 전체 연결 토폴로지 등 CS 공부할 때 봤었던 내용들은 다중 리더 복제 시, 리더간 쓰기 작업을 공유하는 통신 경로로서 사용되고 있었다.

원형, 별 : 모든 복제 서버에 도달하기 전에 쓰기 작업이 여러 노드를 거쳐야 한다. 무한 복제 루프 방지를 위한 고유 식별자 존재. SPOF 가능성

전체 연결 : 일부 네트워크 연결이 다른 연결보다 빠르다면 일부 복제 메시지가 다른 메시지를 추월할 수 있다. 이러한 경우 타임스탬프 추가만으로는 해결이 불가능하므로, 버전 벡터라는 기법을 사용하면 된다.

 

 

찾아본 내용

 

1. 아마존의 충돌 해소 핸들러 (장바구니 충돌 해소 로직)

아마존은 수백만 명의 사용자가 동시에 서비스를 이용하는 상황에서도 데이터의 일관성을 유지해야 합니다. 사용자가 장바구니에 아이템을 추가하거나 삭제할 때, 이러한 변경 사항은 다른 사용자의 동시 변경과 충돌할 수 있습니다.


충돌 해소 로직의 핵심은 '최종 쓰기 승리'(Last Write Wins, LWW) 전략과 '상태 병합'(State Merging) 기법을 사용하는 것입니다. LWW 전략은 가장 최근의 변경을 우선시하여 충돌을 해결하지만, 이 방법만으로는 사용자의 의도와 다른 결과가 발생할 수 있습니다. 예를 들어, 한 사용자가 아이템을 장바구니에서 제거하고 거의 동시에 다른 사용자가 같은 아이템의 수량을 변경하는 경우, 단순히 가장 최근의 변경을 적용하는 것만으로는 올바른 결과를 얻을 수 없습니다.

이를 해결하기 위해 아마존은 상태 병합 기법을 사용합니다. 이 기법은 각 변경 사항을 독립적인 단위로 취급하여, 충돌이 발생했을 때 모든 관련 변경 사항을 고려하여 최종 상태를 결정합니다. 예를 들어, 사용자 A가 아이템을 삭제하고 사용자 B가 수량을 변경한 경우, 시스템은 두 변경 사항을 모두 고려하여 최종적으로 어떤 동작을 취할지 결정합니다.

 

2. 리더 데이터베이스와 달리 코디네이터 노드는 특정 순서로 쓰기를 수행하지 않는 이유

리더 데이터베이스가 엄격한 데이터 일관성과 순서를 중시하는 반면, 코디네이터 노드는 시스템의 확장성과 성능을 우선시하며 유연한 데이터 일관성 모델을 사용합니다.

 

3. 리더없는 복제에서의 느슨한 정족수

정족수(Quorum)의 정의 : 정족수는 특정 연산(쓰기나 읽기)이 성공적으로 완료되기 위해 참여해야 하는 노드의 최소 수를 의미합니다. 전통적으로, 쓰기 정족수와 읽기 정족수의 합은 전체 노드 수보다 커야 합니다.
느슨한 정족수의 개념 : 느슨한 정족수는 이러한 엄격한 조건을 완화합니다. 이는 시스템의 일부 노드가 응답하지 않거나 네트워크가 분할된 경우에도 연산을 계속 수행할 수 있도록 합니다. 즉, 느슨한 정족수는 시스템이 더 많은 장애와 네트워크 문제에 대해 견딜 수 있도록 해줍니다.
데이터 일관성과 가용성 사이의 균형 : 느슨한 정족수는 데이터의 최종 일관성(eventual consistency)을 제공합니다. 이는 모든 노드가 결국 일관된 상태에 도달하게 되지만, 일시적으로 데이터가 불일치할 수 있음을 의미합니다. 느슨한 정족수는 높은 가용성을 제공하지만, 이로 인해 일관성이 약간 저하될 수 있습니다.
응용 : 느슨한 정족수는 특히 읽기 연산이 빈번하고 쓰기 연산보다 덜 중요한 애플리케이션에서 유용합니다. 예를 들어, 소셜 미디어 피드나 블로그 게시물과 같이 최신 데이터가 즉시 필요하지 않은 경우에 적합합니다.

 

 

궁금한 내용

 

1. 다중 리더 복제에서 특정 사용자의 요청을 동일한 데이터센터로 항상 라우팅해서 충돌 회피를 하는 방법이 있는데, 비동기로 같은 리더에 쓰는 작업과 동기 방식으로 각자 다른 리더에 쓰기 작업을 수행하는 것의 차이는 무엇일까? 동기 방식으로도 커밋 단위를 짧게 가져가면 결국 유사한 결과를 낳지 않나?

 

비동기 방식으로 동일한 리더에 쓰기
데이터 일관성 : 비동기 방식에서는 쓰기 작업이 동일한 리더에 순차적으로 이루어집니다. 이 방법은 데이터의 순서를 보장하며, 일관성 유지에 유리합니다.
성능과 가용성 : 비동기 방식은 네트워크 지연이나 장애가 발생해도 쓰기 작업이 계속될 수 있도록 합니다. 이는 시스템의 가용성을 향상시키지만, 다른 리더에 대한 데이터 복제가 지연될 수 있습니다.
충돌 회피 : 동일한 리더에 쓰기 작업을 할당하면, 데이터 충돌의 가능성이 감소합니다. 이는 모든 쓰기가 같은 순서로 이루어지기 때문입니다.

동기 방식으로 서로 다른 리더들에 쓰기
데이터 일관성 : 동기 방식으로 여러 리더에 쓰기를 수행하면, 각 리더가 독립적으로 쓰기 작업을 수행합니다. 이는 데이터 일관성 관리에 있어서 추가적인 도전을 제시합니다.
성능과 가용성 : 동기 방식은 네트워크 지연이나 다른 리더의 가용성에 민감할 수 있습니다. 즉, 한 리더에 문제가 생기면 전체 시스템의 쓰기 성능에 영향을 미칠 수 있습니다.
충돌 발생 가능성 : 서로 다른 리더에 동기적으로 쓰기를 수행하면, 데이터 충돌의 가능성이 증가합니다. 각 리더가 독립적으로 쓰기를 수행하기 때문에, 병합 및 충돌 해결 메커니즘이 필요합니다.

커밋 단위와 유사한 결과에 대한 고려
동기 방식으로 쓰기 작업을 수행하며 커밋 단위를 짧게 가져간다 해도, 이는 비동기 방식과 완전히 동일한 결과를 낳지 않습니다. 커밋 단위를 줄이는 것은 충돌의 가능성을 감소시킬 수 있지만, 여전히 다른 리더에 대한 쓰기 작업이 독립적으로 이루어지므로 충돌 및 데이터 불일치 문제를 완전히 해결하지는 못합니다. 또한, 더 짧은 커밋 단위는 성능에 부담을 줄 수 있으며, 시스템의 전체적인 복잡성을 증가시킬 수 있습니다.

 

2. 최종 쓰기 승리(LWW)라 부르는 충돌 해소 알고리즘에서 카산드라는 키로 UUID를 사용해 모든 쓰기 작업에 고유한 키를 부여한다는데, 샤딩을 사용하는 MySQL에서 pk를 자동증가 컬럼이 아닌 uuid로 설정하는 것은 어떻게 생각하시나요?

 

UUID의 사용 이점
글로벌 유일성 : UUID는 전 세계적으로 유일하며, 분산 시스템이나 샤딩된 환경에서 데이터가 겹치지 않게 해줍니다.
분산 환경 적합 : 샤딩 또는 복제가 있는 분산 데이터베이스 환경에서, UUID는 충돌을 방지하고 데이터 일관성을 유지하는데 도움이 됩니다.
스케일링 용이 : 샤드나 복제본 간의 데이터 이동이나 재배치가 더 용이해집니다.

MySQL에서 UUID 사용의 단점
성능 문제 : UUID는 전통적인 숫자 기반의 PK보다 크기가 크고(16바이트 대 4바이트), 이는 인덱스 크기와 성능에 영험을 미칠 수 있습니다.
인덱스 비효율성 : UUID가 순차적이지 않기 때문에, 새로운 레코드가 테이블의 중간에 삽입될 수 있으며, 이는 인덱스 재구성과 페이지 분할을 유발하여 성능을 저하시킬 수 있습니다.
읽기 성능 저하 : 크기가 큰 UUID는 읽기 쿼리에 대한 성능을 저하시킬 수 있으며, 특히 대용량 데이터에서 더욱 두드러집니다.
스토리지 요구 증가 : UUID의 크기가 더 크기 때문에, 같은 양의 데이터를 저장하는 데 더 많은 디스크 공간을 필요로 합니다.

 

2-1. 인덱스 재구성과 페이지 분할을 유발이 되는 이유가 뭘까?

 

B-트리 인덱스
B-트리 구조 : 대부분의 관계형 데이터베이스는 B-트리 또는 그 변형을 인덱스로 사용합니다. B-트리는 키-값 쌍을 정렬된 상태로 유지하면서, 빠른 검색, 삽입, 삭제 연산을 지원합니다.
순차적 키 삽입 : 순차적인 기본 키(예: 자동 증가 정수)를 사용하면, 새로운 레코드는 항상 B-트리의 마지막 노드에 삽입됩니다. 이는 인덱스 구조를 변경할 필요가 거의 없으며, 효율적입니다.

UUID와 인덱스 재구성 및 페이지 분할
비순차적 키 삽입 : UUID와 같은 비순차적 키를 사용하면, 새로운 레코드가 B-트리의 중간 어딘가에 삽입될 수 있습니다. 이는 인덱스 노드의 재구성을 필요로 하며, 더 많은 디스크 I/O를 발생시킵니다.
페이지 분할 : 데이터베이스는 데이터를 '페이지'라는 단위로 디스크에 저장합니다. B-트리의 한 노드가 페이지에 꽉 차게 되면, 추가 데이터 삽입을 위해 해당 노드는 '분할'되어야 합니다. 새로운 노드는 새로운 페이지에 저장되며, 인덱스 구조가 조정됩니다.
성능 저하 : 페이지 분할은 자원 집약적인 작업입니다. B-트리에 비순차적 키가 삽입될 때마다 페이지 분할이 발생할 가능성이 있으며, 이는 인덱스 성능을 저하시키고 디스크 공간을 더 많이 사용하게 합니다.

반응형