확장 가능하고 가용성이 높은 키-값 스토리지 시스템을 구축하는 방법

이 기사는 공식 계정에 처음 게시되었습니다. 더 많은 AI (power_ai), 관심을 가져 주셔서 감사합니다. 프로그래밍 및 AI 건조 제품이 제 시간에 전달됩니다!

키-값 데이터베이스라고도 하는 키-값 저장소는 비관계형 데이터베이스 유형입니다. 각 고유 식별자는 관련 값과 함께 키로 저장됩니다. 이 데이터 쌍을 "키-값" 쌍이라고 합니다.

키-값 쌍에서 키는 고유해야 하며 키를 통해 키와 연결된 값에 액세스할 수 있습니다. 키는 일반 텍스트 또는 해시일 수 있습니다. 짧은 키는 성능상의 이유로 더 좋습니다. 열쇠는 어떻게 생겼나요? 여기 몇 가지 예가 있어요.

  • 일반 텍스트 키: "last_logged_in_at"
  • 해시 키: 253DDEC4

키-값 쌍의 값은 문자열, 목록, 객체 등이 될 수 있습니다. 키-값 저장소에서 값은 일반적으로 Amazon dynamo[1], Memcached[2], Redis[3] 등과 같이 불투명 객체로 취급됩니다.

다음은 키-값 저장소의 데이터 조각입니다.

이미지-20230525200227341

이 장에서는 다음 작업을 지원하는 키-값 저장소를 설계하라는 요청을 받습니다.

  • put(key, value) // "key"와 관련된 "value"를 삽입합니다.
  • get(key) // "키"와 관련된 "값"을 가져옵니다.

문제 이해 및 설계 범위 설정

완벽한 디자인은 없습니다. 각 디자인은 읽기, 쓰기 및 메모리 사용량 절충과 관련하여 특정 균형을 달성합니다. 여전히 이루어져야 하는 절충안은 일관성과 가용성 사이에 있습니다. 이 장에서는 다음 기능을 사용하여 키-값 저장소를 설계합니다.

  • 키-값 쌍의 크기는 10KB 미만으로 작습니다.
  • 빅데이터 저장이 가능합니다.
  • 고가용성: 시스템은 장애 중에도 빠르게 응답합니다.
  • 높은 확장성: 시스템은 대규모 데이터 세트를 지원하도록 확장할 수 있습니다.
  • Auto Scaling: 트래픽에 따라 서버를 자동으로 추가/제거합니다.
  • 조정 가능한 일관성.
  • 낮은 대기 시간.

단일 서버 키-값 저장소

단일 서버에 있는 키-값 저장소를 쉽게 개발할 수 있습니다. 직관적인 접근 방식은 키-값 쌍을 해시 테이블에 저장하여 모든 것을 메모리에 유지하는 것입니다. 메모리 액세스는 빠르지만 공간 제약으로 인해 모든 것을 메모리에 맞추는 것이 불가능할 수 있습니다. 단일 서버에 더 많은 데이터를 저장하기 위해 두 가지 최적화를 수행할 수 있습니다.

  • 데이터 압축
  • 자주 사용하는 데이터만 메모리에 저장하고 나머지는 디스크에 저장

이러한 최적화에도 불구하고 단일 서버는 신속하게 용량에 도달할 수 있습니다. 빅 데이터를 지원하려면 분산 키-값 저장소가 필요합니다.

분산 키-값 저장소

분산 키-값 저장소는 여러 서버에 키-값 쌍을 배포하는 분산 해시 테이블이라고도 합니다. 분산 시스템을 설계할 때 CAP( C Consistency, A Availability, P Partition Tolerance) 이론을 이해하는 것이 중요합니다 .

CAP 정리

CAP 정리는 분산 시스템이 일관성, 가용성 및 파티션 허용의 세 가지 보장 중 두 가지 이상을 동시에 제공하는 것은 불가능하다고 말합니다. 몇 가지 정의를 정리합시다.

일관성 : 일관성이란 모든 클라이언트가 연결된 노드에 관계없이 동시에 동일한 데이터를 보는 것을 의미합니다.

가용성 : 가용성은 일부 노드가 다운되더라도 데이터를 요청하는 모든 클라이언트가 응답을 받을 수 있음을 의미합니다.

