6장 키-값 저장소 설계 : 면접관


키-값 저장소(key-value store)

키-값 데이터베이스라고도 불리는 비 관계형 데이터베이스.
키와 값 사이의 이런 연결 관계를 “키-값” 쌍 (pair) 이라고 지칭한다.
키-값 저장소의 키는 중복되거나 변경할 수 없는 고유값이며,

키는 일반 텍스트일 수도 있고 해시 값일 수도 있다.
값은 문자열일 수도 있고 리스트(list)일 수도 있고 객체(object)
일 수도 있다.

해시키가 짧으면 어떤 점이 좋나요?

성능상의 이유로, 키는 짧을수록 좋다.
키가 길 때보다 짧을 때 좋은 점은 아래와 같다.

(1) 저장시 메모리 사용량을 절감할 수 있음.
(2)더 빠르게 주소 탐색이 가능해짐.
(3)네트워크 대역 폭 및 I/O 비용이 감소

해시키가 길어 성능이 저하된다면?

지나치게 길이가 긴 키를 사용하게 될 경우에는 메모리 사용량이 늘어나고 인덱싱 및 데이터 탐색 속도가 떨어져 성능이 저하된다.

이때 해시 키(Hash Key)를 사용하면 아무리 긴 문자열 키라도 일정한 고정 길이로 규격화할 수 있어,
키의 상한선 길이를 제한하고 주소 탐색 연산 효율을 높일 수 있다.

• 일반 텍스트 키 : "user:1004:preference:notification:last_logged_in_at" (48자)
• 해시 키       : "253DDEC4" (8자 고정)

예시

(1) 아마존 다이나모
(2) memcached
(3) 레디스


그러나

성능 극대화를 위해 해시키를 짧게 만들면, 메모리사용률과 처리 성능 개선을 이룰 수 있지만,
해시 충돌로 인한 데이터 일관성 훼손의 위험이 있다.

데이터 쓰기와 읽기(데이터 정합성) 그리고 메모리 사용량 사이에 어떤 균형을 찾고,
데이터의 일관성과 가용성 사이에서 타협적 결정을 내린 설계 방식에 대해 고민해보자.


단일 서버 키-값 저장소

키-값 쌍 전부를 메모리에 해시 테이블로 저장하는 방식

  • 장점 : 빠른속도를 보장함.

  • 단점 : 모든 데이터를 메모리 안에 두는 것이 불가능할 수도 있다.

  • 해결책
    (1) 데이터 압축(compression)
    (2) 자주 쓰이는 데이터만 메모리에 두고 나머지는 디스크에 저장

속도는 빠르지만 메모리 용량에 한계가 있기 때문에, 압축하거나 자주 사용하는 데이터만 메모리에 저장하는 방식으로 보완한다


분산 키-값 저장소

해시 함수(Hash Function)를 이용해 데이터를 어떤 서버에 저장할지 결정하는 방식.
분산 해시 테이블이라고도 불린다.

  • 장점:
  • 단점:

더 알아보기 - CAP 정리

분산 키-값 저장소를 사용해 분산시스템을 설계할 때는 CAP정리를 이해하고 있어야 한다.

CAP정리는 일관성(consistency), 가용성(availability), 파티션 감내 (partition tolerance)라는 세 가지 요구사항을 동시에 만족하는 분산 시스템을설계하는 것은 불가능하다는 정리로 이들 가운데 어던 두가지를 충족하려면 나머지 하나는 반드시 희생되어야 한다.

※ 데이터 일관성: 분산 시스템에 접속하는 모든 클라이언트는 어떤 노드에 접속했느냐에 관계없이 언제나 같은 데이터를 보게 되어야 한다.
※ 분산 시스템에 접속하는 클라이언트는 일부 노드에 장애가 발생하더라도 항상 응답을 받을 수 있어야 한다.
※ 파티션은 두 노드 사이에 통신 장애가 발생하였음을 의미한다. 
파티션 감내는 네트워크에 파티션이 생기더라도 시스템은 계속 동작하여야 한다는 것을 뜻한다.

CP 시스템

일관성과 파티션 감내를 지원하는 키-값 저장소.
가용성을 희생한다.

AP 시스템

가용성과 파티션 감내를 지원하는 키-값 저장소.
데이터 일관성을 희생한다.

CA 시스템

