Snowflake

Kooks·2025년 11월 18일

SpringBoot

목록 보기
4/7
post-thumbnail

Snowflake란?

단일 DB Auto Increment에 의존하지 않고 다중 서버(멀티 노드)에서 동시 생성해도 충돌없는 고유 ID를 만드는 알고리즘

  • DB INSERT 전에 ID를 만들어도 됨(DB 부하 감소)
  • 분산환경에서도 충돌 없음
  • 시간 순서대로 정렬 가능(정렬 성능 ↑)
  • 64 비트의 long값 하나로 표현 -> 빠르고 메모리 효율적
1  비트  : UNUSED  
41 비트  : Timestamp (시간)
10 비트  : Node ID (서버 ID)
12 비트  : Sequence (동일 시간 내 증가값)
-----------------------------------------
총 64비트 = long
비트필드저장 내용
1UNUSED항상 0 (long 부호 비트)
41Timestamp기준 시각부터 몇 ms 지났는지
10Node ID서버 ID(0~1023)
12Sequence같은 ms 안에서 생성된 번호(0~4095)

Code

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
profile
I'm kooks

0개의 댓글