Partition Tolerance : 파티션은 두 노드 간의 통신 손실을 나타냅니다. 파티션 허용은 네트워크 파티션이 발생한 경우에도 시스템이 계속 작동함을 의미합니다.

CAP 정리는 그림 6-1과 같이 세 가지 속성 중 하나가 다른 두 가지를 위해 희생되어야 한다고 말합니다.

이미지-20230525200252771

오늘날 키-값 저장소는 지원하는 두 가지 CAP 특성에 따라 분류됩니다.

CP(Consistency and Partition Tolerance) 시스템 : CP 키-값 저장소는 가용성을 희생시키면서 일관성과 파티션 허용 오차를 지원합니다.

AP(가용성 및 파티션 허용 오차) 시스템 : AP 키-값 저장소는 일관성을 희생하면서 가용성 및 파티션 허용 오차를 지원합니다.

CA(일관성 및 가용성) 시스템 : CA 키-값 저장소는 파티션 허용 오차를 희생하여 일관성과 가용성을 지원합니다. 네트워크 장애는 불가피하므로 분산 시스템은 네트워크 파티션을 허용해야 합니다. 따라서 실제 응용 프로그램에는 CA 시스템이 없습니다.

위에서 읽은 내용은 주로 정의 부분입니다. 이해를 돕기 위해 몇 가지 구체적인 예를 살펴보겠습니다. 분산 시스템에서 데이터는 일반적으로 여러 번 복제됩니다. 그림 6-1과 같이 세 개의 복제본 노드 n1 , n2n3 에 데이터가 복제되었다고 가정합니다 .

이상적인 상황

이상적인 세상에서는 네트워크 파티션이 발생하지 않습니다. n1 에 기록된 데이터는 자동으로 n2n3 에 복사됩니다 . 일관성과 가용성이 모두 달성됩니다.

이미지-20230525200306382

실제 분산 시스템

분산 시스템에서 파티션은 불가피하며 파티션이 발생하면 일관성과 가용성 중 하나를 선택해야 합니다. 그림 6-3에서 n3은 다운되어 n1n2 와 통신할 수 없습니다 . 클라이언트가 n1 또는 n2 에 데이터를 쓰면 데이터가 n3에 전파되지 않습니다. 데이터가 n3 에 기록되었지만 n1n2 로 전파되지 않은 경우 n1n2는 오래된 데이터를 갖게 됩니다.

이미지-20230525200323569

가용성보다 일관성을 선택하는 경우(CP 시스템) n1n2 에 대한 모든 쓰기 작업을 차단하여 이 세 서버 간의 데이터 불일치를 방지해야 시스템을 사용할 수 없게 됩니다. 은행 시스템은 일반적으로 일관성 요구 사항이 매우 높습니다. 예를 들어 은행 시스템에서는 최신 잔액 정보를 표시하는 것이 중요합니다. 네트워크 분할로 인해 불일치가 발생하면 은행 시스템은 불일치가 해결될 때까지 오류를 반환합니다.

그러나 일관성보다 가용성을 선택하면(AP 시스템) 시스템이 오래된 데이터를 반환하더라도 계속해서 읽기를 허용합니다. 쓰기 작업의 경우 n1n2는 계속해서 쓰기를 수락하고 네트워크 파티션이 해결되면 데이터가 n3 에 동기화됩니다 .

사용 시나리오에 맞는 올바른 CAP 보장을 선택하는 것은 분산 키-값 저장소를 구축하는 데 있어 중요한 단계입니다. 면접관과 이에 대해 논의한 후 논의를 기반으로 시스템을 설계할 수 있습니다.

시스템 구성요소

이 섹션에서는 키-값 저장소를 구축할 때 사용되는 다음과 같은 핵심 구성 요소 및 기술에 대해 설명합니다.

  • 데이터 파티션
  • 데이터 복제
  • 일관성
  • 불일치 해결
  • 문제 해결
  • 시스템 아키텍처 다이어그램
  • 쓰기 경로
  • 읽기 경로

다음은 크게 Dynamo[4], Cassandra[5] 및 BigTable[6]의 세 가지 인기 있는 키-값 스토리지 시스템을 기반으로 합니다.

