이번주는 천사와 함께 북스터디 세션 진행을 맡게 되었으며, 특히 7장을 도맡을 예정이다.

백엔드 프로그래밍을 하며 관계형 DB를 사용할 때, 데이터의 고유값을 위한 유일키(unique key)를 어떻게 만들고 있는가?
관계형 DB의 auto-increment 기능 사용이라는 편리한 방식이 있지만 분산 환경에서는 한계가 있다.

예를 들어 사용자가 영상 URL이나 영상 파일을 서버에 업로드한다고 하자. 같은 URL이나 영상이 여러 번 업로드될 수 있으므로, 각 요청(작업)을 구분할 고유값이 필요하다. 다만 분산 시스템에서는 여러 서버가 동시에 ID를 생성하기 때문에 순차적인 ID가 필요하거나 생성 순서를 보장해야 하는 경우에는 단순한 자동 증가 방식으로 처리하기 어렵다. 서버 간 중복이나 순서 문제를 방지하기 위해 분산 환경에 적합한 ID 생성 전략이 필요하다.

DB서버 1대를 쓸 경우: 사용자가 많아지면 DB 서버 1대로는 모든 요청을 감당하지 못해 과부하가 걸린다.

DB서버 여러대를 쓸 경우:
DB 서버를 여러 대(A, B)로 늘리면, 각 서버가 중복된 ID를 만들어낼 수 있다.
이를 (중복된 ID 생성) 막으려고 여러 DB 서버끼리 통신하며 확인하는 절차에서 응답 속도가 심각하게 느려진다.

그러니 유일성이 보장되는 아이디 생성에 대해 생각해보자.


1단계 문제 이해 및 설계 범위 확정

시스템 설계 면접 문제를 푸는 첫 단계는 면접관에게 질문을 던져 문제의 모호함을 명확히하고 설계 방향을 정하는 것이다.

예시
지원자: ID는 어떤 특성을 갖나요?
면접관: ID는 유일해야 하고, 정렬 가능해야 합니다.
지원자: 새로운 레코드에 붙일 ID는 항상 1만큼 큰 값이 어야 하나요?
면접관: ID의 값은 시간이 흐름에 따라 커질 테지만 언제나 1씩 증가한다고 할수는 없습니다. 
다만 확실한 것은, 아침에 만든 ID보다는 저녁에 만든 ID가 큰 값을 갖는다는 점 입 니다.
지원자: ID는 숫자로만 구성되나요?
면접관: 그렇습니다.
지원자: 시스템 규모는 어느 정도입니까?
면접관: 초당 10,000 ID를 생성할 수 있어야 합니다.

질문을 통해 도출된 면접관의 요구사항은 아래와 같다.

  • ID는 반드시 유일해야 함.
  • ID는 숫자로만 구성되어야 함.
  • ID는 64비트로 표현 가능한 값이어야 함.
  • 값이 정확히 1씩 증가하지는 않지만, 발급 시각(시간) 순서에 따라 정렬 가능해야 함 (= 늦게 생성된 ID일수록 큰 값을 가짐).
  • 시스템 규모는 초당 10,000개의 ID를 생성할 수 있어야 함.

이처럼 면접관에게 질문을 던짐으로써 분산 시스템을 위한 유일 ID 생성기 설계 문제의 요구사항을 파악하고, 모호한 부분을 명확히 할 수 있었다.
이는 이후 개략적 수치 추정 및 상세 설계에 도움이 될 것이다.


2단계 개략적 설계안 제시 및 동의 구하기

분산 시스템에서 유일성이 보장되는 ID를 만드는 방법은 여러가지.

다중 마스터 복제

여러 DB 서버가 동시에 데이터를 생성할 수 있게 하고, 각 서버가 서로 겹치지 않는 ID를 생성하도록 하는 방식

모든 DB 서버는 auto_increment 즉 ID 자동증가 기능을 사용하게 함.
아이디 자동증가를 할 때, 최초가 아닌 다음 서버 증가값은 +1이 아닌 서버 대수 만큼 증가시키는 방식.

장점
여러 서버가 동시에 ID를 생성할 수 있으므로 단일 서버에서 ID를 생성하는 것보다 초당 생성 가능한 ID 수를 늘릴 수 있다.

단점
(1) 여러 데이터 센터에 걸쳐 규모를 늘리기 어렵다.

  • 서버가 여러 데이터 센터에 흩어져 있으면, 모든 서버가 동일한 서버 수와 증가 규칙을 실시간으로 공유하기 어렵다.

(2) ID의 유일성은 보장되지만, DB 서버별 처리 속도 차이로 인해 ID 값이 생성된 순서대로 정렬되지 않을 수 있다.

  • 즉, 시간 흐름에 맞추어 ID 값이 항상 커진다는 것까지는 보장할 수 없어 "늦게 생성된 ID일수록 더 큰 값을 가져야 한다"는 요구사항을 충족하기 어렵다.

