Skip to content

Commit 0cee0c3

Browse files
committed
SnowFlake ID 생성 알고리즘 구현
1 parent 6da7b84 commit 0cee0c3

File tree

2 files changed

+101
-0
lines changed

2 files changed

+101
-0
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package io.github.gunkim.algorithm.identifier;
2+
3+
public class SnowFlakeIdentifier {
4+
private static final int DATACENTER_ID_BITS = 5;
5+
private static final int WORKER_ID_BITS = 5;
6+
private static final int SEQUENCE_BITS = 12;
7+
8+
private static final int WORKER_ID_SHIFT = SEQUENCE_BITS;
9+
private static final int DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
10+
private static final int TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS;
11+
12+
private long timestamp;
13+
private long datacenterId;
14+
private long workerId;
15+
private long sequence;
16+
17+
public SnowFlakeIdentifier(long timestamp, long datacenterId, long workerId, long sequence) {
18+
this.timestamp = timestamp;
19+
this.datacenterId = datacenterId;
20+
this.workerId = workerId;
21+
this.sequence = sequence;
22+
}
23+
24+
public long getValue() {
25+
return (timestamp << TIMESTAMP_SHIFT) |
26+
(datacenterId << DATACENTER_ID_SHIFT) |
27+
(workerId << WORKER_ID_SHIFT) |
28+
sequence;
29+
}
30+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package io.github.gunkim.algorithm.identifier;
2+
3+
/**
4+
* 트위터 Snowflake ID 생성기
5+
* 총 64비트 구조
6+
* - sign 1비트 : 부호 비트, 음수값으로 사용되지 않음
7+
* - timestamp 41비트 : 41비트로 표현되는 타임스탬프 (밀리초 단위)
8+
* - worker id 5비트 : 서버 ID, 최대 32(2^5)개 서버 지원
9+
* - datacenter id 5비트 : 데이터센터 ID, 최대 32(2^5)개 데이터센터 지원
10+
* - sequence 12비트 : 일련번호, 1밀리초 내에 최대 4096까지 증가하며 매 밀리초마다 초기화
11+
* <p>
12+
* Snowflake ID는 64비트 크기로 **정렬 가능**하며, **전 세계 어디에서나 독립적으로 유니크한 ID**를 생성할 수 있다.
13+
* 이론적으로, 최대 32개의 데이터센터와 각 데이터센터마다 32개의 서버를 사용할 수 있다.
14+
*/
15+
public final class SnowFlakeIdentifierGenerator {
16+
private static final int DATACENTER_ID_BITS = 5;
17+
private static final int WORKER_ID_BITS = 5;
18+
private static final int SEQUENCE_BITS = 12;
19+
20+
private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);
21+
private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
22+
private static final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BITS);
23+
24+
private final long epoch;
25+
private final long workerId;
26+
private final long datacenterId;
27+
28+
private long lastTimestamp = -1L;
29+
private long sequence = 0L;
30+
31+
public SnowFlakeIdentifierGenerator(long epoch, long workerId, long datacenterId) {
32+
if (workerId > MAX_WORKER_ID || workerId < 0) {
33+
throw new IllegalArgumentException("Worker ID는 " + MAX_WORKER_ID + "를 초과할 수 없습니다");
34+
}
35+
if (datacenterId > MAX_DATACENTER_ID || datacenterId < 0) {
36+
throw new IllegalArgumentException("Datacenter ID는 " + MAX_DATACENTER_ID + "를 초과할 수 없습니다");
37+
}
38+
39+
this.epoch = epoch;
40+
this.workerId = workerId;
41+
this.datacenterId = datacenterId;
42+
}
43+
44+
public synchronized SnowFlakeIdentifier nextId() {
45+
long timestamp = System.currentTimeMillis() - epoch;
46+
47+
if (timestamp < lastTimestamp) {
48+
throw new RuntimeException("시스템 시계가 이전 시간으로 되돌아갔습니다");
49+
}
50+
51+
if (lastTimestamp == timestamp) {
52+
sequence = (sequence + 1) & MAX_SEQUENCE;
53+
if (sequence == 0) {
54+
timestamp = tilNextMillis(lastTimestamp);
55+
}
56+
} else {
57+
sequence = 0;
58+
}
59+
60+
lastTimestamp = timestamp;
61+
return new SnowFlakeIdentifier(timestamp, datacenterId, workerId, sequence);
62+
}
63+
64+
private long tilNextMillis(long lastTimestamp) {
65+
long timestamp = System.currentTimeMillis() - epoch;
66+
while (timestamp <= lastTimestamp) {
67+
timestamp = System.currentTimeMillis() - epoch;
68+
}
69+
return timestamp;
70+
}
71+
}

0 commit comments

Comments
 (0)