데이터 파티션

대규모 애플리케이션의 경우 전체 데이터 세트를 단일 서버에 맞추는 것은 불가능합니다. 이 작업을 수행하는 가장 쉬운 방법은 데이터를 더 작은 파티션으로 나누고 여러 서버에 저장하는 것입니다. 데이터를 분할할 때 두 가지 문제가 있습니다.

  • 여러 서버에 데이터를 고르게 분산합니다.
  • 노드를 추가하거나 제거할 때 데이터 이동을 최소화합니다.

5장에서 논의된 일관된 해싱은 이러한 문제에 대한 훌륭한 솔루션입니다. 일관된 해싱이 높은 수준에서 어떻게 작동하는지 다시 살펴보겠습니다.

  • 먼저 서버를 해시 링에 놓습니다. 그림 6-4에서 s0, s1, ..., s7 로 표시된 8개의 서버가 해시 링에 배치됩니다.
  • 다음으로 키가 동일한 링에 해시되고 시계 방향으로 이동할 때 만나는 첫 번째 서버에 저장됩니다. 예를 들어 key0은 이 논리에 따라 s1 에 저장됩니다 .

이미지-20230525200347075

일관된 해싱을 사용하여 데이터를 분할하면 다음과 같은 이점이 있습니다.

Auto Scaling: 부하에 따라 서버를 자동으로 추가 및 제거할 수 있습니다.

이기종: 서버의 가상 노드 수는 서버 용량에 비례합니다. 예를 들어 용량이 더 큰 서버에는 더 많은 가상 노드가 할당됩니다.

데이터 복제

고가용성 및 안정성을 위해 데이터는 N개의 서버에서 비동기식으로 복제 되어야 합니다 . 여기서 N 은 구성 가능한 매개변수 입니다 . N개의 서버는 다음 논리에 따라 선택됩니다. 키가 해시 링의 특정 위치에 매핑된 후 해당 위치에서 시계 방향으로 이동하고 링에서 첫 번째 N개의 서버를 선택하여 데이터 복사본을 저장 합니다 . 그림 6-5( N = 3 ) 에서 key0은 s1, s2 s3복제됩니다 .

이미지-20230525200401723

가상 노드의 경우 링의 처음 N 노드는 N 개 미만의 물리적 서버에서 소유할 수 있습니다. 이 문제를 방지하기 위해 시계 방향으로 걷는 논리를 수행할 때 고유한 서버만 선택합니다.

정전, 네트워크 문제, 자연 재해 등으로 인해 동일한 데이터 센터의 노드가 동시에 실패하는 경우가 많습니다. 더 나은 안정성을 위해 복제본은 고속 네트워크로 연결된 서로 다른 데이터 센터에 배치됩니다.

일관성

데이터는 여러 노드에 걸쳐 복제되므로 복제본 간에 동기화되어야 합니다. 쿼럼(쿼럼) 합의는 읽기 및 쓰기 작업의 일관성을 보장할 수 있습니다. 먼저 몇 가지 개념을 정의해 보겠습니다.

N = 복제본 수

W =쓰기 쿼럼 크기 W. 쓰기 작업이 성공한 것으로 간주되려면 W 복제본에서 쓰기 작업에 대한 승인을 얻어야 합니다

R = 크기 R 의 정족수를 읽습니다. 읽기 작업이 성공한 것으로 간주하려면 읽기 작업이 적어도 R 복제본의 응답을 기다려야 합니다.

그림 6-6에서 N=3 에 대한 다음 예를 고려하십시오 .

이미지-20230525200418446

W = 1은 데이터가 서버에 기록된다는 의미가 아닙니다. 예를 들어, 그림 6-6의 구성에서 데이터는 s0 , s1s2 에 복제됩니다 . W = 1은 코디네이터가 쓰기 작업이 성공한 것으로 간주하기 전에 적어도 하나의 승인을 받아야 함을 의미합니다. 예를 들어 s1 에서 승인을 받으면 s0s2 에서 승인을 기다릴 필요가 없습니다 . 코디네이터는 클라이언트와 노드 간의 프록시 역할을 합니다.

