
단일 DB Auto Increment에 의존하지 않고 다중 서버(멀티 노드)에서 동시 생성해도 충돌없는 고유 ID를 만드는 알고리즘
1 비트 : UNUSED
41 비트 : Timestamp (시간)
10 비트 : Node ID (서버 ID)
12 비트 : Sequence (동일 시간 내 증가값)
-----------------------------------------
총 64비트 = long
| 비트 | 필드 | 저장 내용 |
|---|---|---|
| 1 | UNUSED | 항상 0 (long 부호 비트) |
| 41 | Timestamp | 기준 시각부터 몇 ms 지났는지 |
| 10 | Node ID | 서버 ID(0~1023) |
| 12 | Sequence | 같은 ms 안에서 생성된 번호(0~4095) |
public class Snowflake {
private static final int UNUSED_BITS = 1;
private static final int EPOCH_BITS = 41;
private static final int NODE_ID_BITS = 10;
private static final int SEQUENCE_BITS = 12;
private static final long maxNodeId = (1L << NODE_ID_BITS) - 1;
private static final long maxSequence = (1L << SEQUENCE_BITS) - 1;
private final long nodeId = RandomGenerator.getDefault().nextLong(maxNodeId + 1);
private final long startTimeMillis = 1704067200000L;
private long lastTimeMillis = startTimeMillis;
private long sequence = 0L;
public synchronized long nextId() {
long currentTimeMillis = System.currentTimeMillis();
if (currentTimeMillis < lastTimeMillis) {
throw new IllegalStateException("Invalid Time");
}
if (currentTimeMillis == lastTimeMillis) {
sequence = (sequence + 1) & maxSequence;
if (sequence == 0) {
currentTimeMillis = waitNextMillis(currentTimeMillis);
}
} else {
sequence = 0;
}
lastTimeMillis = currentTimeMillis;
return ((currentTimeMillis - startTimeMillis) << (NODE_ID_BITS + SEQUENCE_BITS))
| (nodeId << SEQUENCE_BITS)
| sequence;
}
private long waitNextMillis(long currentTimestamp) {
while (currentTimestamp <= lastTimeMillis) {
currentTimestamp = System.currentTimeMillis();
}
return currentTimestamp;
}
}
Snowflake idGen = new Snowflake();
long id = idGen.nextId();
System.out.println(id); // 전 세계 고유 ID