티스토리 뷰

반응형

들어가며

서비스를 개발하다 보면 거의 모든 데이터에 ID가 필요하다.

예를 들어 다음과 같은 데이터들은 대부분 고유한 식별자를 가진다.

  • 회원 ID
  • 주문 ID
  • 게시글 ID
  • 댓글 ID
  • 결제 ID

단일 서버와 단일 데이터베이스만 사용하는 환경에서는 ID 생성이 비교적 단순하다. 데이터베이스의 AUTO_INCREMENTSEQUENCE를 사용하면 된다.

User Table

id | name
---|------
1  | Kim
2  | Lee
3  | Park

하지만 시스템 규모가 커지면 상황이 달라진다.

서버가 여러 대로 늘어나고, 데이터베이스가 샤딩되고, 여러 지역의 데이터센터에서 동시에 요청을 처리해야 한다면 단순히 숫자를 1씩 증가시키는 방식만으로는 충분하지 않을 수 있다.

Server A ─┐
Server B ─┼──> ID 생성
Server C ─┘

분산 시스템에서 ID를 생성할 때 중요한 문제는 단순히 “중복되지 않는 값”을 만드는 것만이 아니다.

ID가 시간순으로 정렬될 수 있는지, 데이터베이스 인덱스에 유리한지, 중앙 서버 장애에 영향을 받는지, 외부에 노출해도 안전한지, 초당 얼마나 많은 ID를 생성할 수 있는지도 함께 고려해야 한다.

이 글에서는 대규모 분산 시스템에서 고유 ID를 생성하는 대표적인 방법들을 간단히 정리한다. 각 방식의 내부 구조나 세부 구현은 이후 글에서 하나씩 더 자세히 살펴볼 예정이다.

 


 

좋은 ID는 어떤 조건을 만족해야 할까?

ID 생성 방식을 비교하기 전에 먼저 평가 기준을 정리할 필요가 있다.

모든 상황에 완벽한 ID 생성 방식은 없다. 어떤 방식은 구현이 단순하지만 확장성에 약하고, 어떤 방식은 분산 환경에 강하지만 운영 복잡도가 높다.

좋은 ID 생성 방식을 판단할 때는 보통 다음 기준을 본다.

기준 설명
고유성 서로 다른 데이터가 같은 ID를 가지면 안 된다.
생성 성능 많은 요청이 들어와도 빠르게 ID를 생성할 수 있어야 한다.
분산 생성 가능 여부 여러 서버가 동시에 ID를 만들어도 충돌이 없어야 한다.
정렬 가능성 ID만 보고 생성 순서나 대략적인 시간을 알 수 있는지 여부다.
저장 공간 ID가 차지하는 크기가 작을수록 저장 공간과 인덱스 크기에 유리하다.
인덱스 효율 데이터베이스 B-Tree 인덱스에서 삽입 성능이 좋은지 여부다.
예측 가능성 외부 사용자가 다음 ID를 쉽게 추측할 수 있는지 여부다.
운영 복잡도 별도의 중앙 서버, Worker ID 관리, 시간 동기화 등이 필요한지 여부다.

예를 들어 AUTO_INCREMENT는 단순하고 인덱스 효율이 좋지만, 분산 환경에서는 확장성 문제가 생길 수 있다.

반대로 UUID는 여러 서버에서 독립적으로 생성하기 쉽지만, UUID v4처럼 완전히 랜덤한 값은 데이터베이스 인덱스 관점에서 불리할 수 있다.

 


 

1. Auto Increment / Sequence

가장 단순한 방식은 데이터베이스가 ID 생성을 담당하는 방식이다.

MySQL의 AUTO_INCREMENT, PostgreSQL의 SEQUENCE가 대표적이다.

CREATE TABLE users (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100)
);

이 방식은 데이터가 insert될 때마다 데이터베이스가 자동으로 증가하는 숫자 ID를 부여한다.

1, 2, 3, 4, 5, ...

장점

Auto Increment와 Sequence 방식의 장점은 명확하다.

  • 구현이 매우 단순하다.
  • ID가 숫자라 저장 공간이 작다.
  • 순차적으로 증가하므로 정렬이 쉽다.
  • B-Tree 인덱스에 유리하다.
  • 운영 복잡도가 낮다.

RDBMS 기반의 일반적인 CRUD 서비스라면 이 방식만으로도 충분한 경우가 많다.

단점

문제는 시스템이 커졌을 때 발생한다.

