🛠️

Whiz Tools

Build • Create • Innovate

Generator identyfikatorów Snowflake dla systemów rozproszonych

Generuj i analizuj identyfikatory Snowflake z Twittera, unikalne 64-bitowe identyfikatory używane w systemach rozproszonych. To narzędzie pozwala na tworzenie nowych identyfikatorów Snowflake oraz analizowanie istniejących, dostarczając informacji o ich znaczniku czasu, identyfikatorze maszyny i numerze sekwencyjnym.

Generator ID Snowflake

Generator ID Snowflake

Optional: Unix timestamp in milliseconds (defaults to current time)
📚

Dokumentacja

1## Generator ID Snowflake
2
3### Wprowadzenie
4
5ID Snowflake to unikalny identyfikator używany w systemach rozproszonych, pierwotnie opracowany przez Twittera. To narzędzie pozwala generować i analizować identyfikatory Snowflake, które są 64-bitowymi liczbami całkowitymi składającymi się z znacznika czasu, identyfikatora maszyny i numeru sekwencyjnego.
6
7### Jak działają identyfikatory Snowflake
8
9Identyfikatory Snowflake to 64-bitowe liczby całkowite, które mają następującą strukturę:
10
11- 41 bitów: Znacznik czasu (milisekundy od niestandardowej epoki)
12- 10 bitów: Identyfikator maszyny (5 bitów dla identyfikatora centrum danych, 5 bitów dla identyfikatora roboczego)
13- 12 bitów: Numer sekwencyjny
14
15Ta struktura pozwala na generowanie około 4,096 unikalnych identyfikatorów na milisekundę na maszynę.
16
17### Używanie generatora ID Snowflake
18
191. (Opcjonalnie) Ustaw niestandardową epokę (domyślnie epoka Twittera: 2010-11-04T01:42:54.657Z)
202. Wprowadź identyfikator maszyny (0-31) i identyfikator centrum danych (0-31)
213. Kliknij "Generuj", aby utworzyć nowy identyfikator Snowflake
224. Wygenerowany identyfikator i jego składniki zostaną wyświetlone
23
24Aby przeanalizować istniejący identyfikator Snowflake, wprowadź go w polu "Analizuj ID" i kliknij "Analizuj".
25
26### Wzór
27
28ID Snowflake jest konstruowane przy użyciu operacji bitowych:
29
30

plaintext ID = (znacznik_czasu << 22) | (identyfikator_centrum_danych << 17) | (identyfikator_roboczy << 12) | sekwencja

