본문 바로가기
DBMS

PolarDB 아키텍처 세부: 컴퓨팅-스토리지 분리 (Compute-Storage Separation Challenges of Shared Storage)

by developer's warehouse 2023. 11. 25.

이 글에서는 Compute-Storage 분리 관련한 세부 사항에 대해서 설명합니다. 

PolarDB 아키텍처 세부: 컴퓨팅-스토리지 분리 (Compute-Storage Separation Challenges of Shared Storage) 썸네일

Challenges of Shared Storage


컴퓨팅-스토리지 분리를 통해 PolarDB 클러스터의 컴퓨팅 노드가 동일한 물리적 스토리지를 공유할 수 있습니다. 공유 스토리지에는 다음과 같은 문제가 있습니다:

 

  • 데이터 일관성: 컴퓨팅 클러스터의 데이터 복사본 N개와 스토리지 클러스터의 데이터 복사본 1개 간에 일관성을 보장하는 방법.
  • 읽기/쓰기 분할: 짧은 지연 시간으로 데이터를 복제하는 방법.
  • 고가용성: 복구 및 장애 조치를 수행하는 방법.
  • I/O 모델: 버퍼링 I/O에서 직접 I/O로 파일 시스템을 최적화 하는 방법.

 

 

공유 스토리지의 기본 원칙(Basic Principle)


글 내용

 

공유 스토리지 기본 원칙 설계도


공유 스토리지를 사용하는데 있어서 기본 설계 원칙은 다음과 같습니다:

  • Primary 노드는 읽기 요청과 쓰기 요청을 처리할 수 있습니다. 읽기 전용 노드는 읽기 요청만 처리할 수 있습니다.
  • 기본 노드만 공유 스토리지에 데이터를 쓸 수 있습니다. 이렇게 하면 기본 노드에서 쿼리하는 데이터는 읽기 전용 노드에서 쿼리하는 데이터와 동일합니다.
  • 읽기 전용 노드는 WAL 레코드를 적용하여 읽기 전용 노드의 메모리에 있는 페이지가 기본 노드의 메모리에 있는 페이지와 동기화되도록 합니다.
  • 기본 노드는 WAL 레코드를 공유 스토리지에 쓰고, WAL 레코드의 메타데이터만 읽기 전용 노드에 복제됩니다.
  • 읽기 전용 노드는 공유 스토리지에서 WAL 레코드를 읽고 WAL 레코드를 적용합니다.

Data Consistency


Shared-nothing 아키텍처의 인메모리 페이지 동기화

기존 데이터베이스 시스템에서는 기본(Primary) 인스턴스와 읽기 전용 인스턴스에 각각 독립적인 메모리 리소스와 스토리지 리소스가 할당됩니다. 

기본 인스턴스는 WAL 레코드를 읽기 전용 인스턴스에 복제하고, 읽기 전용 인스턴스는 WAL 레코드를 읽고 적용합니다. 이러한 기본 원칙은 복제 상태 머신에도 적용됩니다.

공유 스토리지(Shared-storage) 아키텍처의 인메모리 페이지 동기화

PolarDB 클러스터에서 기본 노드는 WAL 레코드를 공유 스토리지에 복제합니다. 읽기 전용 노드는 공유 스토리지에서 가장 최근의 WAL 레코드를 읽고 적용하여 읽기 전용 노드의 메모리에 있는 페이지가 기본 노드의 메모리에 있는 페이지와 동기화되도록 합니다.

 

공유 디스크에서 메모리 페이지 동기화

 

  • 기본 노드는 페이지의 WAL 레코드를 플러시하여 페이지의 버전 200을 공유 스토리지에 기록합니다.
  • 읽기 전용 노드는 페이지의 WAL 레코드를 읽고 적용하여 페이지를 버전 100에서 버전 200으로 업데이트합니다.

 

공유 스토리지 아키텍처의 오래된 페이지

앞의 그림에 표시된 작업 흐름에서 읽기 전용 노드가 WAL 레코드를 적용하여 얻은 새 페이지는 읽기 전용 노드의 버퍼 풀에서 제거될 수 있습니다. 
이렇게 된 경우 읽기 전용 노드에서 페이지를 쿼리하면 읽기 전용 노드는 공유 스토리지에서 페이지를 읽습니다. 결과적으로 페이지의 이전 버전만 반환됩니다. 이 이전 버전을 오래된 페이지라고 합니다. 다음 그림은 자세한 내용을 보여줍니다.
 
