튜토리얼 목록

키벨류저장소

DDX LAB·1주 전
섹션 01

키-값 저장소를 처음 이해하기

키-값 저장소가 무엇인지 일상적인 비유와 기본 API를 통해 이해합니다.

키-값 저장소는 고유한 키를 이용해 원하는 값을 빠르게 찾는 데이터베이스입니다. 사물함 번호를 알면 해당 사물함 안의 물건을 바로 꺼낼 수 있는 구조와 비슷합니다. 데이터 사이의 복잡한 관계보다 특정 키로 빠르게 저장하고 조회하는 데 집중합니다.

빠른 조회가 필요하고 데이터 사이의 복잡한 관계를 자주 계산하지 않을 때 적합합니다. 로그인 세션, 캐시, 사용자 설정, 장바구니, 조회 수 같은 데이터에 많이 사용됩니다. 하나의 키를 기준으로 데이터를 통째로 읽고 쓰는 사용 사례와 잘 맞습니다.

섹션 02

키와 값 설계하기

좋은 키를 만드는 방법과 값의 크기 및 구조를 정하는 기준을 살펴봅니다.

키는 데이터의 의미를 알아볼 수 있으면서도 유일하게 만들어야 합니다. 예를 들어 user:145:profile처럼 종류, 식별자, 용도를 구분해 작성할 수 있습니다. 너무 긴 키는 저장 공간과 네트워크 비용을 늘리므로 적절한 길이를 유지해야 합니다.

기술적으로 큰 값도 저장할 수 있지만 일반적인 키-값 저장소에서는 작은 값을 권장합니다. 값이 커질수록 네트워크 전송, 복제, 메모리 사용량과 응답 지연이 함께 증가합니다. 이미지나 영상은 객체 저장소에 보관하고 키-값 저장소에는 주소만 저장하는 방식이 일반적입니다.

섹션 03

단일 서버에서 시작하기

한 대의 서버로 키-값 저장소를 구현하는 방법과 단일 서버가 가진 한계를 이해합니다.

가장 단순한 방법은 메모리에 해시 테이블을 두는 것입니다. put 요청이 오면 해시 테이블에 값을 넣고 get 요청이 오면 키를 통해 값을 찾습니다. 구현은 간단하지만 메모리 용량과 서버 장애라는 한계가 있습니다.

데이터가 서버의 디스크나 메모리 용량을 넘거나 요청량이 CPU와 네트워크 처리 능력을 넘을 때 한계가 옵니다. 서버 장애가 곧 전체 서비스 장애로 이어진다는 점도 중요한 한계입니다. 더 큰 서버로 교체할 수 있지만 비용과 물리적인 상한이 있습니다.

섹션 04

여러 서버를 사용할 때 발생하는 네트워크 문제와 일관성·가용성의 선택을 이해합니다.

여러 서버를 사용할 때 발생하는 네트워크 문제와 일관성·가용성의 선택을 이해합니다.

분산 키-값 저장소는 데이터를 여러 서버에 나누어 저장하고 필요하면 같은 데이터를 여러 서버에 복제하는 시스템입니다. 서버를 추가해 저장 용량과 처리량을 늘릴 수 있고 일부 서버가 고장 나도 서비스를 계속 운영할 수 있습니다. 대신 서버 간 통신과 데이터 동기화가 필요해 설계가 복잡해집니다.

네트워크가 끊긴 상황에서는 최신 데이터만 제공할지, 일부 데이터가 오래되더라도 계속 응답할지 선택해야 한다는 의미입니다. 최신 데이터만 고집하면 일부 요청을 거절해야 하고, 계속 응답하려면 잠시 다른 값을 보여줄 수 있습니다. 이것이 일관성과 가용성 사이의 핵심 선택입니다.

섹션 05

일관성을 우선하는 {tip:CP}(Consistency and Partition Tolerance · 네트워크 파티션 상황에서 일관성을 우선하는 시스템)와 가용성을 우선하는 {tip:AP}(Availability and Partition Tolerance · 네트워크 파티션 상황에서 가용성을 우선하는 시스템) 시스템의 차이를 실제 사례로 이해합니다.