ID 생성을 데이터베이스가 담당하므로, 결국 데이터베이스가 ID 생성의 중심 지점이 된다.

Server A ─┐
Server B ─┼──> Database Sequence
Server C ─┘

서버가 여러 대로 늘어나도 ID 생성은 데이터베이스에 의존한다. 트래픽이 커지면 데이터베이스가 병목이 될 수 있다.

또한 데이터베이스를 여러 개로 샤딩하면 더 복잡해진다.

Shard 1: id = 1
Shard 2: id = 1
Shard 3: id = 1

각 샤드가 독립적으로 Auto Increment를 사용하면 서로 같은 ID가 만들어질 수 있다.

이를 피하기 위해 샤드별로 ID 범위를 나누거나, 증가 간격을 다르게 설정하거나, 별도의 ID 생성 서버를 두는 방법을 사용할 수 있다. 하지만 이 순간부터 단순했던 Auto Increment 방식도 운영 복잡도가 증가한다.

적합한 경우

Auto Increment나 Sequence는 다음과 같은 상황에 적합하다.

  • 단일 데이터베이스 기반 서비스
  • 샤딩이 필요 없는 규모
  • 내부 관리자용 데이터
  • 순차적인 숫자 ID가 필요한 경우
  • 구현 단순성이 중요한 경우

 


 

2. 중앙 ID 생성 서버

두 번째 방식은 ID 생성을 전담하는 중앙 서버를 두는 것이다.

Server A ─┐
Server B ─┼──> ID Generator Server ──> 1000001
Server C ─┘

애플리케이션 서버는 직접 ID를 만들지 않고, ID 생성 서버에 요청해서 새로운 ID를 받아온다.

구현 방식은 다양하다.

  • Redis INCR
  • 데이터베이스 Sequence 전용 서버
  • ZooKeeper
  • etcd
  • 별도 ID Generator API 서버

이 방식은 Auto Increment를 데이터베이스 테이블에 직접 묶어두는 대신, ID 생성 책임을 별도 컴포넌트로 분리한 형태라고 볼 수 있다.

장점

중앙 ID 생성 서버를 사용하면 여러 애플리케이션 서버가 같은 규칙으로 ID를 발급받을 수 있다.

  • 전역적으로 중복 없는 ID를 만들기 쉽다.
  • 순차 증가 ID를 만들기 쉽다.
  • ID 생성 정책을 한 곳에서 관리할 수 있다.
  • 여러 서비스가 같은 ID 생성 규칙을 공유할 수 있다.

단점

가장 큰 단점은 중앙 장애점이 생긴다는 것이다.

ID Generator Server 장애
        ↓
새로운 데이터 생성 불가

ID 생성 서버가 죽으면 새로운 주문, 게시글, 메시지를 만들 수 없게 된다.

물론 ID 생성 서버도 고가용성 구조로 만들 수 있다. 하지만 이 경우 시스템 복잡도는 다시 증가한다.

또한 모든 ID 생성 요청이 네트워크 호출을 필요로 하므로, 애플리케이션 내부에서 직접 생성하는 방식보다 지연 시간이 늘어날 수 있다.

적합한 경우

중앙 ID 생성 서버는 다음과 같은 상황에 적합하다.

  • 전역적으로 순차적인 ID가 필요한 경우
  • 여러 서비스가 동일한 ID 발급 정책을 사용해야 하는 경우
  • ID 생성 정책을 중앙에서 통제해야 하는 경우
  • 중앙 컴포넌트를 고가용성으로 운영할 수 있는 경우

 


 

3. UUID

UUID는 Universally Unique Identifier의 약자다.

UUID는 128bit 식별자이며, 일반적으로 다음과 같은 문자열 형태로 표현된다.

550e8400-e29b-41d4-a716-446655440000

UUID의 가장 큰 장점은 중앙 서버 없이 각 서버가 독립적으로 ID를 생성할 수 있다는 점이다.

Server A ──> UUID 생성
Server B ──> UUID 생성
Server C ──> UUID 생성

각 서버가 서로 통신하지 않아도 충돌 가능성이 매우 낮은 ID를 만들 수 있다.

UUID에는 여러 버전이 있다. 이 글에서는 대표적으로 UUID v1, UUID v4, UUID v7을 간단히 살펴본다.

 


 

3-1. UUID v1

UUID v1은 시간 정보와 노드 정보를 기반으로 ID를 생성한다.

단순화하면 다음과 같은 구조다.

Timestamp + Clock Sequence + Node