이전 버전 읽는 예제
  • T1에서 기본 노드는 로그 시퀀스 번호(LSN)가 200인 WAL 레코드를 메모리에 기록하여 페이지 1을 버전 500에서 버전 600으로 업데이트합니다.
  • T1에서 읽기 전용 노드의 페이지 1은 버전 500입니다.
  • T2에서 기본 노드는 WAL 레코드 200의 메타데이터를 읽기 전용 노드로 전송하여 읽기 전용 노드에 새 WAL 레코드가 있음을 알립니다.
  • T3에서는 읽기 전용 노드에서 페이지 1을 쿼리합니다. 읽기 전용 노드는 페이지 1의 버전 500과 WAL 레코드 200을 읽고 WAL 레코드 200을 적용하여 페이지 1을 버전 500에서 버전 600으로 업데이트합니다.
  • T4에서 읽기 전용 노드는 버퍼 풀이 충분한 공간을 제공할 수 없기 때문에 페이지 1의 버전 600을 제거합니다.
  • 기본 노드는 페이지 1의 버전 600을 공유 스토리지에 쓰지 않습니다. 공유 저장소에 있는 페이지 1의 가장 최근 버전은 여전히 버전 500입니다.
  • T5에서 읽기 전용 노드에서 페이지 1을 쿼리합니다. 읽기 전용 노드의 메모리에서 페이지 1이 제거되었으므로 읽기 전용 노드는 공유 저장소에서 페이지 1을 읽습니다. 이 경우 페이지 1의 오래된 버전 500이 반환됩니다.

오래된 페이지에 대한 해결책

특정 시점에 읽기 전용 노드에서 페이지를 쿼리할 때 읽기 전용 노드는 해당 시점까지의 페이지의 기본 버전과 WAL 레코드를 읽어야 합니다. 그런 다음 읽기 전용 노드는 WAL 레코드를 순차적으로 하나씩 적용해야 합니다. 
다음 그림은 더 자세한 내용을 보여줍니다.
Read-only node 페이지 적용 방식
  • 각 페이지의 WAL 레코드 메타데이터는 읽기 전용 노드의 메모리에 유지됩니다.
  • 읽기 전용 노드에서 페이지를 쿼리하면 읽기 전용 노드가 페이지의 최신 버전을 얻을 때까지 읽기 전용 노드가 해당 페이지의 WAL 레코드를 읽고 적용해야 합니다.
  • 읽기 전용 노드는 WAL 레코드의 메타데이터를 기반으로 공유 스토리지에서 WAL 레코드를 읽고 적용합니다.
PolarDB는 각 페이지에서 페이지의 WAL 레코드에 대한 매핑을 저장하는 inverted index(역 인덱스)를 유지해야 합니다. 그러나 각 읽기 전용 노드의 메모리 용량은 제한되어 있습니다. 따라서 이러한 inverted index(역 인덱스)는 영구적으로 디스크에 저장되어야 합니다. 이 요구 사항을 충족하기 위해 PolarDB는 LogIndex를 제공합니다. LogIndex는 해시 데이터를 영구적으로 저장하는 데 사용되는 인덱스 구조입니다.
  • 읽기 전용 노드의 WAL 수신 프로세스는 기본 노드로부터 WAL 레코드의 메타데이터를 수신합니다.
  • 각 WAL 레코드의 메타데이터에는 어떤 페이지가 업데이트 되었는지에 대한 정보가 포함되어 있습니다.
  • 읽기 전용 노드는 각 WAL 레코드의 메타데이터를 LogIndex 구조에 삽입하여 LogIndex 레코드를 생성합니다. LogIndex 레코드의 키는 업데이트되는 페이지의 ID이며, LogIndex 레코드의 값은 WAL 레코드의 LSN입니다.
  • 하나의 WAL 레코드에는 업데이트된 여러 페이지에 대한 정보가 포함될 수 있습니다. 이 프로세스를 인덱스 블록 분할이라고 정의합니다. 인덱스 블록이 분할되면 하나의 WAL 레코드가 여러 개의 LogIndex 레코드를 매핑합니다.
  • 읽기 전용 노드는 업데이트된 각 페이지를 버퍼 풀에서 오래된 페이지로 표시합니다. 읽기 전용 노드에서 업데이트된 페이지를 쿼리하면 읽기 전용 노드는 WAL 레코드를 매핑하는 LogIndex 레코드를 기반으로 해당 페이지의 WAL 레코드를 읽고 적용할 수 있습니다.
  • 읽기 전용 노드의 메모리 사용량이 특정 임계값에 도달하면 LogIndex 구조에 저장된 해시 데이터가 메모리에서 디스크로 비동기적으로 플러시됩니다.
