[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
---
|
||||
id: cs-snowflake-id-generation
|
||||
title: ID 생성 — UUID v7 / Snowflake / KSUID
|
||||
category: Coding
|
||||
status: draft
|
||||
source_trust_level: B
|
||||
verification_status: conceptual
|
||||
created_at: 2026-05-09
|
||||
updated_at: 2026-05-09
|
||||
tags: [cs, id, uuid, snowflake, vibe-coding]
|
||||
tech_stack: { language: "TS", applicable_to: ["Backend"] }
|
||||
applied_in: []
|
||||
aliases: [UUID v7, Snowflake, KSUID, ULID, NanoID, time-ordered ID, sortable]
|
||||
---
|
||||
|
||||
# ID Generation
|
||||
|
||||
> AUTO_INCREMENT = 한계 (분산 X). UUID v4 = 안 정렬. **UUID v7 / Snowflake / KSUID = 시간 정렬 + 분산 OK + 인덱스 friendly**.
|
||||
|
||||
## 📖 핵심 개념
|
||||
- 시간 정렬: 최신 INSERT 가 인덱스 끝 (페이지 수 적게).
|
||||
- 분산 생성: 중앙 service 없이 다중 노드.
|
||||
- 비밀: UUID v4 가 추측 불가 (URL 안전).
|
||||
- 길이: 16-26 byte.
|
||||
|
||||
## 💻 코드 패턴
|
||||
|
||||
### UUID v7 (modern, 표준 RFC 9562)
|
||||
```ts
|
||||
import { v7 as uuidv7 } from 'uuid';
|
||||
const id = uuidv7();
|
||||
// "01918fec-d2c5-7000-8aab-1234abcd"
|
||||
// 첫 48 bit = unix ms timestamp → 시간 정렬
|
||||
```
|
||||
|
||||
```sql
|
||||
-- Postgres 17+
|
||||
SELECT uuidv7();
|
||||
```
|
||||
|
||||
→ UUID v4 호환 + 시간 정렬. **2026 권장 디폴트**.
|
||||
|
||||
### Snowflake (Twitter)
|
||||
```
|
||||
64-bit:
|
||||
41 bit: timestamp (ms since epoch)
|
||||
10 bit: machine ID
|
||||
12 bit: sequence (per ms)
|
||||
|
||||
→ ms 당 4096 ID, 1024 machines.
|
||||
```
|
||||
|
||||
```ts
|
||||
class Snowflake {
|
||||
private seq = 0;
|
||||
private lastMs = 0;
|
||||
|
||||
constructor(private machineId: number) {}
|
||||
|
||||
next(): bigint {
|
||||
const now = Date.now();
|
||||
if (now === this.lastMs) {
|
||||
this.seq++;
|
||||
if (this.seq >= 4096) {
|
||||
// wait for next ms
|
||||
while (Date.now() === this.lastMs);
|
||||
return this.next();
|
||||
}
|
||||
} else {
|
||||
this.seq = 0;
|
||||
}
|
||||
this.lastMs = now;
|
||||
|
||||
return (BigInt(now) << 22n) | (BigInt(this.machineId) << 12n) | BigInt(this.seq);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
→ 64-bit (BigInt). 빠르고 정렬. machine ID 충돌 위험.
|
||||
|
||||
### KSUID (Segment)
|
||||
```ts
|
||||
import { KSUID } from 'ksuid';
|
||||
const id = KSUID.randomSync().string;
|
||||
// "0ujsswThIGTUYm2K8FjOOfXtY1K"
|
||||
// 27 글자, 첫 4 byte = 32-bit unix seconds, 16 byte random
|
||||
```
|
||||
|
||||
→ 27 char, sortable, URL-safe.
|
||||
|
||||
### ULID
|
||||
```ts
|
||||
import { ulid } from 'ulid';
|
||||
const id = ulid();
|
||||
// "01ARZ3NDEKTSV4RRFFQ69G5FAV"
|
||||
// 26 글자, 첫 10 = ms timestamp, 16 = random
|
||||
```
|
||||
|
||||
→ KSUID 비슷, 더 짧음.
|
||||
|
||||
### NanoID (random, secure)
|
||||
```ts
|
||||
import { nanoid } from 'nanoid';
|
||||
const id = nanoid(); // 21 char
|
||||
const short = nanoid(10); // 10 char
|
||||
```
|
||||
|
||||
→ URL-safe, 빠름. 단 시간 정렬 X.
|
||||
|
||||
### CUID2
|
||||
```ts
|
||||
import { createId } from '@paralleldrive/cuid2';
|
||||
const id = createId();
|
||||
```
|
||||
|
||||
→ Collision-resistant + URL-safe + 시간 정렬.
|
||||
|
||||
### Postgres column type
|
||||
```sql
|
||||
-- UUID
|
||||
CREATE TABLE orders (
|
||||
id UUID PRIMARY KEY DEFAULT uuidv7() -- PG 17+
|
||||
);
|
||||
|
||||
-- 또는 string
|
||||
CREATE TABLE orders (
|
||||
id TEXT PRIMARY KEY -- KSUID / ULID / NanoID
|
||||
);
|
||||
|
||||
-- BIGINT (Snowflake)
|
||||
CREATE TABLE orders (
|
||||
id BIGINT PRIMARY KEY
|
||||
);
|
||||
```
|
||||
|
||||
### 인덱스 영향
|
||||
```
|
||||
v4 UUID: random → 매 INSERT 가 인덱스 random page → 큰 cache miss.
|
||||
v7 UUID: 시간 정렬 → 항상 끝 page → 빠름.
|
||||
AUTO_INCREMENT: 같지만 분산 X.
|
||||
```
|
||||
|
||||
→ 큰 테이블 = v7 가 v4 보다 INSERT 5-10x 빠름.
|
||||
|
||||
### 보안
|
||||
```
|
||||
v4: 122-bit random → 추측 불가. URL 안전.
|
||||
v7: 첫 48 bit = 시간 노출 → 정확 시각 추적 가능.
|
||||
(random 80-bit 도 함께 — collision 안전)
|
||||
```
|
||||
|
||||
→ Public ID = v4 또는 짧은 nanoid. Internal = v7.
|
||||
|
||||
```ts
|
||||
// 두 단계 — internal sortable + public random
|
||||
{ id: 'uuid-v7', publicId: 'nanoid' }
|
||||
```
|
||||
|
||||
### 분산 서비스 (Sonyflake / IdGen)
|
||||
- Snowflake 변형, machine ID 자동 할당.
|
||||
- Redis SETNX 또는 etcd.
|
||||
|
||||
```ts
|
||||
class Sonyflake {
|
||||
// sub-second precision, larger machine bits
|
||||
// machine ID = MAC 주소 last 16 bit
|
||||
}
|
||||
```
|
||||
|
||||
### Sequence database (PG / MySQL)
|
||||
```sql
|
||||
-- PG sequence
|
||||
CREATE SEQUENCE orders_id_seq START 1;
|
||||
SELECT nextval('orders_id_seq');
|
||||
|
||||
-- 단일 PG = bottleneck. 분산 X.
|
||||
```
|
||||
|
||||
### Encrypted ID (Hashids / Sqids)
|
||||
```ts
|
||||
import Sqids from 'sqids';
|
||||
const sqids = new Sqids({ minLength: 8 });
|
||||
const encoded = sqids.encode([userId]); // "yWBV0AVL"
|
||||
const decoded = sqids.decode(encoded); // [userId]
|
||||
```
|
||||
|
||||
→ Auto-increment 숨기기. URL 짧음 + 의미 있는 short link.
|
||||
|
||||
## 🤔 의사결정 기준
|
||||
| 상황 | 추천 |
|
||||
|---|---|
|
||||
| 새 프로젝트 디폴트 | UUID v7 |
|
||||
| Public URL ID | NanoID / KSUID |
|
||||
| 분산 서비스 (Twitter scale) | Snowflake |
|
||||
| Schema 호환 (UUID 컬럼) | UUID v7 |
|
||||
| 짧은 ID 필요 | NanoID 10-12 |
|
||||
| 정렬 + 짧음 | KSUID / ULID |
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **UUID v4 + 큰 테이블**: 인덱스 성능 추락.
|
||||
- **AUTO_INCREMENT 분산 환경**: collision / sync.
|
||||
- **Snowflake machine ID hard-code**: 충돌. 자동 할당.
|
||||
- **System clock backwards (NTP)**: ID 충돌. monotonic clock.
|
||||
- **추측 가능 ID public 노출 (1, 2, 3)**: enumeration 공격.
|
||||
- **ID = 비즈니스 의미**: ABC-2026-0001 같은 — 변경 어려움.
|
||||
- **여러 system 다른 형식**: 통일 (모두 UUID 또는 모두 KSUID).
|
||||
|
||||
## 🤖 LLM 활용 힌트
|
||||
- 새 = UUID v7 (PG 17+ 또는 라이브러리).
|
||||
- Public URL = NanoID.
|
||||
- Distributed scale = Snowflake.
|
||||
- 시간 정렬 + URL-safe = KSUID / ULID.
|
||||
|
||||
## 🔗 관련 문서
|
||||
- [[DB_Index_Strategy]]
|
||||
- [[DB_Sharding_Strategies]]
|
||||
- [[Security_OWASP_Top_10_Practical]]
|
||||
Reference in New Issue
Block a user