W, RN 의 구성은 대기 시간과 일관성 간의 일반적인 절충안입니다. W = 1 또는 R = 1 인 경우 코디네이터는 하나의 복제본에서 응답만 기다리면 되므로 작업이 빠르게 반환됩니다. W 또는 R > 1 이면 시스템이 더 나은 일관성을 제공하지만 코디네이터가 가장 느린 복제본의 응답을 기다려야 하므로 쿼리 속도가 느려집니다.

W + R > N 이면 일관성을 보장하기 위해 최신 데이터가 있는 중첩 노드가 하나 이상 있어야 하므로 강력한 일관성이 보장됩니다.

사용 사례에 맞게 N, WR을 구성하는 방법은 무엇입니까 ? 가능한 설정은 다음과 같습니다.

R = 1W = N 인 경우 시스템은 빠른 읽기에 최적화되어 있습니다.

W = 1R = N 이면 시스템이 빠른 쓰기에 최적화되어 있습니다.

W + R > N 이면 강력한 일관성이 보장됩니다(일반적으로 N = 3, W = R = 2 ).

W + R <= N 이면 강력한 일관성을 보장할 수 없습니다.

요구 사항에 따라 원하는 수준의 일관성을 달성하기 위해 W, R, N 값을 조정할 수 있습니다.

일관성 모델

일관성 모델은 키-값 저장소를 설계할 때 고려해야 할 또 다른 중요한 요소입니다. 일관성 모델은 데이터 일관성의 정도를 정의하며 가능한 많은 일관성 모델이 있습니다.

  • 강력한 일관성: 모든 읽기 작업에서 반환된 값은 가장 최근에 업데이트된 기록된 데이터 항목의 결과에 해당합니다. 클라이언트는 오래된 데이터를 볼 수 없습니다.
  • 약한 일관성: 후속 읽기 작업에 최신 업데이트 값이 표시되지 않을 수 있습니다.
  • 최종 일관성: 약한 일관성의 특정 형태입니다. 충분한 시간이 주어지면 모든 업데이트가 전파되고 모든 복제본이 일관됩니다.

강력한 일관성은 일반적으로 모든 복제본이 현재 쓰기에 동의할 때까지 복제본이 새로운 읽기/쓰기 작업을 수락하지 않도록 강제함으로써 달성됩니다. 이 접근 방식은 새로운 작업을 차단할 수 있으므로 고가용성 시스템에는 적합하지 않습니다. Dynamo 및 Cassandra는 키-값 저장소에 권장되는 일관성 모델인 최종 일관성을 사용합니다. 동시 쓰기에서 최종 일관성은 일관성 없는 값이 시스템에 입력되도록 허용하고 클라이언트가 조정하기 위해 값을 읽도록 강제합니다. 다음 섹션에서는 조정을 위해 버전 제어가 작동하는 방식을 설명합니다.

불일치 해결: 버전 제어

복제는 고가용성을 제공하지만 복제본 간에 불일치가 발생합니다. 버전 관리 및 벡터 잠금은 불일치를 해결하는 데 사용됩니다. 버전 관리는 각 데이터 수정을 변경할 수 없는 새로운 버전의 데이터로 취급하는 것을 말합니다. 버전 제어에 대해 이야기하기 전에 예를 들어 불일치가 어떻게 발생하는지 설명하겠습니다.

그림 6-7과 같이 복제본 노드 n1n2 는 동일한 값을 가집니다. 이 값을 원래 값이라고 하자 . 서버 1서버 2는 *get("name")* 작업에 대해 동일한 값을 가져옵니다.

이미지-20230525200443156

다음으로 그림 6-8과 같이 서버 1은 이름을 "johnSanFrancisco"로 변경하고 서버 2는 이름을 "johnNewYork"로 변경합니다. 이 두 가지 변경 사항은 동시에 적용됩니다. 이제 버전 v1v2 라는 상충되는 값이 있습니다 .

이미지-20230525200456151

이 예에서는 원래 값을 기반으로 수정하므로 원래 값을 무시할 수 있습니다. 그러나 마지막 두 버전 간의 충돌을 해결할 명확한 방법이 없습니다. 이 문제를 해결하려면 충돌을 감지하고 조정할 수 있는 버전 관리 시스템이 필요합니다. 벡터 클럭은 이 문제를 해결하기 위한 일반적인 기술입니다. 벡터 시계가 어떻게 작동하는지 봅시다.