일관성과 가용성을 지원하는 키-값 저장소. 파티션 감내는 지원하지 않는다.
그러나, 노드 사이에 통신장애는 완전히 회피할 수 없기에 분산시스템은 파티션 문제를 완전히 회피할 수 없으며
설계시에는 반드시 파티션 문제를 감내할 수 있도록 설계되어야 한다.
따라서 실세계에서 CA 시스템은 존재하기 어려운 개념이라고 이해하는 것이 좋다.

분산시스템의 이상적인 노드상태

n1에 기록된 데이터가 자동적으로 n2와 n3에 복제되며 데이터 일관성과 가용성을 유지한다.
이상적인 환경에서는 네트워크 파티션이 발생하는 상황도 절대로 일어나지 않을 것이다.

실세계의 분산 시스템

분산 시스템은 파티션 문제를 피할 수 없다.
그리고 파티션 문제가 발생하면 우리는 일관성과 가용성 사이에서 하나를 선택해야 한다.

만약 n3에 장애가 발생한다면 n1, n2노드와 통신할 수 없다.
따라서 클라이언트가 n1 , n2에 기록한 데이터는 n3에 전달되지 않는다.
n3에 기록되었으나 아직 n1 , n2로 전달되지 않은 데이터가 있다면 n1과 n2는 오래된 사본을 갖고 있을 것이다.

가용성 대신 일관성을 선택한다면

세 서버 사이에 생길 수 있는 데이터 불일치 문제를 피하기 위해 nl과 n2에 대해 쓰기 연산을 중단시켜야 하는
데,
그러면 시스템을 사용할 수 없게 된다

일관성 대신 가용성을 선택한다면

시스템(클라이언트 서비스)은 사용 가능하나, 노드 간 네트워크가 복구될 때까지 데이터 불일치 현상이 지속된다.

시스템 컴포넌트

키-값 저장소 구현에 사용될 핵심 컴포넌트들 및 기술들을 살펴보자.

데이터 파티션

대규모 애플리케 이션의 경우 전체 데이터를 한 대 서버에 욱여넣는 것은 불가능하다.
가장 단순한 해결책은 데이터를 작은 파티션들로 분할한 다음 여러 대 서버에 저장하는 것이다.

이때 고려해야할 점은

1) 데이터를 여러 서버에 고르게 분산할 수 있는가
2) 노드가 추가되거나 삭제될 때 데이터의 이동을 최소화 할 수 있는가

해시함수로 샤딩을 할 경우,
이 문제를 해결하기 위해서 ' 안정 해시(consistent hash)를 쓰면 도움이 된다.

안정해시

키를 해시 링(hash ring) 위에 올린 뒤, 시계 방향으로 가장 가까운 서버에 저장하는 방식.
이때 실제 서버 수가 적으면 데이터가 특정 서버로 쏠릴 수 있으므로, 부하를 균등하게 분산하기 위해 다수의 가상 노드를 해시 링에 올린다

[주요 특징]

주요 특징이점 (좋은 점)
규모 확장 자동화 (Automatic Scaling)• 트래픽 변동에 따라 서버가 자동으로 추가/삭제되어 안정적 운영 가능
• 사용량이 적은 시간에는 서버를 줄여 비용 절감
다양성 (Heterogeneity)• 서버 스펙이 달라도 성능에 맞게 가상 노드 수를 조정해 부하 분배
• 고성능 서버에 더 많은 가상 노드를 배치해 자원 활용 극대화

데이터 다중화

높은 가용성과 안정성 확보를 위해 데이터 사본을 N개 서버에 보관한다.
그런데, 이때 선택된 N개의 가상노드가 매핑되는 실제 물리 서버의 개수가 N개보다 작다면(즉, 서로 다른 가상노드가 같은 물리 서버에 속해 있다면), 서버에 문제가 생겼을 때 예상보다 적은 사본이 남아있을 가능성이 있다.

즉, 의도했던 수준의 내결함성을 확보하지 못하는 것이다.
뿐만 아니라, 같은 데이터 센터에 속한 노드는 정전, 네트워크 이슈, 자연재해 등의 문제를 동시에 겪을 가능성이 있다. 따라서 데이터의 사본은 다른 센터의 서버에 보관하고, 센터들은 고속 네트워크로 연결한다.