1
2Gdzie:
3- `znacznik_czasu` to liczba milisekund od epoki
4- `identyfikator_centrum_danych` to 5-bitowa liczba całkowita (0-31)
5- `identyfikator_roboczy` to 5-bitowa liczba całkowita (0-31)
6- `sekwencja` to 12-bitowa liczba całkowita (0-4095)
7
8### Obliczenia
9
10Generator ID Snowflake wykonuje następujące kroki:
11
121. Pobierz aktualny znacznik czasu w milisekundach
132. Upewnij się, że znacznik czasu jest większy niż ostatnio używany znacznik czasu (dla unikalności)
143. Jeśli znacznik czasu jest taki sam jak ostatni, zwiększ numer sekwencyjny
154. Jeśli numer sekwencyjny przekroczy (osiągnie 4096), poczekaj na następną milisekundę
165. Połącz składniki przy użyciu operacji bitowych, aby utworzyć ostateczny identyfikator
17
18### Przypadki użycia
19
20Identyfikatory Snowflake są szczególnie przydatne w:
21
221. Systemach rozproszonych: Generowanie unikalnych identyfikatorów na wielu maszynach bez koordynacji
232. Danych o dużej objętości: Tworzenie sortowalnych identyfikatorów dla dużych zbiorów danych
243. Mikrousługach: Zapewnienie unikalnych identyfikatorów w różnych usługach
254. Podziale bazy danych: Użycie komponentu znacznika czasu lub identyfikatora maszyny do efektywnego podziału
26
27#### Alternatywy
28
29Chociaż identyfikatory Snowflake są potężne, inne systemy generacji identyfikatorów to:
30
311. UUID (Uniwersalny unikalny identyfikator): Przydatny, gdy potrzebna jest rozproszona generacja bez sortowalności
322. Identyfikatory automatycznie inkrementowane w bazach danych: Proste, ale ograniczone do pojedynczych instancji baz danych
333. ULID (Uniwersalny unikalny identyfikator sortowalny leksykograficznie): Podobny do Snowflake, ale z inną strukturą
34
35### Przypadki graniczne i ograniczenia
36
371. Synchronizacja zegara: Identyfikatory Snowflake polegają na czasie systemowym. Jeśli zegar przesunie się wstecz z powodu korekcji NTP lub zmian czasu letniego, może to powodować problemy z generacją identyfikatorów.
38
392. Problem roku 2038: 41-bitowy znacznik czasu przepełni się w 2079 roku (zakładając epokę Twittera). Systemy korzystające z identyfikatorów Snowflake powinny planować na tę ewentualność.
40
413. Kolizje identyfikatorów maszyn: W dużych systemach rozproszonych zapewnienie unikalnych identyfikatorów maszyn może być wyzwaniem i może wymagać dodatkowej koordynacji.
42
434. Przepełnienie sekwencji: W ekstremalnych scenariuszach o wysokiej przepustowości możliwe jest wyczerpanie 4096 sekwencji na milisekundę, co może powodować opóźnienia.
44
455. Niekonsekwencja w czasie na różnych maszynach: Chociaż identyfikatory są monotonicznie rosnące na jednej maszynie, mogą nie być ściśle monotoniczne na wielu maszynach.
46
47### Historia
48
49Identyfikatory Snowflake zostały wprowadzone przez Twittera w 2010 roku, aby zaspokoić potrzebę rozproszonych, sortowalnych czasowo unikalnych identyfikatorów. Od tego czasu zostały przyjęte i dostosowane przez wiele innych firm i projektów.
50
51### Przykłady
52
53Oto implementacje generatorów ID Snowflake w różnych językach:
54
55

javascript class SnowflakeGenerator { constructor(epoch = 1288834974657, datacenterIdBits = 5, workerIdBits = 5, sequenceBits = 12) { this.epoch = BigInt(epoch); this.datacenterIdBits = datacenterIdBits; this.workerIdBits = workerIdBits; this.sequenceBits = sequenceBits; this.maxDatacenterId = -1n ^ (-1n << BigInt(datacenterIdBits)); this.maxWorkerId = -1n ^ (-1n << BigInt(workerIdBits)); this.sequenceMask = -1n ^ (-1n << BigInt(sequenceBits)); this.workerIdShift = BigInt(sequenceBits); this.datacenterIdShift = BigInt(sequenceBits + workerIdBits); this.timestampLeftShift = BigInt(sequenceBits + workerIdBits + datacenterIdBits); this.sequence = 0n; this.lastTimestamp = -1n; }

nextId(datacenterId, workerId) { let timestamp = this.currentTimestamp();

if (timestamp < this.lastTimestamp) {
  throw new Error('Zegar przesunął się wstecz. Odrzucam generację id');
}

if (timestamp === this.lastTimestamp) {
  this.sequence = (this.sequence + 1n) & this.sequenceMask;
  if (this.sequence === 0n) {
    timestamp = this.tilNextMillis(this.lastTimestamp);
  }
} else {
  this.sequence = 0n;
}

this.lastTimestamp = timestamp;

return ((timestamp - this.epoch) << this.timestampLeftShift) |
       (BigInt(datacenterId) << this.datacenterIdShift) |
       (BigInt(workerId) << this.workerIdShift) |
       this.sequence;

}

tilNextMillis(lastTimestamp) { let timestamp = this.currentTimestamp(); while (timestamp <= lastTimestamp) { timestamp = this.currentTimestamp(); } return timestamp; }

currentTimestamp() { return BigInt(Date.now()); } }