일관성을 우선하는 CP와 가용성을 우선하는 AP 시스템의 차이를 실제 사례로 이해합니다.

CP 시스템은 네트워크 파티션 중에도 데이터 일관성을 지키기 위해 일부 읽기나 쓰기를 거절합니다. 최신 상태를 확인할 수 없다면 잘못된 값을 반환하지 않고 오류를 반환하는 방식입니다. 계좌 잔액이나 재고처럼 틀린 값이 큰 문제를 만드는 데이터에 적합합니다.

AP 시스템은 네트워크 파티션이 발생해도 가능한 서버에서 계속 요청을 처리합니다. 이 과정에서 서로 다른 서버에 다른 값이 저장될 수 있지만 나중에 충돌을 해결해 하나의 상태로 수렴시킵니다. 서비스 중단보다 지속적인 응답이 중요한 경우 적합합니다.

섹션 06

데이터를 여러 서버에 나누어 저장하는 방법과 {tip:안정 해시}(서버 수가 변할 때 키 재배치를 최소화하는 해시 방식)가 필요한 이유를 학습합니다.

데이터를 여러 서버에 나누어 저장하는 방법과 안정 해시가 필요한 이유를 학습합니다.

단일 서버에 전체 데이터를 담기 어려워지면 데이터 파티션이 필요합니다. 데이터 파티션은 전체 데이터를 여러 조각으로 나누어 서로 다른 서버에 저장하는 것입니다. 한 서버가 모든 데이터를 보관하지 않으므로 저장 용량과 요청 처리를 여러 서버에 분산할 수 있습니다. 책을 여러 책장에 나누어 꽂는 것과 비슷합니다.

일반적인 나머지 연산은 서버 수가 변할 때 대부분의 키가 다른 서버로 이동하는 문제가 있습니다. 안정 해시를 사용하면 새 서버 주변의 데이터만 이동합니다. 서버를 자주 추가하거나 제거하는 시스템에서 재배치 비용을 크게 줄일 수 있습니다.

섹션 07

{tip:가상 노드}(물리 서버가 담당하는 해시 공간상의 논리적 위치)가 부하 분산과 서로 다른 성능의 서버를 지원하는 방법을 이해합니다.

가상 노드가 부하 분산과 서로 다른 성능의 서버를 지원하는 방법을 이해합니다.

아닙니다. 여기서 가상 노드는 해시 링 위에 표시되는 논리적인 위치를 뜻합니다. 하나의 물리 서버가 여러 개의 가상 노드 위치를 담당할 수 있습니다.

새 서버의 가상 노드 위치가 해시 링에 추가되고 인접 서버가 담당하던 일부 키를 새 서버로 이동합니다. 모든 데이터를 다시 배치하지 않고 새 서버가 맡게 된 범위만 이동합니다. 데이터 이동 중에도 기존 서버가 요청을 처리하도록 점진적으로 진행할 수 있습니다.

섹션 08

데이터 복제와 다중화

같은 데이터를 여러 서버와 데이터 센터에 복제하는 이유와 복제본 배치 방법을 알아봅니다.

파티션은 서로 다른 데이터를 여러 서버에 나누는 것이고 복제는 같은 데이터를 여러 서버에 복사하는 것입니다. 파티션만 사용하면 특정 조각을 담당하는 서버가 장애를 일으킬 때 해당 데이터에 접근하지 못하거나 데이터를 잃을 수 있으므로, 같은 데이터를 여러 서버에 복제해야 합니다. 파티션은 용량과 처리량을 늘리고 복제는 장애 대응과 읽기 가용성을 높입니다. 실제 분산 저장소는 두 방식을 함께 사용합니다.

키가 배치된 서버부터 해시 링을 따라가며 서로 다른 물리 서버를 선택합니다. 복제본 수를 나타내는 {tip:N}(전체 복제본 수를 나타내는 값)3이면 일반적으로 담당 서버와 그다음 두 서버에 저장할 수 있습니다. 같은 랙이나 데이터 센터에만 몰리지 않도록 위치 정보도 고려해야 합니다.

섹션 09