※ 내결함성 : 일부 서버에 장애가 발생해도 시스템이 정상적으로 서비스를 유지할 수 있는 능력

데이터 일관성

여러 서버에 데이터를 복제(다중화)해두면 가용성은 올라가지만, 새로운 문제가 생긴다.

"여러 개의 사본 중에, 누가 최신 데이터인지 어떻게 알지?"

이 문제를 해결하는 방법이 바로 정족수 합의(Quorum Consensus) 프로토콜이다.

※ 정족수 : 분산 시스템에서 읽기/쓰기 연산이 "성공했다"고 인정받으려면 일정 개수 이상의 서버가 응답해야 한다.

정족수 합의 프로토콜 변수

기호의미
N데이터의 사본(복제본) 개수
W쓰기 연산 정족수 — 쓰기가 성공했다고 인정받기 위해 필요한 최소 서버 응답 수
R읽기 연산 정족수 — 읽기가 성공했다고 인정받기 위해 필요한 최소 서버 응답 수

클라이언트가 데이터를 쓸 때, N개의 서버 중 적어도 W개로부터 "쓰기 성공" 응답을 받아야 그 쓰기 연산은 성공한 것으로 간주된다.
클라이언트가 데이터를 읽을 때, N개의 서버 중 적어도 R개로부터 응답을 받아야 그 읽기 연산은 성공한 것으로 간주된다.

예를들어 N = 3 일때,
W=1 은 쓰기 연산이 성공했다고 판단하기 위해 클라이언트와 노드 사이에 위치한 중재자(coordinator)는 최소 한 대 서버로부터 쓰기 성공 응답을 받아야 한다는 뜻이다.
si으로부터 성공 응답을 받았다면 sO, s2로부터의 응답은 기다릴 필요가 없다.

이처럼 N, W, R의 값을 정하는 것은 응답 지연(latency)과 데이터 일관성(consistency) 사이의 타협점을 찾는 전형적인 과정이다. W=1 또는 R=1인 구성의 경우, 중재자는 한 대 서버로부터의 응답만 받으면 되므로 응답 속도는 빠를 것이다. 반면 W나 R의 값이 1보다 큰 경우에는 시스템이 보여주는 데이터 일관성의 수준은 향상되겠지만, 중재자는 가장 느린 서버로부터의 응답까지 기다려야 하므로 전체 응답 속도는 느려질 것이다.

특히 W + R > N인 경우에는 강한 일관성(strong consistency)이 보장된다. 쓰기 연산에 참여한 서버 집합과 읽기 연산에 참여한 서버 집합이 겹치지 않을 수 없기 때문에, 일관성을 보증할 최신 데이터를 가진 노드가 최소 하나는 겹치게 되는 것이다.

일관성 모델

데이터 일관성을 위한 키-값 저장소를 설계할 때 고려해야 할 중요한 요소 중 하나.
일관성의 수준을 결정함.

일관성 모델의미
강한 일관성 (Strong Consistency)모든 읽기 연산은 가장 최근에 갱신된 결과를 반환한다. 즉, 클라이언트는 절대 낡은(out-of-date) 데이터를 보지 못한다.
약한 일관성 (Weak Consistency)읽기 연산이 가장 최근에 갱신된 결과를 반환하지 못할 수도 있다.
결과적 일관성 (Eventual Consistency)약한 일관성의 한 형태로, 갱신 결과가 즉시는 아니어도 결국에는 모든 사본에 반영(동기화)되는 모델이다.

강한 일관성은 모든 사본에 갱신이 반영될 때까지 읽기/쓰기를 막는 방식인데, 요청 처리가 멈추므로 고가용성 시스템엔 부적합하다.
그래서 다이나모, 카산드라 등은 결과적 일관성 모델을 택하며, 이번 장의 설계도 이를 따른다.

다만 결과적 일관성은 병렬 쓰기 시 데이터 불일치가 생길 수 있어, 이를 클라이언트가 버전 정보로 해결해야 한다.

비 일관성 해소 기법: 데이터 버저닝

그 기법이 바로 데이터 버저닝이다.

데이터 버저닝은 데이터를 바꿀 때마다 기존 걸 덮어쓰지 않고, 새 버전을 만드는 것이다. 그래서 각 버전은 절대 변하지 않는다.

