Foundry
분산 ID 생성기 설계
중급
핵심

요구사항과 포기할 순서

같은 밀리초 안 순서를 포기하면 조율이 사라진다

ID 생성기는 만드는 것이 쉬워 보여서 요구사항을 건너뛰기 쉽습니다. 그런데 어떤 순서를 포기하는지에 따라 설계가 완전히 달라집니다.

항목
대상공용 ID 발급
ID 형식64비트 정수
발급량피크 초당 10만
발급기64대
발급 지연상위 1퍼센트가 1ms 이내
수명최소 30년
정렬대략 시간순으로 커진다
같은 밀리초 안순서를 보장하지 않는다
중복절대 허용하지 않는다

기능 요구사항과 범위 밖

구분내용
이번에 만든다ID 발급, 여러 서비스가 함께 쓰기, 발급기 추가와 제거
범위 밖빈틈 없는 연속 번호, ID로 발급 시각을 정확히 되돌리기, 문자열 ID, 발급 이력 조회

빈틈 없는 연속 번호를 뺀 것이 이 설계의 출발점입니다. 1, 2, 3처럼 빈틈이 없어야 한다면 모든 발급이 한 곳을 지나야 하고, 그러면 그곳이 처리량 상한이자 장애점이 됩니다. 빈틈을 허용하면 각 노드가 스스로 만들 수 있습니다.

왜 정렬이 필요한가

시간순으로 커지는 ID 는 인덱스 끝에만 붙고 무작위 ID 는 전체에 흩어져 닿는다 시간순으로 커지는 ID 닿는 곳이 끝 한 군데다. 그 페이지만 메모리에 있으면 된다 무작위 ID 전체에 흩어진다. 매 삽입이 다른 페이지를 건드린다 하루 86억 건이면 이 차이가 디스크 접근 수를 정한다 그래서 정렬은 취향이 아니라 저장 비용 요구사항이다

ID는 대개 저장소의 기본 키가 됩니다. 그 값이 시간순으로 커지면 새 행이 인덱스의 한쪽 끝에만 붙습니다. 그 부분만 메모리에 있으면 되므로 삽입이 값쌉니다.

무작위 값이면 삽입 위치가 전체에 흩어져 매번 다른 페이지를 건드립니다. 하루 86억 건 규모에서 이 차이가 디스크 접근 수를 정합니다.

정렬은 취향이 아니라 저장 비용 요구사항입니다. 이 이유를 말하지 못하면 왜 무작위 방식을 안 쓰는지 설명할 수 없습니다.

왜 64비트 인가

수명 30년 에 하루 86억 건이면 총 발급량이 약 9경 4천조입니다. 64비트 정수가 담는 범위가 그보다 훨씬 크므로 충분합니다.

크기대가
64비트정수 하나. 인덱스와 전송에 그대로 쓴다
128비트저장과 인덱스가 두 배. 정수 연산으로 다루기 어렵다

크기가 두 배면 인덱스도 두 배이고, 인덱스가 메모리에 들어가는지가 조회 속도를 정합니다. ID 하나의 크기가 시스템 전체의 메모리 예산에 곱해집니다.

"대략" 시간순의 뜻

같은 밀리초 안에서는 순서를 보장하지 않습니다. 이 포기가 결정적입니다.

보장한다면: 같은 밀리초에 발급하는 노드들이 서로 조율해야 한다
포기하면: 각 노드가 혼자 만든다. 왕복이 0이 된다

지연 1ms 는 외부에 물어볼 시간이 없다는 뜻입니다. 조율을 없애야 지킬 수 있고, 조율을 없애려면 밀리초 안 순서를 포기해야 합니다.

무엇을 절대 포기하지 않는가

중복은 허용하지 않습니다. 같은 ID가 두 번 나오면 다른 두 데이터가 하나로 취급되고, 그 사실을 나중에 알아채기도 어렵습니다.

유일성은 확률이 아니라 구조로 보장해야 합니다. 뒤의 모든 판단에서 이 기준이 가장 앞섭니다.

면접에서 이렇게 나옵니다

Q.ID 생성기의 요구사항을 어떻게 정리하시겠습니까