{tip:정족수}(작업 성공을 판단하기 위해 필요한 최소 응답 수)와 읽기·쓰기 성공 기준

N, W, R의 의미와 응답 속도 및 데이터 일관성 사이의 관계를 이해합니다.

복제된 데이터는 갱신 시점이나 장애로 인해 복제본 사이의 값이 어긋날 수 있으므로 N·W·R 정족수로 읽기와 쓰기의 성공 기준을 정합니다. N은 하나의 데이터를 저장하는 전체 복제본 수입니다. W는 쓰기를 성공으로 판단하기 위해 필요한 서버 응답 수이고 R은 읽기를 성공으로 판단하기 위해 필요한 서버 응답 수입니다. 이 값을 조절해 응답 속도와 일관성 수준을 바꿀 수 있습니다.

빠른 읽기가 중요하면 R을 작게 하고 빠른 쓰기가 중요하면 W를 작게 설정할 수 있습니다. W+R>N이 되면 읽기와 쓰기 집합이 최소 하나의 복제본에서 겹칩니다. 다만 정족수 조건만으로 모든 형태의 강한 일관성이 자동 보장되는 것은 아닙니다.

섹션 10

일관성 모델과 오래된 데이터

강한 일관성, 약한 일관성, 최종 일관성을 초보자 관점에서 구분합니다.

강한 일관성은 쓰기가 성공한 뒤 발생하는 읽기가 항상 최신 값을 반환하는 성질입니다. 사용자는 이미 저장한 값이 이전 값으로 되돌아간 것처럼 보지 않습니다. 이를 보장하려면 여러 복제본의 상태를 확인해야 하므로 지연시간이나 가용성을 희생할 수 있습니다.

맞습니다. 새로운 쓰기가 더 이상 발생하지 않고 네트워크가 정상이라면 모든 복제본이 결국 같은 값으로 수렴합니다. 수렴하기 전에는 서버에 따라 다른 값이나 오래된 값이 보일 수 있습니다.

섹션 11

동시 쓰기와 버전 충돌

서로 다른 서버에서 같은 키를 동시에 수정할 때 충돌이 발생하는 이유와 해결 방법을 살펴봅니다.

두 요청이 서로의 변경을 확인하지 못한 채 다른 복제본에 저장되면 두 개의 유효한 버전이 생길 수 있습니다. 한 값을 무조건 덮어쓰면 다른 사용자의 변경이 사라질 수 있습니다. 시스템은 버전 관계를 기록하고 충돌을 탐지해야 합니다.

여러 서버에서 동시에 쓰기가 발생하면 실제 시간이나 단순한 버전 번호만으로 무엇이 최신인지 판단하기 어렵습니다. 벡터 시계는 각 버전이 어느 서버의 변경을 알고 있는지 기록해 변경의 선후 관계와 충돌을 감지합니다. 이를 비교하면 한 버전이 다른 버전의 후속인지, 서로 독립적으로 만들어진 충돌 버전인지 판단할 수 있습니다. 실제 시간을 기록하는 시계라기보다 서버별 변경 횟수를 기록한 목록에 가깝습니다.

섹션 12

장애 감지와 가십 프로토콜

분산 시스템이 서버 장애를 판단하는 과정과 가십 프로토콜의 역할을 이해합니다.

장애 대응을 시작하려면 먼저 어떤 노드를 사용할 수 없는지 판별해야 합니다. 각 서버는 가십 프로토콜을 통해 자신과 다른 서버의 상태 정보를 주기적으로 공유하고, 다른 서버들은 이 정보를 관찰합니다. 일정 시간 동안 정보가 갱신되지 않으면 장애가 의심된다고 판단합니다. 한 서버의 판단만으로 즉시 장애를 확정하지 않고 여러 서버의 관찰을 함께 사용해 일시적인 네트워크 지연이나 단절로 인한 오판을 줄입니다.

가십 프로토콜은 각 노드가 자신이 가진 멤버십 정보를 무작위 노드와 조금씩 교환하는 방식입니다. 사람들이 소문을 전달하듯 여러 번의 교환을 거쳐 클러스터 전체에 상태가 퍼집니다. 중앙 관리 서버 없이도 큰 규모로 확장하기 쉽습니다.