다만 버저닝만으로는 한계가 있다. 여러 클라이언트가 동시에(병렬로) 같은 데이터를 수정하면, 사본마다 서로 다른 버전을 갖게 되는데 — 이 중 어떤 버전이 진짜 최신인지, 아니면 서로 충돌하는 버전인지 버전 번호만으로는 판단할 수 없다.

백터시계

버전들 사이의 인과관계를 파악하고 충돌 여부를 판단하는 문제를 해결하기 위해 등장한 기술이 바로 벡터 시계(vector clock)다.

D([S1,v1],[S2,v2],,[Sn,vn])D([S_1, v_1], [S_2, v_2], \dots, [S_n, v_n])

  • DD: 데이터
  • SiS_i: 그 데이터를 수정한 서버 (노드 ID)
  • viv_i: 그 서버에서 몇 번째 수정인지 나타내는 카운터 (버전)

"데이터 DDS1S_1v1v_1번, S2S_2v2v_2번... 수정했다"는 버전 이력표.


어떤 서버 SiS_i가 데이터 DD를 읽고 수정할 때:

  1. 이미 [Si,vi][S_i, v_i] 항목이 있으면
    • 그 카운터 숫자를 1 증가시킨다. (vivi+1v_i \rightarrow v_i + 1)
  2. 목록에 [Si,vi][S_i, v_i] 항목이 없으면
    • 새로 [Si,1][S_i, 1] 항목을 추가한다.

동작 예시

단계발생한 작업Vector Clock 상태설명
1S1S_1이 데이터 최초 작성D([S1,1])D([S_1, 1])S1S_1 항목 신규 추가 (규칙 2)
2S1S_1이 데이터를 한 번 더 수정D([S1,2])D([S_1, 2])S1S_1 카운터 +1 (규칙 1)
3S2S_2가 이 데이터를 가져와서 수정D([S1,2],[S2,1])D([S_1, 2], [S_2, 1])S2S_2 항목 신규 추가 (규칙 2)
4S1S_1DD를 다시 가져와서 수정D([S1,3],[S2,1])D([S_1, 3], [S_2, 1])S1S_1 카운터 +1 (규칙 1)

충돌

두 버전 DAD_ADBD_B를 비교할 때:

  • 선후 관계 있음 (정상 덮어쓰기 가능):
    한 쪽 버전의 모든 서버 카운터가 다른 쪽 버전의 카운터보다 작거나 같으면 옛날 버전임.
  • 동시 쓰기 충돌 (Conflict 발생!):
    어느 한쪽이 완벽하게 우위에 있지 않은 경우 (예: DA([S1,2],[S2,1])D_A([S_1, 2], [S_2, 1]) vs DB([S1,1],[S2,2])D_B([S_1, 1], [S_2, 2]))
    -> 시스템은 이 두 버전을 모두 남겨두고 클라이언트/어플리케이션에서 병합(Merge)하도록 처리함.

장애 처리

대규모 분산 시스템에서 장애(failure)는 예외적 사건이 아니라 매우 흔하게 발생하는 일이다.

장애 처리는 크게 두 단계로 나뉜다:
장애 감지(Failure Detection) — 어떤 서버에 문제가 생겼는지 파악하는 것
장애 해소(Failure Resolution) — 감지된 장애에 대응하는 전략. 일시적 장애처리와 영구적 장애처리로 나뉜다.

장애감지

최소 서버 2대 이상이 같은 문제를 겪어야 장애로 인식된다.

모든 노드 사이 에 멀티캐스팅(multicasting) 채널을 구축하는 것이 서버 장애를 감지하는 가장 손쉬운 방법이다.
하지만 이 방법은 서버가 많을 때는 분명 비효율적 이다.

서버가 많을 때는, 가십 프로토콜(gossip protocol) 같은 분산형 장애 감지 (decentralized failure detection) 솔루션을 채택하는 편이 보다 효율적이다.

분산형 장애감지 솔루션의 과정은 아래와 같다.

[1. Heartbeat 증가] ──> [2. Random Gossip 전파] ──> [3. 최신값 Merge 갱신] ──> [4. Timeout 장애 판정]

예시

데이터 분산과 멤버십 관리의 효율성을 극대화하기 위해,
테이블과 같은 멤버십 목록을 해시 링 위에 올려두자.
해시 링 위에 올라간 노드 s0 역시 좌측 테이블과 같은 멤버십 목록을 가진다.