벡터 시계는 데이터 항목과 연결된 *[server, version]* 쌍입니다. 버전이 다른 버전 이전인지 이후인지 또는 충돌하는지 확인하는 데 사용할 수 있습니다.

벡터 시계가 D([S1, v1], [S2, v2], …, [Sn, vn]) 로 표시된다고 가정합니다 . 여기서 D 는 데이터 항목, v1 은 버전 카운터, s1 은 서버 번호 등입니다. . 데이터 항목 D 가 서버 Si 에 기록되면 시스템은 다음 작업 중 하나를 수행해야 합니다.

  • *[Si, vi] 가 있으면 vi*를 증가시킵니다 .
  • 그렇지 않으면 새 항목 *[Si, 1]*을 만듭니다.

위의 추상 논리는 그림 6-9의 구체적인 예와 함께 설명됩니다.

이미지-20230525200550038

  1. 클라이언트는 데이터 항목 D1 을 시스템에 기록하고 쓰기 작업은 이제 벡터 클럭 D1[(Sx, 1)]을 갖는 서버 Sx 에 의해 처리됩니다 .

  2. 다른 클라이언트는 최신 D1 을 읽고 D2 로 업데이트한 다음 다시 씁니다. D2 는 D1 에서 파생되므로 D1을 재정의합니다 . 쓰기 작업이 동일한 서버 Sx 에 의해 처리되고 이제 Sx 에 벡터 클럭 D2([Sx, 2])가 있다고 가정합니다 .

  3. 다른 클라이언트는 최신 D2 를 읽고 D3 로 업데이트한 다음 다시 씁니다. 쓰기 작업이 서버 Sy 에 의해 처리되고 이제 Sy 에 벡터 클럭 D3([Sx, 2], [Sy, 1])) 이 있다고 가정합니다 .

  4. 다른 클라이언트는 최신 D2 를 읽고 D4 로 업데이트한 다음 다시 씁니다. 쓰기 작업이 서버 Sz 에 의해 처리되고 이제 Sz 에 D4([Sx, 2], [Sz, 1])) 이 있다고 가정합니다 .

  5. 다른 클라이언트가 D3D4를 읽을 때 SySz 모두에 의해 수정 되는 데이터 항목 D2 로 인해 발생하는 충돌을 찾습니다 . 클라이언트에서 충돌을 해결하고 업데이트된 데이터를 서버로 보냅니다. 쓰기 작업이 Sx 에 의해 처리되고 이제 Sx 에 D5([Sx, 3], [Sy, 1], [Sz, 1]) 이 있다고 가정합니다 . 충돌을 감지하는 방법은 나중에 설명하겠습니다.

벡터 시계를 사용하면 버전 Y의 벡터 시계 에 있는 참가자 의 버전 카운터가 버전 X 의 카운터보다 크거나 같은 경우 버전 X가 버전 Y 의 조상임을 쉽게 알 수 있습니다(즉, 충돌이 없음). . 예를 들어 벡터 클럭 *D([s0, 1], [s1, 1])은 D([s0, 1], [s1, 2])*의 조상 입니다 . 따라서 충돌이 기록되지 않습니다.

마찬가지로, Y의 벡터 클럭 카운터가 X 의 해당 카운터보다 작은 참가자가 있는 경우 (, 충돌이 있는 경우) 버전 X 는 Y의 형제로 판단될 수 있습니다 . 예를 들어 다음 두 벡터 시계는 충돌을 나타냅니다. D([s0, 1], [s1, 2])D([s0, 2], [s1, 1]).

벡터 시계가 충돌을 해결할 수 있지만 두 가지 중요한 단점이 있습니다. 첫째, 벡터 클럭은 충돌 해결 논리를 구현해야 하므로 클라이언트의 복잡성을 증가시킵니다.