섹션 13

일시적 장애와 느슨한 정족수

잠시 사용할 수 없는 서버를 우회하고 복구 후 데이터를 전달하는 방법을 알아봅니다.

가용성을 우선하는 시스템에서는 잠시 사용할 수 없는 복제 서버 때문에 쓰기를 중단하지 않습니다. 원래 복제 서버가 아니더라도 현재 접근 가능한 정상 서버로 느슨한 정족수를 구성하고, 해당 서버가 데이터를 임시로 저장합니다. 장애 서버가 복구되면 임시 저장한 데이터를 원래 복제 서버에 전달합니다.

단서 후 임시 위탁(hinted handoff)은 정상 서버가 장애 서버 대신 데이터를 임시로 보관하는 방식입니다. 데이터와 함께 원래 어느 서버에 전달해야 하는지에 대한 단서를 기록합니다. 원래 서버가 복구되면 임시 저장을 맡은 서버가 단서를 보고 데이터를 전달합니다.

섹션 14

영구적 장애와 머클 트리

복제본의 차이를 효율적으로 찾아내고 필요한 데이터만 동기화하는 방법을 학습합니다.

복구된 서버와 정상 서버의 데이터를 비교해 누락되거나 오래된 값을 찾아야 합니다. 장기 장애가 발생할 때마다 전체 데이터를 다시 복사하면 비용이 크므로, 구간별 해시를 계층적으로 정리한 머클 트리를 사용합니다. 머클 트리에서 해시가 다른 가지만 찾아 필요한 데이터만 부분 동기화할 수 있습니다.

먼저 두 트리의 루트 해시를 비교합니다. 루트가 다르면 자식 해시를 비교하고, 다른 자식만 계속 따라 내려갑니다. 마지막에는 실제로 다른 키가 포함된 버킷만 찾아 동기화합니다.

섹션 15

쓰기 경로 이해하기

클라이언트의 쓰기 요청이 커밋 로그, 메모리 캐시, SSTable을 거치는 과정을 살펴봅니다.

쓰기 요청은 커밋 로그→멤테이블→SSTable 순서로 처리됩니다. 먼저 쓰기 내용을 커밋 로그에 기록하고 멤테이블에 반영합니다. 멤테이블이 일정 크기에 도달하면 데이터를 정렬해 디스크의 SSTable 파일로 저장합니다. 로그를 먼저 기록하기 때문에 서버가 갑자기 종료되어도 재시작 후 복구할 수 있습니다.

SSTable은 메모리의 데이터를 정렬된 상태로 디스크에 저장하는 구조입니다. 기존 파일을 직접 수정하지 않고 새로운 파일을 추가하므로 순차 쓰기에 유리합니다. 이후 여러 SSTable은 컴팩션 과정에서 하나로 병합됩니다.

섹션 16

메모리에 없는 키를 여러 SSTable에서 효율적으로 찾는 방법을 이해합니다. {tip:블룸 필터}(키가 확실히 없음을 빠르게 판별하지만 존재 여부에는 오탐이 가능한 확률적 자료구조)

메모리에 없는 키를 여러 SSTable에서 효율적으로 찾는 방법을 이해합니다.

읽기 요청은 멤테이블→블룸 필터와 인덱스→후보 SSTable 순서로 처리됩니다. 먼저 멤테이블에서 키를 찾고, 없으면 블룸 필터와 인덱스로 키가 있을 가능성이 있는 SSTable을 추립니다. 후보 SSTable에서 최신 버전을 읽어 클라이언트에 반환합니다.

블룸 필터는 디스크 파일을 열어보기 전에 사용하는 간단한 검사 장치입니다. 없다고 판단하면 실제 파일을 조회하지 않아도 됩니다. 있다고 판단해도 실제로는 없을 수 있으므로 최종 확인은 필요합니다.

섹션 17

컴팩션과 저장 공간 관리

여러 SSTable을 병합하고 오래된 데이터를 정리하는 컴팩션을 이해합니다.