// Użycie const generator = new SnowflakeGenerator(); const id = generator.nextId(1, 1); console.log(Wygenerowany identyfikator Snowflake: ${id});

1
2

python import time import threading

class SnowflakeGenerator: def init(self, datacenter_id, worker_id, sequence=0): self.datacenter_id = datacenter_id self.worker_id = worker_id self.sequence = sequence

    self.last_timestamp = -1
    self.epoch = 1288834974657

    self.datacenter_id_bits = 5
    self.worker_id_bits = 5
    self.sequence_bits = 12

    self.max_datacenter_id = -1 ^ (-1 << self.datacenter_id_bits)
    self.max_worker_id = -1 ^ (-1 << self.worker_id_bits)

    self.worker_id_shift = self.sequence_bits
    self.datacenter_id_shift = self.sequence_bits + self.worker_id_bits
    self.timestamp_left_shift = self.sequence_bits + self.worker_id_bits + self.datacenter_id_bits
    self.sequence_mask = -1 ^ (-1 << self.sequence_bits)

    self._lock = threading.Lock()

def _til_next_millis(self, last_timestamp):
    timestamp = self._get_timestamp()
    while timestamp <= last_timestamp:
        timestamp = self._get_timestamp()
    return timestamp

def _get_timestamp(self):
    return int(time.time() * 1000)

def next_id(self):
    with self._lock:
        timestamp = self._get_timestamp()

        if timestamp < self.last_timestamp:
            raise ValueError("Zegar przesunął się wstecz. Odrzucam generację id")

        if timestamp == self.last_timestamp:
            self.sequence = (self.sequence + 1) & self.sequence_mask
            if self.sequence == 0:
                timestamp = self._til_next_millis(self.last_timestamp)
        else:
            self.sequence = 0

        self.last_timestamp = timestamp

        return ((timestamp - self.epoch) << self.timestamp_left_shift) | \
               (self.datacenter_id << self.datacenter_id_shift) | \
               (self.worker_id << self.worker_id_shift) | \
               self.sequence

Użycie

generator = SnowflakeGenerator(datacenter_id=1, worker_id=1) snowflake_id = generator.next_id() print(f"Wygenerowany identyfikator Snowflake: {snowflake_id}")

1
2

java import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;

public class SnowflakeGenerator { private final long epoch; private final long datacenterIdBits; private final long workerIdBits; private final long sequenceBits; private final long maxDatacenterId; private final long maxWorkerId; private final long workerIdShift; private final long datacenterIdShift; private final long timestampLeftShift; private final long sequenceMask;

private long datacenterId;
private long workerId;
private long sequence = 0L;
private long lastTimestamp = -1L;

private final Lock lock = new ReentrantLock();

public SnowflakeGenerator(long datacenterId, long workerId) {
    this.epoch = 1288834974657L;
    this.datacenterIdBits = 5L;
    this.workerIdBits = 5L;
    this.sequenceBits = 12L;

    this.maxDatacenterId = ~(-1L << datacenterIdBits);
    this.maxWorkerId = ~(-1L << workerIdBits);

    this.workerIdShift = sequenceBits;
    this.datacenterIdShift = sequenceBits + workerIdBits;
    this.timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    this.sequenceMask = ~(-1L << sequenceBits);

    if (datacenterId > maxDatacenterId || datacenterId < 0) {
        throw new IllegalArgumentException("datacenterId can't be greater than maxDatacenterId or less than 0");
    }
    if (workerId > maxWorkerId || workerId < 0) {
        throw new IllegalArgumentException("workerId can't be greater than maxWorkerId or less than 0");
    }
    this.datacenterId = datacenterId;
    this.workerId = workerId;
}

public long nextId() {
    lock.lock();
    try {
        long timestamp = timeGen();
        if (timestamp < lastTimestamp) {
            throw new RuntimeException("Zegar przesunął się wstecz. Odrzucam generację id");
        }

        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }

        lastTimestamp = timestamp;

        return ((timestamp - epoch) << timestampLeftShift) |
               (datacenterId << datacenterIdShift) |
               (workerId << workerIdShift) |
               sequence;
    } finally {
        lock.unlock();
    }
}