(3) 서버를 추가하거나 삭제할 때도 잘 동작하도록 만들기 어렵다.

  • 서비스를 운영하면서 서버 수를 유동적으로 조절하려고 하면 그때마다 모든 서버의 설정(증가폭, 시작값)을 동기화해서 바꿔야 하고, 그 과정에서 ID 충돌이나 일시적 장애가 발생하기 쉽다

UUID( Universally Unique Identifier)

네트워크 상에서 중복되지 않는 고유한 값을 만들기 위한 128비트 표준 규약이다.

# 형식 : 09c93e62-50b4-468d-bf8a-c07el040bfb2

장점
(1) UUID는 서버 간 조율 없이 독립적으로 생성 가능.

  • 서버 간 조율로 인한 문제 발생 하지 않음. (EX. DB 서버별 처리 속도 차이로 인한 문제

(2) 중복 된 값이 생겨 충돌할 가능성이 지극히 낮음.

  • 위키피디아에 의하면, 중복 UUID가 1 개 생길 확률을 50%로 끌어 올리려면 초당 10억 개의 UUID를 100년 동안 계속해서 만들어야 한다.

단점
(1) ID가 128비트로 길다.

  • 문제의 요구사항은 64비트다.

(2) ID를 시간순으로 정렬할 수 없다.

  • 문제 요구 사항에 맞지 않음.

(3) ID에 숫자(numeric) 아닌 값이 포함될 수 있다.

  • 문제 요구 사항에 맞지 않음.

티켓 서버 (ticket server)

이 접근법은 auto_increment 기능을 갖춘 데이터베이스 서버, 즉 티켓 서버(Ticket Server)를 중앙 집중형으로 하나만 두는 방식이다.
티켓 서버(Ticket Server) 는 오직 "유일한 ID(티켓)를 발급하는 일"만 전담하며, 다른 애플리케이션 서버들은 ID가 필요할 때마다 이 티켓 서버에 요청을 보내 값을 받아온다.

장점
(1) 유일성이 보장되는, 오직 숫자로만 구성된 ID를 쉽게 만들 수 있다.

  • auto_increment 기능을 가진 서버 단 하나에서만 ID를 발급하므로, 여러 서버가 동시에 ID를 만들면서 생기는 충돌 위험이 원천적으로 없다.

(2) 구현하기 쉽고, 중소 규모 애플리케이션에 적합하다.

  • 별도의 증가 규칙 조정이나 조율 로직 없이, DB 서버 한 대에 auto_increment 컬럼만 두면 되므로 개발·운영 난이도가 낮다.

단점
(1) 티켓 서버가 SPOF(Single Point of Failure)가 된다.

  • 이 서버에 장애가 발생하면 이 서버를 이용하는 모든 시스템이 영향을 받는다. 이를 피하려면 티켓 서버를 여러 대 준비해야 하는데, 그렇게 하면 데이터 동기화 같은 새로운 문제가 발생한다.

※ SPOF(Single Point of Failure) : 시스템을 구성하는 여러 요소 중, 그 부분 하나가 고장 나면 시스템 전체(또는 서비스 전체)가 멈춰버리는 지점. 단일장애지점이라고도 불림.


트위터 스노플레이크(twitter snowflake) 접근법

전체 64비트 ID 구조를 의미 있는 여러 개의 절(section)로 쪼개어 처리하는 각개 격파 전략(Divide and Conquer)을 사용함.

각개 격파 전략(Divide and Conquer)

생성해야 하는 ID의 구조를 여러 절(section)로 분할하는 것.
비트 분할 비율은 시스템의 요구사항과 비즈니스 특성에 따라 다르게 설계한다.
스노플레이크의 비트 분할 비율은 아래와 같다.

스노우플레이크 ID(64비트)는 시간·위치·순번 충돌을 각각 방어하도록 비트를 쪼갠 구조이며, 필드 구성은 시스템마다 다르게 설계될 수 있다.

필드비트방어하는 충돌
사인 비트1(예비, 미사용)
타임스탬프41시간 축 충돌 → 정렬 가능성 확보
데이터센터 ID5지역(데이터센터) 간 충돌
서버 ID5같은 데이터센터 내 서버 간 충돌
일련번호12같은 서버·같은 밀리초 내 충돌

장점
모든 요구사항을 만족하면서도 분산 환경에서 규모 확장이 가능

단점
(1) 비트 수 제약으로 인한 확장 한계

  • 데이터센터/서버/일련번호에 배분된 비트 수는 설계 시점에 고정되므로, 운영 중에 서버 수가 예상보다 많아지거나 밀리초당 발급량이 늘어나도 비트 구조를 유연하게 바꾸기 어렵다.

(2) 데이터센터/서버 ID를 수동으로 관리해야 함

  • 각 서버에 고유 ID를 미리 배정하고 충돌 없이 관리해야 하므로 운영 부담이 있다.

3단계 상세 설계

유일성, 숫자로만 구성, 64비트 표현, 시간순 정렬 가능성, 초당 1만 개 이상 생성까지 앞서 도출된 요구사항을 전부 충족하는 유일한 방식,,,
트위터 스노플레이크 접근법으로 상세 설계를 해보자.

타임스탬프

타임스탬프는 유한한 비트 수로 표현되므로, 시간이 흐르면 언젠가 표현 가능한 값의 최댓값을 넘어서는 오버플로우가 발생할 수밖에 없다.

동일 서버에서 같은 밀리초에 여러 ID가 생성되면 타임스탬프와 서버 정보가 중복되어 충돌이 발생한다.

타임스탬프는 41비트로 표현되는데, 이 비트가 담을 수 있는 값에는 최댓값이 있다(2⁴¹-1 밀리초).
이를 연 단위로 환산하면 대략 69년이다. 즉, 69년이 지나면 타임스탬프가 41비트로 담을 수 있는 최댓값을 넘어서 오버플로우(overflow)가 발생한다.

보통은 유닉스 시간(Unix Time) 기준에 따라 1970년 1월 1일을 타임스탬프의 시작점(기원 시각, epoch)으로 삼지만, 트위터는 이 69년을 최대한 오래 사용하기 위해 현재에 가까운 시점(2010년 11월 4일)을 기원 시각으로 잡았다.
69년짜리 카운트다운의 시작을 늦춰서, 오버플로우가 발생하는 시점도 뒤로 미룬 것이다.

하지만 이는 근본적인 해결책이 아니므로, 결국 69년이 지나면 기원 시각을 다시 현재에 맞게 재설정하거나 ID 체계 자체를 새로운 방식으로 이전(migration)해야 한다.

타임스탬프에는 이런 장기적인 오버플로우 문제 외에도, 더 짧은 시간 단위에서 발생하는 문제가 하나 더 있다.
밀리초 단위로만 기록되기 때문에, 동일 서버에서 같은 밀리초에 여러 ID가 생성되면 타임스탬프와 서버 정보가 중복되어 충돌이 발생한다는 점이다.

※ 컴퓨터가 처리할 수 있는 숫자나 메모리 공간의 최대 범위를 넘어서는 현상


일련번호

타임스탬프의 중복 문제를 해결할 수 있는 방법.

일련번호는 12비트라서 2¹² = 4096가지 값(0~4095)을 가질 수 있다.

각 서버는 자기만의 카운터로 이 값을 관리하며, 같은 밀리초 안에 ID를 여러 개 만들 때마다 1씩 증가시킨다(0, 1, 2...). 밀리초가 바뀌면 다시 0으로 초기화된다.

순서타임스탬프(예시)서버ID일련번호
1번째 ID1586451091225 // x서버A0
2번째 ID1586451091225 // x서버A1
3번째 ID1586451091225 // x서버A2
다음 밀리초 첫 ID1586451091226 // x + 1서버A0 (리셋)

서로 다른 서버끼리 일련번호가 같아도 문제없다. 데이터센터ID·서버ID가 이미 서버를 구분해주기 때문이다.

타임스탬프서버ID일련번호
서버 A의 ID1586451091225A0
서버 B의 ID1586451091225B0

4단계 마무리

이번 장에서는 유일성이 보장되는 ID 생성기 구현에 쓰일 수 있는 네 가지 전략을 살펴보았다.
이 중 우리가 최종적으로 선택한 방식은 스노우플레이크로, 모든 요구사항을 만족하면서도 분산 환경에서 규모 확장이 가능했기 때문이다.
설계를 마치고 시간이 남았다면, 면접관과 다음과 같은 주제를 추가로 논의해볼 수 있을 것 이다.

주제이유
시계 동기화(clock synchronization)이번 설계는 모든 ID 생성 서버가 같은 시계를 쓴다고 가정했지만, 서버가 여러 코어나 여러 물리 장비에서 실행되면 이 가정이 깨질 수 있음. NTP(Network Time Protocol, 네트워크상의 여러 장비들의 시계를 하나의 기준 시각에 맞춰 동기화하는 표준 프로토콜)가 대표적 해결 수단
각 절(section)의 길이 최적화동시성이 낮고 수명이 긴 애플리케이션이라면 일련번호 비트를 줄이고 타임스탬프 비트를 늘리는 등 상황에 맞게 조정 가능
고가용성(high availability)ID 생성기는 필수 불가결(mission critical, 시스템 운영에 반드시 있어야 하며 장애 시 서비스 전체에 심각한 영향을 미치는 핵심 요소)한 컴포넌트이므로 매우 높은 가용성이 요구됨
※ **NTP(Network Time Protocol)**: 네트워크로 연결된 여러 컴퓨터(서버)들의 시계를 표준 기준 시각(주로 원자시계 기반)에 맞춰 자동으로 동기화해주는 프로토콜입니다. 서버마다 내부 시계가 조금씩 오차가 나는 문제(clock drift)를 주기적으로 보정해주는 역할을 한다.
profile
양치기소녀

0개의 댓글