log index 관련 설명

 

LogIndex는 오래된 페이지를 방지하고 읽기 전용 노드가 지연 로그 적용 모드(lazy log apply mode)에서 실행되도록 지원합니다. 지연 로그 적용 모드에서 읽기 전용 노드는 더티 페이지에 대한 WAL 레코드의 메타데이터만 적용합니다.
 
 

Future Pages in Shared-storage Architecture

 
읽기 전용 노드는 읽기 전용 노드에 기록된 버전보다 최신 버전인 아직 적용되지 않은 페이지(Future Page)를 반환할 수 있습니다. 다음 그림은 자세한 내용을 보여줍니다.
 
Future Page 설명
  • T1에서 기본 노드는 페이지 1을 버전 500에서 버전 700으로 두 번 업데이트합니다. 업데이트 프로세스 중에 두 개의 WAL 레코드가 생성됩니다. 한 WAL 레코드의 LSN은 200이고 다른 WAL 레코드의 LSN은 300입니다. 현재 페이지 1은 기본 노드와 읽기 전용 노드에서 여전히 버전 500에 있습니다.
  • T2에서 기본 노드는 WAL 레코드 200을 읽기 전용 노드로 보냅니다.
  • T3에서 읽기 전용 노드는 WAL 레코드 200을 적용하여 페이지 1을 버전 600으로 업데이트합니다. 이때 읽기 전용 노드는 WAL 레코드 300을 읽거나 적용하지 않습니다.
  • T4에서 기본 노드는 페이지 1의 버전 700을 공유 스토리지에 씁니다. 동시에 읽기 전용 노드의 버퍼 풀에서 페이지 1이 제거됩니다.
  • T5에서 읽기 전용 노드는 페이지 1을 다시 읽으려고 시도합니다. 읽기 전용 노드의 버퍼 풀에서 페이지 1을 찾을 수 없습니다. 따라서 읽기 전용 노드는 공유 스토리지에서 페이지 1의 버전 700을 가져옵니다. 읽기 전용 노드가 WAL 레코드 300을 읽거나 적용하지 않았기 때문에 페이지 1의 버전 700은 읽기 전용 노드에 대한 미래의 페이지(Future Page)입니다.
  • 읽기 전용 노드가 공유 스토리지에서 가져오는 페이지 중 일부는 미래 페이지이고 일부는 정상 페이지인 경우 데이터 불일치가 발생할 수 있습니다. 예를 들어, 인덱스 블록이 각각 페이지를 매핑하는 두 개의 인덱스로 분할된 후 읽기 전용 노드가 읽은 페이지 중 하나는 일반 페이지이고 다른 하나는 미래 페이지(Future Page)입니다. 이 경우 인덱스의 B+ 트리 구조가 손상됩니다.
 

Solutions to Future Pages

읽기 전용 노드는 지연 적용 모드에서 WAL 레코드를 빠른 속도로 적용합니다. 그러나 이 속도는 기본 노드가 WAL 레코드를 플러시하는 속도보다 여전히 낮을 수 있습니다. 기본 노드가 읽기 전용 노드가 WAL 레코드를 적용하는 속도보다 빠르게 WAL 레코드를 플러시하면 향후 페이지가 반환됩니다. 향후 페이지가 반환되지 않도록 하려면 기본 노드가 WAL 레코드를 플러시하는 속도가 읽기 전용 노드가 WAL 레코드를 적용하는 속도를 초과하지 않도록 PolarDB가 보장해야 합니다. 다음 그림은 자세한 내용을 보여줍니다.
 
Future 페이지 문제를 해결하는 방법
  • 읽기 전용 노드는 T4에서 생성된 WAL 레코드를 적용합니다.
  • 기본 노드가 WAL 레코드를 공유 스토리지로 플러시할 때, 모든 WAL 레코드를 LSN별로 정렬하고 T4까지 업데이트된 WAL 레코드만 플러시합니다.
  • T4에서 생성된 LSN의 파일 위치가 일관성 파일 위치로 정의됩니다.
결론적으로, Future Page 문제를 해결하기 위해서 버퍼 풀의 더티 페이지를 Read-Only노드들에 맞춰서 flush하며, 페이지 동기화를 위해서 WAL 로그에대한 log index를 메모리에 유지하고 페이지 요청시 해당 인덱스를 찾아서 적용해서 처리한다. 
이렇게 요약할 수 있겠습니다. 
facebook twitter kakaoTalk kakaostory naver band shareLink