여기서 Node 값에는 전통적으로 MAC Address가 사용될 수 있었다.

시간 기반이기 때문에 어느 정도 정렬 가능하다는 장점이 있지만, MAC Address 같은 하드웨어 식별 정보가 포함될 수 있다는 점 때문에 개인정보나 보안 측면에서 부담이 있다.

현재 신규 시스템에서 UUID v1을 적극적으로 선택하는 경우는 많지 않다.

 


 

3-2. UUID v4

UUID v4는 랜덤 기반 UUID다.

Random 122 bits + Version/Variant bits

대부분의 개발자가 흔히 사용하는 UUID가 이 방식이다.

Java에서는 다음과 같이 생성할 수 있다.

UUID id = UUID.randomUUID();

UUID v4의 장점은 단순하다.

  • 중앙 서버가 필요 없다.
  • 여러 서버에서 동시에 생성해도 충돌 가능성이 극히 낮다.
  • 구현이 쉽다.
  • 외부에 노출해도 다음 ID를 예측하기 어렵다.

하지만 단점도 있다.

UUID v4는 랜덤 값이기 때문에 생성 순서와 ID 정렬 순서가 일치하지 않는다.

생성 순서:
A → B → C → D

UUID 정렬 결과:
C → A → D → B

이 특성은 데이터베이스 인덱스에서 문제가 될 수 있다.

B-Tree 인덱스는 정렬된 구조를 유지한다. 순차적으로 증가하는 ID는 인덱스의 끝에 계속 추가되기 때문에 비교적 효율적이다.

반면 UUID v4처럼 랜덤한 값은 인덱스의 여러 위치에 흩어져 삽입된다. 이로 인해 페이지 분할, 캐시 효율 저하, 인덱스 단편화가 발생할 수 있다.

적합한 경우

UUID v4는 다음과 같은 상황에 적합하다.

  • 중앙 서버 없이 ID를 생성해야 하는 경우
  • ID 예측 가능성을 낮춰야 하는 경우
  • 정렬 순서가 중요하지 않은 경우
  • 데이터 생성량이 크지 않거나 인덱스 성능 영향이 크지 않은 경우

 


 

3-3. UUID v7

UUID v7은 시간 정렬성을 고려한 UUID다.

RFC 9562에 정의된 UUID v7은 Unix Epoch 기준 millisecond timestamp를 기반으로 한다. 즉, ID 앞부분에 시간 정보가 들어가고 나머지 부분에 랜덤성이 들어간다.

단순화하면 다음과 같다.

Timestamp + Random

UUID v7은 UUID v4의 장점인 분산 생성 가능성과 낮은 충돌 가능성을 유지하면서, 시간순 정렬에 더 유리하도록 설계되었다.

UUID v4: 랜덤 → 정렬 불리
UUID v7: 시간 + 랜덤 → 정렬 유리

따라서 데이터베이스 Primary Key로 UUID를 사용해야 한다면 UUID v4보다 UUID v7이 더 나은 선택이 될 수 있다.

적합한 경우

UUID v7은 다음과 같은 상황에 적합하다.

  • UUID 형식을 유지하고 싶은 경우
  • 분산 환경에서 각 서버가 독립적으로 ID를 생성해야 하는 경우
  • 시간순 정렬이 필요한 경우
  • 데이터베이스 인덱스 효율도 어느 정도 고려해야 하는 경우

 


 

4. ULID

ULID는 Universally Unique Lexicographically Sortable Identifier의 약자다.

이름 그대로 사전식 정렬이 가능한 고유 ID다.

예시는 다음과 같다.

01ARZ3NDEKTSV4RRFFQ69G5FAV

ULID는 128bit 구조이며, 크게 두 부분으로 나뉜다.

48bit Timestamp + 80bit Randomness

앞부분에 timestamp가 들어가기 때문에 문자열을 정렬했을 때 생성 시간순으로 정렬할 수 있다.

UUID와 비교하면 다음과 같은 특징이 있다.

  • UUID와 동일하게 128bit 수준의 식별자다.
  • 문자열 길이가 26자로 UUID 문자열 표현보다 짧다.
  • 시간순 정렬이 가능하다.
  • URL-safe하다.
  • 사람이 읽기에는 UUID보다 조금 더 간결하다.

ULID는 로그, 이벤트, 메시지, 외부 공개 ID 등에 사용하기 좋다.

다만 UUID처럼 표준 라이브러리에 항상 기본 포함되어 있는 것은 아니므로, 사용하는 언어나 프레임워크에 따라 별도 라이브러리가 필요할 수 있다.

 


 

