반응형
Snowflake란?
- Snowflake는 분산 컴퓨팅 환경에서 고유한 식별자를 생성할 수 있게 해주는 방법을 말한다.
- 트위터에서 개발했다.
- timestamp, machineId, sequence를 64bits 데이터로 관리한다.
- 64bits로 관리하는 이유는 signed long type이 64bits이기 때문인듯 하다.
Snowflake Format
- timestamp
- 현재시간에서 기준시간을 뺀 값을 말한다.
- 트위터에서는 기준시간을 2010년쯤으로 잡았다.
- 42bits로 관리되고, 첫 bit는 양수를 나타내는 bit이다.
- 실질적으로 41bits를 사용 가능하므로, 2^41 milis 미만의 시간만큼 관리할 수 있다.
- 1L << 4 - 1 = 대략 34 ~ 35년
- 서비스가 오래 유지될 것 같다면 timestamp의 bits를 늘린다.
- 현재시간에서 기준시간을 뺀 값을 말한다.
- machineId
- 다중 서버 환경에서 서버 머신을 구분지을 수 있는(서버별로 유니크한) ID를 말한다.
- 10bits로 관리된다.
- 2^10 미만의 machineId를 사용할 수 있다.
- 1L << 10 - 1 = 최대 512
- 서버가 여러대 투입이 필요할 것 같다면 machineId의 bits를 늘린다.
- sequence
- 한 서버에서 동일 시간에 생성된 아이디를 유니크하게 만들어주는 값을 말한다.
- 12bits로 관리된다.
- 2^12 미만의 sequence를 사용할 수 있다.
- 1L << 12 - 1 = 최대 2048
- 한 서버에 동시 요청이 많이 들어올 것 같다면 sequence의 bits를 늘린다.
예제 코드
@RequiredArgsConstructor
public class Snowflake {
private final long baseTimestamp;
private final long machineId;
private static final AtomicLong sequence = new AtomicLong();
private static final int TIMESTAMP_BITS = 42;
private static final int MACHINE_ID_BITS = 10;
private static final int SEQUENCE_BITS = 12;
public long generateId(Long currentTimestamp) {
long timestamp = currentTimestamp - baseTimestamp;
return (timestamp << MACHINE_ID_BITS + SEQUENCE_BITS) | (machineId << SEQUENCE_BITS) | value(sequence.getAndIncrement(), SEQUENCE_BITS, 0);
}
public long generateIdWithTimestampOnly(Long currentTimestamp) {
long timestamp = currentTimestamp - baseTimestamp;
return (timestamp << MACHINE_ID_BITS + SEQUENCE_BITS);
}
public long getTimestamp(Long id) {
return value(id, TIMESTAMP_BITS, MACHINE_ID_BITS + SEQUENCE_BITS) + baseTimestamp;
}
public long getMachineId(Long id) {
return value(id, MACHINE_ID_BITS, SEQUENCE_BITS);
}
public long getSequence(Long id) {
return value(id, SEQUENCE_BITS, 0);
}
private long value(long value, int bits, int shift) {
long mask = (((1L << bits) - 1) << shift);
return (value & mask) >> shift;
}
}
public class SnowflakeTest {
@Test
public void testSnowflake() {
// given
long twitterEpoch = 1288834974657L; // June 28, 2022 16:07:40.105 UTC
long currentTimestamp = 1704034800000L; // 2024-01-01 00:00
Snowflake snowflake1 = new Snowflake(twitterEpoch, 1);
Snowflake snowflake2 = new Snowflake(twitterEpoch, 2);
// when
long id1 = snowflake1.generateId(currentTimestamp);
long id2 = snowflake2.generateId(currentTimestamp);
long id3 = snowflake2.generateId(currentTimestamp);
// then
assertEquals(1741474288235450368L, id1);
assertEquals(1741474288235454465L, id2);
assertEquals(1741474288235454466L, id3);
assertEquals(1741474288235446272L, snowflake1.generateIdWithTimestampOnly(currentTimestamp));
assertEquals(1741474288235446272L, snowflake2.generateIdWithTimestampOnly(currentTimestamp));
assertEquals(currentTimestamp, snowflake1.getTimestamp(id1));
assertEquals(currentTimestamp, snowflake1.getTimestamp(id2));
assertEquals(currentTimestamp, snowflake1.getTimestamp(id3));
assertEquals(1L, snowflake1.getMachineId(id1));
assertEquals(2L, snowflake2.getMachineId(id2));
assertEquals(2L, snowflake2.getMachineId(id3));
assertEquals(0L, snowflake1.getSequence(id1));
assertEquals(1L, snowflake2.getSequence(id2));
assertEquals(2L, snowflake2.getSequence(id3));
}
}
MySQL 쿼리로 snowflake 계산하기
datetime to snowflake
SELECT (UNIX_TIMESTAMP('2024-09-01 00:00:00') * 1000 - UNIX_TIMESTAMP('2024-01-01 00:00:00') * 1000) << 22; -- 88422639206400000
snowflake to datetime
SELECT FROM_UNIXTIME((88422639206400000 >> 22) / 1000 + UNIX_TIMESTAMP('2024-01-01 00:00:00')); -- 2024-09-01 00:00:00.0000
참고
반응형
'Development > Java' 카테고리의 다른 글
[Java] HtmlUnit (8) | 2024.10.06 |
---|---|
[Java] Java 소스 파일 컴파일하여 Class 객체로 로딩하기 (0) | 2023.05.27 |
[Java] Mockito (0) | 2022.06.23 |
[Java] Random (0) | 2022.06.21 |
[Java] Cipher(RSA, AES) (0) | 2022.04.27 |