private long tilNextMillis(long lastTimestamp) {
    long timestamp = timeGen();
    while (timestamp <= lastTimestamp) {
        timestamp = timeGen();
    }
    return timestamp;
}

private long timeGen() {
    return System.currentTimeMillis();
}

public static void main(String[] args) {
    SnowflakeGenerator generator = new SnowflakeGenerator(1, 1);
    long id = generator.nextId();
    System.out.println("Wygenerowany identyfikator Snowflake: " + id);
}

}

1
2

ruby require 'time'

class SnowflakeGenerator def initialize(datacenter_id, worker_id, sequence = 0) @datacenter_id = datacenter_id @worker_id = worker_id @sequence = sequence @last_timestamp = -1 @epoch = 1288834974657

@datacenter_id_bits = 5
@worker_id_bits = 5
@sequence_bits = 12

@max_datacenter_id = -1 ^ (-1 << @datacenter_id_bits)
@max_worker_id = -1 ^ (-1 << @worker_id_bits)

@worker_id_shift = @sequence_bits
@datacenter_id_shift = @sequence_bits + @worker_id_bits
@timestamp_left_shift = @sequence_bits + @worker_id_bits + @datacenter_id_bits
@sequence_mask = -1 ^ (-1 << @sequence_bits)

end

def next_id timestamp = (Time.now.to_f * 1000).to_i

raise 'Zegar przesunął się wstecz' if timestamp < @last_timestamp

if timestamp == @last_timestamp
  @sequence = (@sequence + 1) & @sequence_mask
  timestamp = til_next_millis(@last_timestamp) if @sequence == 0
else
  @sequence = 0
end

@last_timestamp = timestamp

((timestamp - @epoch) << @timestamp_left_shift) |
  (@datacenter_id << @datacenter_id_shift) |
  (@worker_id << @worker_id_shift) |
  @sequence

end

private

def til_next_millis(last_timestamp) timestamp = (Time.now.to_f * 1000).to_i timestamp = (Time.now.to_f * 1000).to_i while timestamp <= last_timestamp timestamp end end

Użycie

generator = SnowflakeGenerator.new(1, 1) snowflake_id = generator.next_id puts "Wygenerowany identyfikator Snowflake: #{snowflake_id}"

1
2

php

epoch = 1288834974657; $this->datacenterIdBits = 5; $this->workerIdBits = 5; $this->sequenceBits = 12; $this->maxDatacenterId = -1 ^ (-1 << $this->datacenterIdBits); $this->maxWorkerId = -1 ^ (-1 << $this->workerIdBits); $this->workerIdShift = $this->sequenceBits; $this->datacenterIdShift = $this->sequenceBits + $this->workerIdBits; $this->timestampLeftShift = $this->sequenceBits + $this->workerIdBits + $this->datacenterIdBits; $this->sequenceMask = -1 ^ (-1 << $this->sequenceBits); if ($datacenterId > $this->maxDatacenterId || $datacenterId < 0) { throw new Exception("datacenterId can't be greater than maxDatacenterId or less than 0"); } if ($workerId > $this->maxWorkerId || $workerId < 0) { throw new Exception("workerId can't be greater than maxWorkerId or less than 0"); } $this->datacenterId = $datacenterId; $this->workerId = $workerId; } public function nextId() { $timestamp = $this->timeGen(); if ($timestamp < $this->lastTimestamp) { throw new Exception("Zegar przesunął się wstecz. Odrzucam generację id"); } if ($this->lastTimestamp == $timestamp) { $this->sequence = ($this->sequence + 1) & $this->sequenceMask; if ($this->sequence == 0) { $timestamp = $this->tilNextMillis($this->lastTimestamp); } } else { $this->sequence = 0; } $this->lastTimestamp = $timestamp; return (($timestamp - $this->epoch) << $this->timestampLeftShift) | ($this->datacenterId << $this->datacenterIdShift) | ($this->workerId << $this->workerIdShift) | $this->sequence; } private function tilNextMillis($lastTimestamp) { $timestamp = $this->timeGen(); while ($timestamp <= $lastTimestamp) { $timestamp = $this->timeGen(); } return $timestamp; } private function timeGen() { return floor(microtime(true) * 1000); } } // Użycie $generator = new SnowflakeGenerator(1, 1); $id = $generator->nextId(); echo "Wygenerowany identyfikator Snowflake: " . $id . "\n";
1
2