이후 가십프로토콜 솔루션의 동작은 아래와 같이 일어난다.

  1. 카운터 이상 감지
  • 노드 s0s0은 노드 s2s2 (Member ID = 2)의 박동 카운터(9908)가 다른 노드들에 비해 오랫동안 증가하지 않았다는 것(Time: 11:58:02)을 발견한다.
  1. 목록 전파 (Gossip)
  • 노드 s0s0은 노드 s2s2의 박동 카운터 정보를 포함한 멤버십 목록을 무작위로 선택된 다른 노드들(예: s1,s3s1, s3)에게 주기적으로 전달한다.
  1. 장애 상태 확정 (Offline)
  • 노드 s2s2의 박동 카운터가 지정된 임계 시간 동안 계속 증가하지 않았음을 확인한 주변 노드들은 최종적으로 노드 s2s2장애(Offline) 노드로 표시한다.

장애 해소

일시적 장애 처리

가십 프로토콜로 장애를 감지한 시스템은 가용성을 보장하기 위해 필요한 조치를 해야 한다.
엄격한 정족수(strict quorum) 접근법을 쓴다면, 읽기와 쓰기 연산을 금지해야 할 것이다.

느슨한 정족수(sloppy quorum) 접근법
엄격한 정졳수 접근법을 완화하여 가용성을 높인다.
(1) 정족수 요구사항을 강제하는 대신, 쓰기 연산을 수행할 W개의 건강한 서버와 읽기 연산을 수행할 R개의 건강한 서버를 해시 링에서 고른다.
(2) 이때 장애 상태인 서버는 무시한다.

단서 후임시 위탁(hinted handoff) 기법
네트워크·서버 장애 상황에서도 가용성을 유지하면서, 복구 후 힌트를 통해 데이터 정합성을 보장한다.
(1) 장애가 발생한 서버로 향하는 요청은 다른 서버가 임시로 대신 처리한다.
(2) 이때 대리 처리한 서버는 해당 변경사항에 대한 힌트(hint)를 남겨둔다.
(3) 장애 서버가 복구되면, 힌트에 기록된 변경사항을 일괄 반영하여 데이터 일관성을 유지한다.

영구장애 처리

단서 후 임시 위탁 기법은 일시적 장애를 처리하기 위한 것이다. 영구적인 노드의 장애 상태는 어떻게 처리해야 할까?
그런 상황을 처리하기 위해 우리는 반-엔트로피(anti-entropy) 프로토콜을 구현하여 사본들을 동기화할 것이다.

반-엔트로피 프로토콜
사본들을 비교하여 최신 버전으로 갱신하는 과정.
사본 간의 일관성이 망가진 상태를 탐지하고 전송 데이터 의 양을 줄이기 위해서는 머클(Merkle) 트리를 사용함.

머클트리
각 노드에 그 자식 노드들에 보관된 값의 해시(자식노드가 종단leaf 노드인 경우), 또는 자식 노드들의 레이블로부터 계산된 해시
값을 레이블로 붙여두는 트리.

머클트리를 사용하면 대규모 자료 구조의 내용을 효과적이면서도 보안상 안전한 방법으로 검증(verification)할 수 있다.

해시트리라고도 불린다.

머클트리 동기화 과정
서버간 데이터 비교는 머클 트리의 루트 노드 해시값을 비교하는 것으로 시작하며, 값이 일치하면 같은 데이터를 가진 것으로 판단되어 동기화 작업이 중단된다.

루트 노드 해시값이 다르면 왼쪽과 오른쪽 자식 노드의 해시값을 순서대로 비교하며 트리 아래쪽으로 내려가고, 이 과정을 반복하다 보면 서로 다른 데이터를 가진 버킷을 찾아낼 수 있어 해당 버킷들만 동기화하면 된다.

예시

(1) 키 공간을 버킷으로 나눈다.


(2) 버킷에 포함된 각각의 키에 균등 분포 해시(uniform hash) 함수를 적용하여 해시 값을 계산한다

(3) 버킷별로 해시값을 계산한 후, 해당 해시 값을 레이블로 갖는 노드를 만든다

(4) 자식 노드의 레이블로부터 새로운 해시 값을 계산하여, 이진 트리를 상향식으로 구성해 나간다