어떤 순서를 포기할지부터 정합니다.

구분내용
기능ID 발급, 여러 서비스가 함께 쓰기, 발급기 추가와 제거
범위 밖빈틈 없는 연속 번호, 발급 시각 정확 복원, 문자열 ID
비기능64비트, 피크 초당 10만, 발급기 64대, 지연 1ms, 수명 30년

가장 중요한 두 줄은 빈틈 없는 연속 번호를 뺀 것같은 밀리초 안 순서를 포기한 것입니다. 둘 다 조율을 없애기 위한 포기입니다.

흔한 실수: 유일성만 요구사항으로 말하는 것. 유일한 값은 무작위로도 만들 수 있습니다. 정렬과 크기와 지연이 함께 있어야 설계가 좁혀지고, 그 셋이 없으면 어떤 방식도 답이 됩니다.

Q.ID가 시간순으로 커져야 하는 이유가 무엇인가요

저장소 삽입 비용 때문입니다. ID는 대개 기본 키가 됩니다.

시간순으로 커지면 새 행이 인덱스의 한쪽 끝에만 붙습니다. 그 부분만 메모리에 있으면 되므로 삽입이 값쌉니다.

무작위 값이면 삽입 위치가 전체에 흩어져 매번 다른 페이지를 건드립니다. 하루 86억 건 규모에서 이 차이가 디스크 접근 수를 정합니다.

시간순: 닿는 곳이 끝 한 군데
무작위: 매 삽입이 다른 곳

흔한 실수: 정렬을 조회 편의로 설명하는 것. 시간순 조회는 별도 컬럼으로도 됩니다. 정렬이 필요한 진짜 이유는 쓰기 비용이고, 그것을 말하지 못하면 무작위 방식을 왜 안 쓰는지 설명할 수 없습니다.

Q.왜 64비트 로 정했나요

필요한 범위를 담으면서 정수 하나로 다룰 수 있는 가장 작은 크기입니다.

수명 30년 에 하루 86억 건이면 총 발급량이 약 9경 4천조입니다. 64비트 정수의 범위가 그보다 훨씬 크므로 충분합니다.

크기대가
64비트정수 하나. 인덱스와 전송에 그대로 쓴다
128비트저장과 인덱스가 두 배

ID 하나의 크기가 시스템 전체의 메모리 예산에 곱해집니다. 인덱스가 메모리에 들어가는지가 조회 속도를 정하므로 두 배는 작은 차이가 아닙니다.

흔한 실수: 크기를 넉넉하게 잡아 두는 것이 안전하다고 보는 것. ID는 모든 테이블과 모든 요청에 실려 다니는 값이라 크기의 영향이 곱셈으로 커집니다. 필요한 만큼만 쓰는 것이 이 값에서는 특히 중요합니다.

Q.같은 밀리초 안 순서를 왜 포기하나요

조율을 없애기 위해서입니다.

보장한다면: 같은 밀리초에 발급하는 노드들이 서로 조율해야 한다
포기하면: 각 노드가 혼자 만든다. 왕복이 0이 된다

지연 1ms 는 외부에 물어볼 시간이 없다는 뜻입니다. 같은 구역 왕복이 0.2에서 1밀리초이므로 한 번만 물어봐도 예산을 씁니다.

밀리초 단위 정렬로 충분한지가 판단 기준입니다. 목록을 시간순으로 보여주는 용도라면 충분하고, 두 사건의 선후를 판정하는 용도라면 부족합니다.

흔한 실수: ID의 순서를 인과 관계의 근거로 쓰는 것. 큰 ID가 나중에 만들어졌다는 보장은 밀리초 단위까지만 유효합니다. 이 성질을 뒤에서 다시 다룹니다.

먼저 스스로 답해보고 아래 답변과 견줘보세요. 막히는 부분은 문제로 확인할 수 있어요.

읽었으면 문제로 확인해보세요

분산 ID 생성기 설계 문제를 풀면 틀린 문제가 자동으로 노트에 쌓입니다. 가입 없이 5문제를 먼저 풀어볼 수도 있어요.