반응형

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

+ Recent posts