csharp using System; using System.Threading;

public class SnowflakeGenerator { private readonly long _epoch; private readonly int _datacenterIdBits; private readonly int _workerIdBits; private readonly int _sequenceBits; private readonly long _maxDatacenterId; private readonly long _maxWorkerId; private readonly int _workerIdShift; private readonly int _datacenterIdShift; private readonly int _timestampLeftShift; private readonly long _sequenceMask;

private readonly long _datacenterId;
private readonly long _workerId;
private long _sequence = 0L;
private long _lastTimestamp = -1L;

private readonly object _lock = new object();

public SnowflakeGenerator(long datacenterId, long workerId)
{
    _epoch = 1288834974657L;
    _datacenterIdBits = 5;
    _workerIdBits = 5;
    _sequenceBits = 12;

    _maxDatacenterId = -1L ^ (-1L << _datacenterIdBits);
    _maxWorkerId = -1L ^ (-1L << _workerIdBits);

    _workerIdShift = _sequenceBits;
    _datacenterIdShift = _sequenceBits + _workerIdBits;
    _timestampLeftShift = _sequenceBits + _workerIdBits + _datacenterIdBits;
    _sequenceMask = -1L ^ (-1L << _sequenceBits);

    if (datacenterId > _maxDatacenterId || datacenterId < 0)
    {
        throw new ArgumentException($"datacenterId can't be greater than {_maxDatacenterId} or less than 0");
    }
    if (workerId > _maxWorkerId || workerId < 0)
    {
        throw new ArgumentException($"workerId can't be greater than {_maxWorkerId} or less than 0");
    }
    _datacenterId = datacenterId;
    _workerId = workerId;
}

public long NextId()
{
    lock (_lock)
    {
        var timestamp = TimeGen();

        if (timestamp < _lastTimestamp)
        {
            throw new Exception("Zegar przesunął się wstecz. Odrzucam generację id");
        }

        if (_lastTimestamp == timestamp)
        {
            _sequence = (_sequence + 1) & _sequenceMask;
            if (_sequence == 0)
            {
                timestamp = TilNextMillis(_lastTimestamp);
            }
        }
        else
        {
            _sequence = 0L;
        }

        _lastTimestamp = timestamp;

        return ((timestamp - _epoch) << _timestampLeftShift) |
               (_datacenterId << _datacenterIdShift) |
               (_workerId << _workerIdShift) |
               _sequence;
    }
}

private long TilNextMillis(long lastTimestamp)
{
    var timestamp = TimeGen();
    while (timestamp <= lastTimestamp)
    {
        timestamp = TimeGen();
    }
    return timestamp;
}

private long TimeGen()
{
    return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
}

}

// Użycie class Program { static void Main(string[] args) { var generator = new SnowflakeGenerator(1, 1); var id = generator.NextId(); Console.WriteLine($"Wygenerowany identyfikator Snowflake: {id}"); } }

1
2

go package main

import ( "fmt" "sync" "time" )

type SnowflakeGenerator struct { epoch int64 datacenterIdBits uint workerIdBits uint sequenceBits uint maxDatacenterId int64 maxWorkerId int64 workerIdShift uint datacenterIdShift uint timestampLeftShift uint sequenceMask int64

datacenterId int64
workerId     int64
sequence     int64
lastTimestamp int64

lock sync.Mutex

}