둘째, 벡터 클럭의 *[server:version]* 쌍이 빠르게 증가할 수 있습니다. 이 문제를 해결하기 위해 길이에 대한 임계값을 설정하고 제한을 초과하면 가장 오래된 쌍이 삭제됩니다. 이는 자손 관계를 정확하게 결정할 수 없기 때문에 중재 시 비효율로 이어질 수 있습니다. 그러나 Dynamo 논문[4]에 따르면 Amazon은 생산 과정에서 이러한 문제가 발생하지 않았으므로 대부분의 회사에서 수용 가능한 솔루션일 수 있습니다.

장애 처리

모든 대규모 시스템과 마찬가지로 장애는 불가피할 뿐만 아니라 일반적입니다. 실패 시나리오를 처리하는 것은 매우 중요합니다. 이 섹션에서는 먼저 실패를 감지하는 기술을 소개합니다. 그런 다음 일반적인 문제 해결 전략에 대해 논의합니다.

오류 감지

분산 시스템에서는 다른 서버가 다운되었다고 해서 서버가 다운되었다고 믿는 것만으로는 충분하지 않습니다. 일반적으로 서버를 작동 중지로 표시하려면 두 개 이상의 독립적인 정보 소스가 필요합니다.

그림 6-10에서 볼 수 있듯이 전방향 멀티캐스트는 간단하고 직관적인 솔루션입니다. 그러나 이것은 시스템에 많은 서버가 있을 때 비효율적입니다.

이미지-20230525200611485

더 나은 솔루션은 가십 프로토콜과 같은 분산된 결함 감지 방법을 사용하는 것입니다. 가십 프로토콜은 다음과 같이 작동합니다.

  • 각 노드는 구성원 ID와 하트비트 카운터가 포함된 노드 구성원 목록을 유지 관리합니다.
  • 각 노드는 주기적으로 하트비트 카운터를 증가시킵니다.
  • 각 노드는 주기적으로 임의의 노드 세트에 하트비트를 전송하고 다른 세트의 노드로 전파됩니다.
  • 노드가 하트비트를 수신하면 구성원 목록이 최신 정보로 업데이트됩니다.
  • 미리 정해진 시간 내에 하트비트가 증가하지 않으면 해당 구성원은 오프라인으로 간주됩니다.

이미지-20230525200627631

그림 6-11과 같이:

  • 노드 s0은 왼쪽에 표시된 노드 구성원 목록을 유지합니다.
  • 노드 s0은 노드 s2(구성원 ID = 2)의 하트비트 카운터가 오랫동안 증가하지 않았음을 알아차립니다.
  • 노드 s0은 s2 에 대한 정보가 포함된 하트비트를 임의의 노드 집합으로 보냅니다. 다른 노드에서 s2 의 하트비트 카운터가 오랫동안 업데이트되지 않았음을 확인하면 노드 s2는 다운된 것으로 표시되고 이 정보는 다른 노드로 전파됩니다.

일시적인 실패 처리

가십 프로토콜을 통해 장애가 감지된 후 시스템은 가용성을 보장하기 위해 특정 메커니즘을 배포해야 합니다. 엄격한 쿼럼 접근 방식에서는 쿼럼 합의 섹션에 표시된 대로 읽기 및 쓰기 작업이 차단될 수 있습니다.

가용성을 향상시키기 위해 "느슨한 조정"[4]이라는 기술이 사용됩니다. 쿼럼 요구 사항을 적용하는 대신 시스템은 쓰기 작업을 위해 해시 링에서 상위 W 정상 서버를 선택하고 읽기 작업을 위해 상위 R 정상 서버를 선택합니다. 오프라인 서버는 무시됩니다.

네트워크 또는 서버 오류로 인해 한 서버를 사용할 수 없는 경우 다른 서버가 일시적으로 요청을 처리합니다. 다운된 서버가 다시 가동되면 데이터 일관성을 위해 변경 사항이 다시 푸시됩니다. 이 프로세스를 힌트 핸드오프라고 합니다. 그림 6-12의 s2를 사용할 수 없기 때문에 s3가 일시적으로 읽기 및 쓰기 작업을 처리합니다. s2가 다시 온라인 상태가 되면 s3는 데이터를 다시 s2 로 넘깁니다 .

이미지-20230525200643811

영구적인 실패 처리