5. Snowflake

Snowflake는 Twitter에서 사용한 것으로 유명한 분산 ID 생성 방식이다.

Snowflake 계열의 핵심은 64bit 정수 하나에 시간, 서버 식별자, 시퀀스 번호를 나누어 담는 것이다.

대표적인 구조는 다음과 같다.

64bit

1bit  : Sign
41bit : Timestamp
10bit : Worker ID
12bit : Sequence

각 필드의 의미는 다음과 같다.

필드 설명
Sign 일반적으로 사용하지 않는 부호 비트다.
Timestamp 기준 시각 이후 흐른 시간을 millisecond 단위로 저장한다.
Worker ID ID를 생성한 서버나 프로세스를 구분한다.
Sequence 같은 millisecond 안에서 여러 ID를 만들기 위한 증가값이다.

예를 들어 같은 시각에 여러 서버가 동시에 ID를 생성하더라도 Worker ID가 다르면 충돌하지 않는다.

Server A: Worker ID = 1
Server B: Worker ID = 2
Server C: Worker ID = 3

그리고 같은 서버에서 같은 millisecond 안에 여러 개의 ID를 생성하면 Sequence 값을 증가시켜 구분한다.

timestamp = 1000, worker = 1, sequence = 0
timestamp = 1000, worker = 1, sequence = 1
timestamp = 1000, worker = 1, sequence = 2

Snowflake의 장점은 명확하다.

  • 64bit 숫자 ID를 만들 수 있다.
  • 시간순 정렬이 가능하다.
  • 중앙 서버 없이 각 서버가 직접 생성할 수 있다.
  • UUID보다 저장 공간이 작다.
  • 데이터베이스 인덱스에 비교적 유리하다.
  • 초당 대량의 ID 생성이 가능하다.

하지만 운영 시 주의할 점도 있다.

첫 번째는 Worker ID 관리다. 두 서버가 같은 Worker ID를 사용하면 같은 시간에 같은 Sequence를 생성할 수 있으므로 ID 충돌이 발생할 수 있다.

두 번째는 서버 시간 문제다. Snowflake는 timestamp를 사용하기 때문에 서버 시간이 뒤로 돌아가면 중복 ID가 생성될 위험이 있다. 이를 Clock Rollback 문제라고 한다.

세 번째는 같은 millisecond 안에서 만들 수 있는 ID 수에 제한이 있다는 점이다. 예를 들어 Sequence가 12bit라면 한 Worker가 같은 millisecond 안에서 만들 수 있는 ID는 최대 4096개다.

적합한 경우

Snowflake는 다음과 같은 상황에 적합하다.

  • 대규모 트래픽을 처리해야 하는 경우
  • 숫자형 ID가 필요한 경우
  • 시간순 정렬이 필요한 경우
  • 데이터베이스 인덱스 효율이 중요한 경우
  • Worker ID와 시간 동기화를 운영할 수 있는 경우

 


 

6. MongoDB ObjectId

MongoDB를 사용하면 기본적으로 ObjectId를 자주 접하게 된다.

ObjectId는 12byte 식별자이며, 일반적으로 다음과 같은 정보를 포함한다.

Timestamp + Random Value + Counter

ObjectId는 생성 시간 정보를 포함하므로 대략적인 시간순 정렬이 가능하다.

MongoDB 환경에서는 애플리케이션이 데이터베이스에 저장하기 전에 ObjectId를 미리 생성할 수도 있다. 즉, 중앙 DB에 insert한 뒤에야 ID를 알 수 있는 구조가 아니라, 클라이언트나 애플리케이션 레벨에서 먼저 ID를 만들 수 있다.

적합한 경우

MongoDB ObjectId는 다음과 같은 상황에 적합하다.

  • MongoDB를 사용하는 경우
  • 문서 생성 시점에 애플리케이션에서 ID를 미리 알고 싶은 경우
  • 대략적인 생성 시간 정보가 필요한 경우

 


 

7. KSUID / Sonyflake

KSUID와 Sonyflake는 Snowflake나 ULID와 비슷한 문제를 해결하기 위해 등장한 ID 생성 방식이다.

KSUID

KSUID는 K-Sortable Unique ID의 약자다.

시간 정보를 포함하고 있어 정렬 가능한 ID를 만들 수 있다. 주로 이벤트, 로그, 분산 시스템의 리소스 ID 등에 사용된다.