머클트리 동기화 시 유의점
머클 트리를 사용하면 동기화해야 할 데이터의 양은 실제 데이터 차이의 크기에 비례할 뿐, 서버에 저장된 전체 데이터 총량과는 무관하다는 장점이 있다.
다만 실제 시스템에서는 버킷 하나의 크기가 상당히 클 수 있음을 유의해야 한다.

예를 들어 10억(1B) 개의 키를 100만(1M) 개의 버킷으로 관리한다면, 버킷 하나당 약 1,000개의 키를 담당하게 되는 셈이다.
즉, 불일치가 발견된 버킷 단위로 동기화가 이루어지므로, 버킷 크기가 크면 실제 차이가 적더라도 한 번에 동기화되는 데이터량이 늘어날 수 있다.

데이터 센터 장애 처리

데이터 센터 장애는 정전, 네트워크 장애, 자연재해 등 다양한 이유로 발생할수 있다.
데이터 센터 장애에 대응할 수 있는 시스템을 만들려면 데이터를 여러 데이터 센터 에 다중화하는 것이 중요하다.

한 데이터센터가 완전히 망가져도 사용자는 다른 데이터 센터 에 보관된 데 이터를 이용할 수 있을 것이다.

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

시스템 아키텍처 다이어그램을 그릴 때 고려해야할 점은 아래와 같다.

  • 클라이언트-저장소 통신: get(key), put(key, value)라는 단순한 API를 통해 이루어진다.
  • 중재자(coordinator): 클라이언트 요청을 받아 키-값 저장소에 대한 프락시 역할을 하는 노드.
  • 노드 분포: 안정 해시(consistent hash)의 해시 링 위에 노드들이 배치되며, 노드의 자동 추가/삭제가 가능하도록 시스템은 완전히 분산화(decentralized)되어 있다.
  • 데이터 다중화: 하나의 데이터는 여러 노드에 복제되어 저장된다.
  • SPOF 없음: 모든 노드가 동등한 책임을 지므로, 단일 장애점(Single Point of Failure)이 존재하지 않는다.

쓰기경로와 읽기경로

쓰기경로

카산드라의 사례로 쓰기 요청 이 특정 노드에 전달되면 무슨 일이 벌어지는지 확인해보자.

① 쓰기 요청이 커밋 로그(commit log) 파일에 기록된다.
② 데이터가 메모리 캐시에 기록된다.
③ 메모리 캐시가 가득차거나 사전에 정의된 어떤 임계치에 도달하면 데이터는 디스크에 있는 SSTable 에 기록된다.

※ SSTable (Sorted-String Table): <Key, Value> 순서쌍을 키 기준으로 정렬하여 관리하는 디스크 상의 불변(Immutable) 데이터 파일

읽기경로

읽기 요청을 받은 노드는 데이터가 메모리 캐시에 있는지부터 살핀다. 있는 경우에는 데이터를 즉시 클라이언트에 반환하고, 없는 경우에는 일련의 과정을 거쳐 디스크에서 결과를 반환한다.

반환할 응답이 메모리캐시가 없을 때 과정은 아래와같다.

① 데이터가 메모리에 없으므로 블룸 필터를 검사한다.
② 블룸 필터를 통해 어떤 SSTable에 키가 보관되어 있는지 알아낸다.
③ SSTable에서 데이터를가져온다.
④ 해당 데이터를 클라이언트에게 반환한다.


요약

목표 / 문제기술 / 해결 방법
대규모 데이터 저장안정 해시
안정 해시를 사용해 서버들에 부하 분산안정 해시
읽기 연산에 대한 높은 가용성 보장데이터 다중화
데이터를 여러 데이터센터에 다중화여러 데이터센터에 걸친 데이터 다중화
쓰기 연산에 대한 높은 가용성 보장버저닝 및 벡터 시계를 사용한 충돌 해소
데이터 파티션안정 해시
점진적 규모 확장성안정 해시
다양성(heterogeneity)안정 해시
조절 가능한 데이터 일관성정족수 합의(quorum consensus)
일시적 장애 처리느슨한 정족수 프로토콜(sloppy quorum)과 단서 후 임시 위탁(hinted handoff)
영구적 장애 처리머클 트리(Merkle tree)
데이터센터 장애 대응여러 데이터센터에 걸친 데이터 다중화
profile
양치기소녀

0개의 댓글