func NewSnowflakeGenerator(datacenterId, workerId int64) (*SnowflakeGenerator, error) { g := &SnowflakeGenerator{ epoch: 1288834974657, datacenterIdBits: 5, workerIdBits: 5, sequenceBits: 12, lastTimestamp: -1, }

g.maxDatacenterId = -1 ^ (-1 << g.datacenterIdBits)
g.maxWorkerId = -1 ^ (-1 << g.workerIdBits)

g.workerIdShift = g.sequenceBits
g.datacenterIdShift = g.sequenceBits + g.workerIdBits
g.timestampLeftShift = g.sequenceBits + g.workerIdBits + g.datacenterIdBits
g.sequenceMask = -1 ^ (-1 << g.sequenceBits)

if datacenterId > g.maxDatacenterId || datacenterId < 0 {
	return nil, fmt.Errorf("datacenterId can't be greater than %d or less than 0", g.maxDatacenterId)
}
if workerId > g.maxWorkerId || workerId < 0 {
	return nil, fmt.Errorf("workerId can't be greater than %d or less than 0", g.maxWorkerId)
}
g.datacenterId = datacenterId
g.workerId = workerId

return g, nil

}

func (g *SnowflakeGenerator) NextId() (int64, error) { g.lock.Lock() defer g.lock.Unlock()

timestamp := g.timeGen()

if timestamp < g.lastTimestamp {
	return 0, fmt.Errorf("zegar przesunął się wstecz, odrzucam generację id")
}

if g.lastTimestamp == timestamp {
	g.sequence = (g.sequence + 1) & g.sequenceMask
	if g.sequence == 0 {
		timestamp = g.tilNextMillis(g.lastTimestamp)
	}
} else {
	g.sequence = 0
}

g.lastTimestamp = timestamp

return ((timestamp - g.epoch) << g.timestampLeftShift) |
	(g.datacenterId << g.datacenterIdShift) |
	(g.workerId << g.workerIdShift) |
	g.sequence, nil

}

func (g *SnowflakeGenerator) tilNextMillis(lastTimestamp int64) int64 { timestamp := g.timeGen() for timestamp <= lastTimestamp { timestamp = g.timeGen() } return timestamp }

func (g *SnowflakeGenerator) timeGen() int64 { return time.Now().UnixNano() / int64(time.Millisecond) }

func main() { generator, err := NewSnowflakeGenerator(1, 1) if err != nil { fmt.Printf("Błąd podczas tworzenia generatora: %v\n", err) return }

id, err := generator.NextId()
if err != nil {
	fmt.Printf("Błąd podczas generacji ID: %v\n", err)
	return
}

fmt.Printf("Wygenerowany identyfikator Snowflake: %d\n", id)

}

1
2### Diagram
3
4Oto wizualna reprezentacja struktury identyfikatora Snowflake:
5
6<svg width="600" height="100" xmlns="http://www.w3.org/2000/svg">
7  <rect x="0" y="0" width="380" height="50" fill="#4299e1"/>
8  <text x="190" y="30" font-family="Arial" fontSize="14" fill="white" textAnchor="middle">Znacznik czasu (41 bitów)</text>
9  
10  <rect x="380" y="0" width="90" height="50" fill="#48bb78"/>
11  <text x="425" y="30" font-family="Arial" fontSize="14" fill="white" textAnchor="middle">Identyfikator maszyny (10 bitów)</text>
12  
13  <rect x="470" y="0" width="130" height="50" fill="#ed8936"/>
14  <text x="535" y="30" font-family="Arial" fontSize="14" fill="white" textAnchor="middle">Sekwencja (12 bitów)</text>
15  
16  <text x="300" y="80" font-family="Arial" fontSize="16" fill="black" textAnchor="middle">Struktura identyfikatora Snowflake 64-bitowego</text>
17</svg>
18
19### Źródła
20
211. "Ogłoszenie Snowflake." Blog inżynieryjny Twittera, https://blog.twitter.com/engineering/en_us/a/2010/announcing-snowflake
222. "ID Snowflake." Wikipedia, https://en.wikipedia.org/wiki/Snowflake_ID
233. "Rozproszona generacja ID w mikrousługach." Medium, https://medium.com/swlh/distributed-id-generation-in-microservices-b6ce9a8dd93f
24