Hinted Handoff는 일시적인 실패를 처리하는 데 사용됩니다. 그렇다면 복제본을 영구적으로 사용할 수 없다면 어떻게 될까요? 이러한 경우를 처리하기 위해 안티 엔트로피 프로토콜을 구현하여 복제본을 동기화 상태로 유지합니다. 엔트로피 방지에는 복제본의 모든 데이터를 비교하고 각 복제본을 최신 버전으로 업데이트하는 작업이 포함됩니다. Merkle tree라는 구조를 사용하여 불일치를 감지하고 전송되는 데이터의 양을 최소화합니다.

Wikipedia[7]에서 인용: "해시 트리 또는 Merkle 트리는 리프 노드가 아닌 각 노드가 하위 노드의 레이블 또는 값(리프 노드의 경우)의 해시로 표시되는 트리입니다. Ha Histree 대규모 데이터 구조의 내용을 효율적이고 안전하게 확인할 수 있습니다."

키스페이스가 1에서 12 사이라고 가정하고 다음 단계는 Merkle 트리를 구축하는 방법을 보여줍니다. 강조 표시된 상자는 불일치를 나타냅니다.

1단계: 키스페이스를 그림 6-13과 같이 버킷(예제에서는 4개)으로 나눕니다. 버킷은 트리의 깊이 제한을 유지하기 위해 루트 레벨 노드로 사용됩니다.

이미지-20230525200700209

2단계: 버킷이 생성되면 균일한 해싱 방법을 사용하여 버킷의 각 키를 해시합니다(그림 6-14).

이미지-20230525200711090

3단계: 각 버킷에 대한 해시 노드를 생성합니다(그림 6-15).

이미지-20230525200720689

4단계: 하위 노드의 해시를 계산하여 루트 노드까지 트리를 구축합니다(그림 6-16).

이미지-20230525200732461

두 Merkle 트리를 비교하려면 먼저 루트 해시를 비교하십시오. 루트 해시가 일치하면 두 서버의 데이터가 동일합니다. 루트 해시가 다르면 왼쪽 자식 해시를 비교한 다음 오른쪽 자식 해시를 비교합니다. 트리를 탐색하여 동기화되지 않은 버킷을 찾고 해당 버킷만 동기화할 수 있습니다.

Merkle 트리에서 동기화해야 하는 데이터의 양은 포함된 데이터의 양이 아니라 두 복제본 간의 차이에 비례합니다. 실제 시스템에서 버킷 크기는 상당히 큽니다. 예를 들어 가능한 구성은 10억 개의 키당 100만 개의 버킷을 포함하는 것이므로 각 버킷에는 1000개의 키만 포함됩니다.

데이터 센터 장애 처리

데이터 센터 장애는 정전, 네트워크 중단, 자연 재해 등으로 인해 발생할 수 있습니다. 데이터 센터 장애에 대처할 수 있는 시스템을 구축하기 위해서는 여러 데이터 센터에 데이터를 복제하는 것이 중요합니다. 한 데이터 센터가 완전히 오프라인인 경우에도 사용자는 다른 데이터 센터를 통해 데이터에 계속 액세스할 수 있습니다.

시스템 아키텍처 다이어그램

키-값 저장소를 설계할 때 다양한 기술적 고려 사항에 대해 논의했으므로 이제 그림 6-17에 표시된 아키텍처 다이어그램으로 주의를 돌릴 수 있습니다.

이미지-20230525200746256

아키텍처의 주요 기능은 다음과 같습니다.

  • 클라이언트는 get(key)put(key,value) 와 같은 간단한 API를 통해 키-값 저장소와 통신합니다 .
  • 코디네이터는 클라이언트와 키-값 저장소 간의 프록시 역할을 하는 노드입니다.
  • 노드는 일관된 해싱을 사용하여 링에 배포됩니다.
  • 이 시스템은 완전히 분산되어 있으며 노드 추가 및 이동이 자동으로 수행될 수 있습니다.
  • 데이터는 여러 노드에 복제됩니다.
  • 각 노드는 동일한 책임 집합을 가지므로 단일 장애 지점이 없습니다.

디자인이 분산되어 있기 때문에 각 노드는 그림 6-18과 같이 많은 작업을 수행합니다.

이미지-20230525200759966

쓰기 경로