ULID와 비슷하게 “시간순 정렬 가능한 문자열 ID”라는 관점에서 볼 수 있다.

Sonyflake

Sonyflake는 Snowflake의 변형이다.

Snowflake와 마찬가지로 timestamp, machine id, sequence를 조합해서 ID를 생성한다. 다만 bit 배치나 시간 단위 등이 Snowflake와 다르다.

이런 변형 방식들은 공통적으로 다음 목표를 가진다.

  • 중앙 서버 없이 ID 생성
  • 시간순 정렬 가능
  • 고성능 생성
  • 분산 환경에서 충돌 방지

 


 

상황별 선택 기준

ID 생성 방식을 선택할 때는 “무엇이 가장 좋은가?”보다 “현재 시스템에서 어떤 특성이 중요한가?”를 기준으로 봐야 한다.

단일 DB 기반 서비스

단일 데이터베이스를 사용하고 있고, 트래픽이 크지 않다면 Auto Increment나 Sequence가 가장 단순하다.

단순함 > 분산 확장성

이 경우 굳이 UUID나 Snowflake를 사용할 필요가 없을 수 있다.

여러 서버에서 독립적으로 ID를 생성해야 하는 경우

애플리케이션 서버가 여러 대이고, 중앙 ID 생성 서버에 의존하고 싶지 않다면 UUID v4, UUID v7, ULID, Snowflake 계열을 고려할 수 있다.

분산 생성 가능성 > 중앙 통제

데이터베이스 Primary Key로 사용할 경우

Primary Key는 인덱스 성능에 직접적인 영향을 준다.

완전히 랜덤한 UUID v4는 B-Tree 인덱스에서 불리할 수 있다. 시간순 정렬이 가능한 UUID v7, ULID, Snowflake가 더 적합할 수 있다.

외부에 노출되는 ID가 필요한 경우

사용자에게 노출되는 URL이나 API 응답에 ID가 포함된다면 예측 가능성을 고려해야 한다.

/users/1
/users/2
/users/3

위와 같은 순차 ID는 다음 리소스의 ID를 쉽게 추측할 수 있다.

이 경우 UUID v4, UUID v7, ULID처럼 예측하기 어려운 ID가 더 적합할 수 있다.

숫자형 ID가 필요한 경우

일부 시스템에서는 문자열 ID보다 숫자형 ID가 더 편리하다.

예를 들어 데이터베이스 저장 공간, 인덱스 크기, 다른 시스템과의 호환성 때문에 BIGINT ID를 선호할 수 있다.

이 경우 Snowflake 계열이 좋은 선택지가 될 수 있다.

 


 

정리

대규모 분산 시스템에서 고유 ID를 생성하는 방식은 다양하다.

각 방식은 해결하려는 문제가 다르다.

방식 핵심 관점
Auto Increment / Sequence 단순하고 빠른 DB 중심 ID 생성
중앙 ID 서버 전역적으로 통제되는 ID 생성
UUID v4 중앙 조정 없는 랜덤 ID 생성
UUID v7 시간 정렬 가능한 UUID
ULID 문자열 정렬 가능한 시간 기반 ID
Snowflake 64bit 숫자 기반 분산 ID
MongoDB ObjectId MongoDB 문서 중심 ID
KSUID / Sonyflake 시간 정렬 가능한 Snowflake 계열 변형

중요한 점은 완벽한 ID 생성 방식은 없다는 것이다.

ID 생성 방식을 선택할 때는 다음 질문을 먼저 해야 한다.

  • ID가 반드시 숫자여야 하는가?
  • 시간순 정렬이 필요한가?
  • 여러 서버가 독립적으로 ID를 생성해야 하는가?
  • 중앙 ID 생성 서버를 둘 수 있는가?
  • 데이터베이스 인덱스 성능이 중요한가?
  • 외부 사용자가 ID를 예측하면 문제가 되는가?
  • Worker ID나 서버 시간 동기화를 운영할 수 있는가?

이 질문에 대한 답에 따라 선택지는 달라진다.

간단한 서비스라면 Auto Increment만으로 충분할 수 있다. 반대로 대규모 분산 시스템에서는 UUID v7, ULID, Snowflake 같은 방식을 고려해야 한다.

이번 글에서는 분산 시스템에서 고유 ID를 생성하는 대표적인 방법들을 전체적으로 훑어보았다.

 


 

참고 자료

반응형

'Server' 카테고리의 다른 글

Forward / Reverse Proxy  (0) 2022.05.02
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2026/07   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함
반응형