새로운 쓰기가 계속 SSTable로 추가되면 같은 키의 여러 버전과 삭제 표시가 쌓입니다. 컴팩션은 여러 SSTable을 병합해 최신 값만 남기고 오래된 값과 불필요한 데이터를 제거합니다. 파일 수가 줄어 읽기 성능과 저장 공간 사용량이 개선됩니다.

하나의 논리적인 쓰기가 로그, 메모리 플러시, 복제, 컴팩션을 거치며 디스크에 여러 번 기록되는 현상입니다. 쓰기 증폭이 크면 디스크 수명과 처리량에 영향을 줄 수 있습니다. 저장 엔진은 컴팩션 전략을 조절해 이를 관리합니다.

섹션 18

중재자와 전체 아키텍처

클라이언트 요청을 받아 담당 노드를 선택하고 복제 요청을 조정하는 중재자의 역할을 이해합니다.

반드시 별도의 서버일 필요는 없습니다. 클라이언트 요청을 처음 받은 일반 저장 노드가 해당 요청에 대해 중재자 역할을 할 수 있습니다. 전용 프록시 서버가 중재자 역할만 수행하는 구조도 가능합니다.

특정한 마스터 서버 하나에 모든 기능을 의존하지 않고 각 노드가 요청 처리, 복제, 장애 감지, 저장 기능을 수행할 수 있다는 뜻입니다. 한 노드가 사라져도 다른 노드가 역할을 이어받을 수 있습니다. 이를 통해 단일 장애 지점을 줄일 수 있습니다.

섹션 19

데이터 센터 장애와 지역 복제

정전과 네트워크 단절 등 데이터 센터 전체 장애에 대응하는 방법을 알아봅니다.

복제 서버가 모두 같은 데이터 센터에 있다면 데이터 센터 전체 장애에는 안전하지 않습니다. 정전, 네트워크 단절, 자연재해가 발생하면 모든 복제본을 동시에 사용할 수 없게 됩니다. 중요한 데이터는 여러 지역이나 데이터 센터에 분산해야 합니다.

지역 간 거리가 멀수록 복제 지연이 커질 가능성이 있습니다. 장애가 발생한 직후 다른 지역으로 전환하면 최신 쓰기 일부가 아직 전달되지 않았을 수 있습니다. 복구 시점 목표와 데이터 손실 허용 범위를 미리 정해야 합니다.

섹션 20

설계 선택의 감 잡기

초보 개발자가 요구사항에 따라 일관성, 가용성, 성능의 우선순위를 선택하는 방법을 정리합니다.

어떤 키로 데이터를 조회하는지, 데이터가 얼마나 큰지, 오래된 값을 잠시 보여줘도 되는지를 먼저 확인해야 합니다. 서버 장애 중에도 요청을 계속 처리해야 하는지와 데이터 손실을 어느 정도 허용하는지도 중요합니다. 기술을 먼저 선택하기보다 서비스의 허용 가능한 실패를 정의해야 합니다.

한 대에 모든 데이터를 담을 수 없으므로 안정 해시의 링과 가상 노드로 데이터를 파티션합니다. 그러나 담당 서버가 죽으면 데이터가 사라질 수 있어 서로 다른 물리 서버 N대에 복제합니다. 복제본 사이의 값이 어긋날 수 있으므로 W개의 쓰기 확인과 R개의 읽기 응답으로 정족수를 구성하며, W + R > N이면 두 집합이 겹쳐 최신값을 읽을 수 있습니다. 동시에 쓰기가 발생해 최신값을 단정하기 어려울 때는 벡터 시계로 충돌을 감지하고 해결은 클라이언트에 맡깁니다. 이러한 장애 대응을 시작하려면 먼저 가십 프로토콜로 여러 노드의 관측을 모아 서버 장애를 판정합니다. 일시 장애에는 느슨한 정족수와 단서 후 임시 위탁으로 쓰기를 이어가고, 영구 장애에는 머클 트리로 차이가 있는 가지만 찾아 부분 동기화합니다. 마지막으로 쓰기 경로는 커밋 로그에서 멤테이블을 거쳐 SSTable로 이어지고, 읽기 경로는 멤테이블을 먼저 확인한 뒤 블룸 필터로 불필요한 SSTable 탐색을 줄입니다.