그림 6-19는 쓰기 요청이 특정 노드로 전달될 때 발생하는 상황을 설명합니다. 쓰기/읽기 경로의 제안된 설계는 주로 Cassandra [8]의 아키텍처를 기반으로 합니다.

이미지-20230525200811822

  1. 쓰기 요청은 커밋 로그 파일에 유지됩니다.

  2. 데이터는 메모리 캐시에 보관됩니다.

  3. Memcache가 가득 차거나 미리 정의된 임계값에 도달하면 데이터가 온디스크 SSTable로 플러시됩니다[9]. 참고: 정렬된 문자열 테이블(SSTable)은 정렬된 쌍 목록입니다. SSTable에 대해 더 알고 싶은 독자는 참조 [9]를 참조하십시오.

읽기 경로

읽기 요청이 특정 노드로 전달된 후 먼저 데이터가 메모리 캐시에 있는지 여부를 확인합니다. 그렇다면 그림 6-20과 같이 데이터가 클라이언트로 반환됩니다.

이미지-20230525200822818

데이터가 메모리에 없으면 디스크에서 검색됩니다. 어떤 SSTable에 이 키가 포함되어 있는지 알아내는 효율적인 방법이 필요합니다. 블룸 필터[10]는 일반적으로 이 문제를 해결하는 데 사용됩니다.

그림 6-21은 데이터가 메모리에 없을 때 읽기 경로를 보여줍니다.

이미지-20230525200837584

  1. 시스템은 먼저 데이터가 메모리에 있는지 확인합니다. 그렇지 않은 경우 2단계로 이동합니다.
  2. 데이터가 메모리에 없으면 시스템은 블룸 필터를 확인합니다.
  3. 블룸 필터는 키를 포함할 가능성이 있는 SSTable을 결정하는 데 사용됩니다.
  4. SSTable은 데이터 세트의 결과를 반환합니다.
  5. 데이터 세트의 결과는 클라이언트에 반환됩니다.

요약하다

이 장에서는 많은 개념과 기술을 소개합니다. 기억을 돕기 위해 다음 표에는 분산 키-값 저장소 및 해당 기술에서 사용하는 기능이 요약되어 있습니다.

이미지-20230525200853259

참조

[1] 아마존 다이나모DB: https://aws.amazon.com/dynamodb/

[2] memcached: https://memcached.org/

[3] 레디스: https://redis.io/

[4] Dynamo: Amazon의 고가용성 키-값 저장소: https://www.allthingsdistributed.com/files/amazon-dynamo-sosp2007.pdf

[5] 카산드라: https://cassandra.apache.org/

[6] Bigtable: 구조화된 분산 데이터 저장 시스템: https://static.googleusercontent.com/media/research.google.com/en//archive/bigtable-osdi06.pdf

[7] 머클 트리: https://en.wikipedia.org/wiki/Merkle_tree

[8] 카산드라 아키텍처: https://cassandra.apache.org/doc/latest/architecture/

[9] SStable: https://www.igvita.com/2012/02/06/sstable-and-log-structured-storage-leveldb/

[10] 블룸 필터 https://en.wikipedia.org/wiki/Bloom_filter

안녕하세요 저는 7년차 개발중인 베테랑 드라이버이자 2년차 인터넷 5년차 외국계 회사 시산입니다. 아산이랑 라오메이도 이길 수 있고 나도 홍보댓글에 망했다. 수년 동안 저는 아르바이트를 하고, 사업을 시작하고, 개인 일을 인수하고, 잡업을 했습니다. 돈을 벌고 돈을 잃었습니다. 그 과정에서 무엇을 배우든 계속 배워야 한다는 것이 저의 가장 깊은 느낌입니다. 인내할 수 있는 한 코너 추월은 쉽게 달성할 수 있습니다! 그러니 지금 내가 하는 일을 하기에는 너무 늦었는지 묻지 마세요. 여전히 방향이 없다면 [공개 계정: 더 많은 AI(power_ai)]를 팔로우할 수 있습니다. 코너링과 추월을 위한 자본을 축적하는 데 도움이 되는 최첨단 정보와 프로그래밍 지식을 자주 공유할 것입니다.

추천

출처blog.csdn.net/smarter_AI/article/details/131819407