Twitter Snowflake ID Generator for Distributed Systems
Generer og analyser Twitter Snowflake ID-er, unike 64-bit identifikatorer brukt i distribuerte systemer. Dette verktøyet lar deg opprette nye Snowflake ID-er og analysere eksisterende, og gir innsikt i deres tidsstempel, maskin-ID og sekvensnummerkomponenter.
Snowflake ID-generator
Snowflake ID-generator
Dokumentasjon
Snowflake ID Generator
Introduksjon
En Snowflake ID er en unik identifikator som brukes i distribuerte systemer, opprinnelig utviklet av Twitter. Dette verktøyet lar deg generere og analysere Snowflake ID-er, som er 64-biters heltall sammensatt av et tidsstempel, maskin-ID og sekvensnummer.
Hvordan Snowflake ID-er fungerer
Snowflake ID-er er 64-biters heltall strukturert som følger:
- 41 biter: Tidsstempel (millisekunder siden en tilpasset epoke)
- 10 biter: Maskin-ID (5 biter for datasenter-ID, 5 biter for arbeider-ID)
- 12 biter: Sekvensnummer
Denne strukturen gjør det mulig å generere omtrent 4 096 unike ID-er per millisekund per maskin.
Bruke Snowflake ID Generator
- (Valgfritt) Sett en tilpasset epoke (standard er Twitters epoke: 2010-11-04T01:42:54.657Z)
- Skriv inn en maskin-ID (0-31) og datasenter-ID (0-31)
- Klikk "Generer" for å lage en ny Snowflake ID
- Den genererte ID-en og dens komponenter vil bli vist
For å analysere en eksisterende Snowflake ID, skriv den inn i feltet "Analyser ID" og klikk "Analyser".
Formel
Snowflake ID-en er konstruert ved hjelp av bitvise operasjoner:
1ID = (tidsstempel << 22) | (datasenterId << 17) | (arbeiderId << 12) | sekvens
2
Hvor:
tidsstempel
er antall millisekunder siden epokendatasenterId
er et 5-biters heltall (0-31)arbeiderId
er et 5-biters heltall (0-31)sekvens
er et 12-biters heltall (0-4095)
Beregning
Snowflake ID-generatoren utfører følgende trinn:
- Hent det nåværende tidsstempelet i millisekunder
- Sørg for at tidsstempelet er større enn det sist brukte tidsstempelet (for unikhet)
- Hvis tidsstempelet er det samme som det forrige, øk sekvensnummeret
- Hvis sekvensnummeret går over (når 4096), vent på neste millisekund
- Kombiner komponentene ved hjelp av bitvise operasjoner for å lage den endelige ID-en
Bruksområder
Snowflake ID-er er spesielt nyttige i:
- Distribuerte systemer: Generere unike ID-er på tvers av flere maskiner uten koordinering
- Høyvolumdata: Opprette sorterbare ID-er for store datasett
- Mikrotjenester: Sikre unike identifikatorer på tvers av forskjellige tjenester
- Databaseshardering: Bruke tidsstempel- eller maskin-ID-komponenten for effektiv hardering
Alternativer
Selv om Snowflake ID-er er kraftige, inkluderer andre ID-genereringssystemer:
- UUID (Universally Unique Identifier): Nyttig når distribuerte generering er nødvendig uten sortering
- Auto-inkrementerende database-ID-er: Enkle, men begrenset til enkelt databaseinstanser
- ULID (Universally Unique Lexicographically Sortable Identifier): Ligner på Snowflake, men med en annen struktur
Edge Cases og Begrensninger
-
Klokkesynkronisering: Snowflake ID-er er avhengige av systemtiden. Hvis klokken går tilbake på grunn av NTP-justeringer eller endringer i sommertid, kan det forårsake problemer med ID-generering.
-
År 2038-problemet: Det 41-bits tidsstempelet vil overflyte i 2079 (forutsatt Twitters epoke). Systemer som bruker Snowflake ID-er bør planlegge for denne eventualiteten.
-
Maskin-ID-kollisjoner: I store distribuerte systemer kan det være utfordrende å sikre unike maskin-ID-er og kan kreve ytterligere koordinering.
-
Sekvensoverløp: I ekstremt høy gjennomstrømning kan det være mulig å tømme de 4096 sekvensene per millisekund, noe som potensielt kan forårsake forsinkelser.
-
Ikke-monotonitet på tvers av maskiner: Selv om ID-er øker monotonisk på en enkelt maskin, kan de ikke være strengt monotone på tvers av flere maskiner.
Historie
Snowflake ID-er ble introdusert av Twitter i 2010 for å møte behovet for distribuerte, tids-sorterbare unike identifikatorer. De har siden blitt adoptert og tilpasset av mange andre selskaper og prosjekter.
Eksempler
Her er implementeringer av Snowflake ID-generatorer i forskjellige språk:
1class SnowflakeGenerator {
2 constructor(epoch = 1288834974657, datacenterIdBits = 5, workerIdBits = 5, sequenceBits = 12) {
3 this.epoch = BigInt(epoch);
4 this.datacenterIdBits = datacenterIdBits;
5 this.workerIdBits = workerIdBits;
6 this.sequenceBits = sequenceBits;
7 this.maxDatacenterId = -1n ^ (-1n << BigInt(datacenterIdBits));
8 this.maxWorkerId = -1n ^ (-1n << BigInt(workerIdBits));
9 this.sequenceMask = -1n ^ (-1n << BigInt(sequenceBits));
10 this.workerIdShift = BigInt(sequenceBits);
11 this.datacenterIdShift = BigInt(sequenceBits + workerIdBits);
12 this.timestampLeftShift = BigInt(sequenceBits + workerIdBits + datacenterIdBits);
13 this.sequence = 0n;
14 this.lastTimestamp = -1n;
15 }
16
17 nextId(datacenterId, workerId) {
18 let timestamp = this.currentTimestamp();
19
20 if (timestamp < this.lastTimestamp) {
21 throw new Error('Klokken har flyttet seg bakover. Nekter å generere id');
22 }
23
24 if (timestamp === this.lastTimestamp) {
25 this.sequence = (this.sequence + 1n) & this.sequenceMask;
26 if (this.sequence === 0n) {
27 timestamp = this.tilNextMillis(this.lastTimestamp);
28 }
29 } else {
30 this.sequence = 0n;
31 }
32
33 this.lastTimestamp = timestamp;
34
35 return ((timestamp - this.epoch) << this.timestampLeftShift) |
36 (BigInt(datacenterId) << this.datacenterIdShift) |
37 (BigInt(workerId) << this.workerIdShift) |
38 this.sequence;
39 }
40
41 tilNextMillis(lastTimestamp) {
42 let timestamp = this.currentTimestamp();
43 while (timestamp <= lastTimestamp) {
44 timestamp = this.currentTimestamp();
45 }
46 return timestamp;
47 }
48
49 currentTimestamp() {
50 return BigInt(Date.now());
51 }
52}
53
54// Bruk
55const generator = new SnowflakeGenerator();
56const id = generator.nextId(1, 1);
57console.log(`Generert Snowflake ID: ${id}`);
58
1import time
2import threading
3
4class SnowflakeGenerator:
5 def __init__(self, datacenter_id, worker_id, sequence=0):
6 self.datacenter_id = datacenter_id
7 self.worker_id = worker_id
8 self.sequence = sequence
9
10 self.last_timestamp = -1
11 self.epoch = 1288834974657
12
13 self.datacenter_id_bits = 5
14 self.worker_id_bits = 5
15 self.sequence_bits = 12
16
17 self.max_datacenter_id = -1 ^ (-1 << self.datacenter_id_bits)
18 self.max_worker_id = -1 ^ (-1 << self.worker_id_bits)
19
20 self.worker_id_shift = self.sequence_bits
21 self.datacenter_id_shift = self.sequence_bits + self.worker_id_bits
22 self.timestamp_left_shift = self.sequence_bits + self.worker_id_bits + self.datacenter_id_bits
23 self.sequence_mask = -1 ^ (-1 << self.sequence_bits)
24
25 self._lock = threading.Lock()
26
27 def _til_next_millis(self, last_timestamp):
28 timestamp = self._get_timestamp()
29 while timestamp <= last_timestamp:
30 timestamp = self._get_timestamp()
31 return timestamp
32
33 def _get_timestamp(self):
34 return int(time.time() * 1000)
35
36 def next_id(self):
37 with self._lock:
38 timestamp = self._get_timestamp()
39
40 if timestamp < self.last_timestamp:
41 raise ValueError("Klokken har flyttet seg bakover. Nekter å generere id")
42
43 if timestamp == self.last_timestamp:
44 self.sequence = (self.sequence + 1) & self.sequence_mask
45 if self.sequence == 0:
46 timestamp = self._til_next_millis(self.last_timestamp)
47 else:
48 self.sequence = 0
49
50 self.last_timestamp = timestamp
51
52 return ((timestamp - self.epoch) << self.timestamp_left_shift) | \
53 (self.datacenter_id << self.datacenter_id_shift) | \
54 (self.worker_id << self.worker_id_shift) | \
55 self.sequence
56
57## Bruk
58generator = SnowflakeGenerator(datacenter_id=1, worker_id=1)
59snowflake_id = generator.next_id()
60print(f"Generert Snowflake ID: {snowflake_id}")
61
1import java.util.concurrent.locks.Lock;
2import java.util.concurrent.locks.ReentrantLock;
3
4public class SnowflakeGenerator {
5 private final long epoch;
6 private final long datacenterIdBits;
7 private final long workerIdBits;
8 private final long sequenceBits;
9 private final long maxDatacenterId;
10 private final long maxWorkerId;
11 private final long workerIdShift;
12 private final long datacenterIdShift;
13 private final long timestampLeftShift;
14 private final long sequenceMask;
15
16 private long datacenterId;
17 private long workerId;
18 private long sequence = 0L;
19 private long lastTimestamp = -1L;
20
21 private final Lock lock = new ReentrantLock();
22
23 public SnowflakeGenerator(long datacenterId, long workerId) {
24 this.epoch = 1288834974657L;
25 this.datacenterIdBits = 5L;
26 this.workerIdBits = 5L;
27 this.sequenceBits = 12L;
28
29 this.maxDatacenterId = ~(-1L << datacenterIdBits);
30 this.maxWorkerId = ~(-1L << workerIdBits);
31
32 this.workerIdShift = sequenceBits;
33 this.datacenterIdShift = sequenceBits + workerIdBits;
34 this.timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
35 this.sequenceMask = ~(-1L << sequenceBits);
36
37 if (datacenterId > maxDatacenterId || datacenterId < 0) {
38 throw new IllegalArgumentException("datasenterId kan ikke være større enn maxDatacenterId eller mindre enn 0");
39 }
40 if (workerId > maxWorkerId || workerId < 0) {
41 throw new IllegalArgumentException("arbeiderId kan ikke være større enn maxWorkerId eller mindre enn 0");
42 }
43 this.datacenterId = datacenterId;
44 this.workerId = workerId;
45 }
46
47 public long nextId() {
48 lock.lock();
49 try {
50 long timestamp = timeGen();
51 if (timestamp < lastTimestamp) {
52 throw new RuntimeException("Klokken har flyttet seg bakover. Nekter å generere id");
53 }
54
55 if (lastTimestamp == timestamp) {
56 sequence = (sequence + 1) & sequenceMask;
57 if (sequence == 0) {
58 timestamp = tilNextMillis(lastTimestamp);
59 }
60 } else {
61 sequence = 0L;
62 }
63
64 lastTimestamp = timestamp;
65
66 return ((timestamp - epoch) << timestampLeftShift) |
67 (datacenterId << datacenterIdShift) |
68 (workerId << workerIdShift) |
69 sequence;
70 } finally {
71 lock.unlock();
72 }
73 }
74
75 private long tilNextMillis(long lastTimestamp) {
76 long timestamp = timeGen();
77 while (timestamp <= lastTimestamp) {
78 timestamp = timeGen();
79 }
80 return timestamp;
81 }
82
83 private long timeGen() {
84 return System.currentTimeMillis();
85 }
86
87 public static void main(String[] args) {
88 SnowflakeGenerator generator = new SnowflakeGenerator(1, 1);
89 long id = generator.nextId();
90 System.out.println("Generert Snowflake ID: " + id);
91 }
92}
93
1require 'time'
2
3class SnowflakeGenerator
4 def initialize(datacenter_id, worker_id, sequence = 0)
5 @datacenter_id = datacenter_id
6 @worker_id = worker_id
7 @sequence = sequence
8 @last_timestamp = -1
9 @epoch = 1288834974657
10
11 @datacenter_id_bits = 5
12 @worker_id_bits = 5
13 @sequence_bits = 12
14
15 @max_datacenter_id = -1 ^ (-1 << @datacenter_id_bits)
16 @max_worker_id = -1 ^ (-1 << @worker_id_bits)
17
18 @worker_id_shift = @sequence_bits
19 @datacenter_id_shift = @sequence_bits + @worker_id_bits
20 @timestamp_left_shift = @sequence_bits + @worker_id_bits + @datacenter_id_bits
21 @sequence_mask = -1 ^ (-1 << @sequence_bits)
22 end
23
24 def next_id
25 timestamp = (Time.now.to_f * 1000).to_i
26
27 raise 'Klokken har flyttet seg bakover' if timestamp < @last_timestamp
28
29 if timestamp == @last_timestamp
30 @sequence = (@sequence + 1) & @sequence_mask
31 timestamp = til_next_millis(@last_timestamp) if @sequence == 0
32 else
33 @sequence = 0
34 end
35
36 @last_timestamp = timestamp
37
38 ((timestamp - @epoch) << @timestamp_left_shift) |
39 (@datacenter_id << @datacenter_id_shift) |
40 (@worker_id << @worker_id_shift) |
41 @sequence
42 end
43
44 private
45
46 def til_next_millis(last_timestamp)
47 timestamp = (Time.now.to_f * 1000).to_i
48 timestamp = (Time.now.to_f * 1000).to_i while timestamp <= last_timestamp
49 timestamp
50 end
51end
52
53## Bruk
54generator = SnowflakeGenerator.new(1, 1)
55snowflake_id = generator.next_id
56puts "Generert Snowflake ID: #{snowflake_id}"
57
1<?php
2
3class SnowflakeGenerator {
4 private $epoch;
5 private $datacenterIdBits;
6 private $workerIdBits;
7 private $sequenceBits;
8 private $maxDatacenterId;
9 private $maxWorkerId;
10 private $workerIdShift;
11 private $datacenterIdShift;
12 private $timestampLeftShift;
13 private $sequenceMask;
14
15 private $datacenterId;
16 private $workerId;
17 private $sequence = 0;
18 private $lastTimestamp = -1;
19
20 public function __construct($datacenterId, $workerId) {
21 $this->epoch = 1288834974657;
22 $this->datacenterIdBits = 5;
23 $this->workerIdBits = 5;
24 $this->sequenceBits = 12;
25
26 $this->maxDatacenterId = -1 ^ (-1 << $this->datacenterIdBits);
27 $this->maxWorkerId = -1 ^ (-1 << $this->workerIdBits);
28
29 $this->workerIdShift = $this->sequenceBits;
30 $this->datacenterIdShift = $this->sequenceBits + $this->workerIdBits;
31 $this->timestampLeftShift = $this->sequenceBits + $this->workerIdBits + $this->datacenterIdBits;
32 $this->sequenceMask = -1 ^ (-1 << $this->sequenceBits);
33
34 if ($datacenterId > $this->maxDatacenterId || $datacenterId < 0) {
35 throw new Exception("datasenterId kan ikke være større enn maxDatacenterId eller mindre enn 0");
36 }
37 if ($workerId > $this->maxWorkerId || $workerId < 0) {
38 throw new Exception("arbeiderId kan ikke være større enn maxWorkerId eller mindre enn 0");
39 }
40 $this->datacenterId = $datacenterId;
41 $this->workerId = $workerId;
42 }
43
44 public function nextId() {
45 $timestamp = $this->timeGen();
46
47 if ($timestamp < $this->lastTimestamp) {
48 throw new Exception("Klokken har flyttet seg bakover. Nekter å generere id");
49 }
50
51 if ($this->lastTimestamp == $timestamp) {
52 $this->sequence = ($this->sequence + 1) & $this->sequenceMask;
53 if ($this->sequence == 0) {
54 $timestamp = $this->tilNextMillis($this->lastTimestamp);
55 }
56 } else {
57 $this->sequence = 0;
58 }
59
60 $this->lastTimestamp = $timestamp;
61
62 return (($timestamp - $this->epoch) << $this->timestampLeftShift) |
63 ($this->datacenterId << $this->datacenterIdShift) |
64 ($this->workerId << $this->workerIdShift) |
65 $this->sequence;
66 }
67
68 private function tilNextMillis($lastTimestamp) {
69 $timestamp = $this->timeGen();
70 while ($timestamp <= $lastTimestamp) {
71 $timestamp = $this->timeGen();
72 }
73 return $timestamp;
74 }
75
76 private function timeGen() {
77 return floor(microtime(true) * 1000);
78 }
79}
80
81// Bruk
82$generator = new SnowflakeGenerator(1, 1);
83$id = $generator->nextId();
84echo "Generert Snowflake ID: " . $id . "\n";
85
1using System;
2using System.Threading;
3
4public class SnowflakeGenerator
5{
6 private readonly long _epoch;
7 private readonly int _datacenterIdBits;
8 private readonly int _workerIdBits;
9 private readonly int _sequenceBits;
10 private readonly long _maxDatacenterId;
11 private readonly long _maxWorkerId;
12 private readonly int _workerIdShift;
13 private readonly int _datacenterIdShift;
14 private readonly int _timestampLeftShift;
15 private readonly long _sequenceMask;
16
17 private readonly long _datacenterId;
18 private readonly long _workerId;
19 private long _sequence = 0L;
20 private long _lastTimestamp = -1L;
21
22 private readonly object _lock = new object();
23
24 public SnowflakeGenerator(long datacenterId, long workerId)
25 {
26 _epoch = 1288834974657L;
27 _datacenterIdBits = 5;
28 _workerIdBits = 5;
29 _sequenceBits = 12;
30
31 _maxDatacenterId = -1L ^ (-1L << _datacenterIdBits);
32 _maxWorkerId = -1L ^ (-1L << _workerIdBits);
33
34 _workerIdShift = _sequenceBits;
35 _datacenterIdShift = _sequenceBits + _workerIdBits;
36 _timestampLeftShift = _sequenceBits + _workerIdBits + _datacenterIdBits;
37 _sequenceMask = -1L ^ (-1L << _sequenceBits);
38
39 if (datacenterId > _maxDatacenterId || datacenterId < 0)
40 {
41 throw new ArgumentException($"datasenterId kan ikke være større enn {_maxDatacenterId} eller mindre enn 0");
42 }
43 if (workerId > _maxWorkerId || workerId < 0)
44 {
45 throw new ArgumentException($"arbeiderId kan ikke være større enn {_maxWorkerId} eller mindre enn 0");
46 }
47 _datacenterId = datacenterId;
48 _workerId = workerId;
49 }
50
51 public long NextId()
52 {
53 lock (_lock)
54 {
55 var timestamp = TimeGen();
56
57 if (timestamp < _lastTimestamp)
58 {
59 throw new Exception("Klokken har flyttet seg bakover. Nekter å generere id");
60 }
61
62 if (_lastTimestamp == timestamp)
63 {
64 _sequence = (_sequence + 1) & _sequenceMask;
65 if (_sequence == 0)
66 {
67 timestamp = TilNextMillis(_lastTimestamp);
68 }
69 }
70 else
71 {
72 _sequence = 0L;
73 }
74
75 _lastTimestamp = timestamp;
76
77 return ((timestamp - _epoch) << _timestampLeftShift) |
78 (_datacenterId << _datacenterIdShift) |
79 (_workerId << _workerIdShift) |
80 _sequence;
81 }
82 }
83
84 private long TilNextMillis(long lastTimestamp)
85 {
86 var timestamp = TimeGen();
87 while (timestamp <= lastTimestamp)
88 {
89 timestamp = TimeGen();
90 }
91 return timestamp;
92 }
93
94 private long TimeGen()
95 {
96 return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
97 }
98}
99
100// Bruk
101class Program
102{
103 static void Main(string[] args)
104 {
105 var generator = new SnowflakeGenerator(1, 1);
106 var id = generator.NextId();
107 Console.WriteLine($"Generert Snowflake ID: {id}");
108 }
109}
110
1package main
2
3import (
4 "fmt"
5 "sync"
6 "time"
7)
8
9type SnowflakeGenerator struct {
10 epoch int64
11 datacenterIdBits uint
12 workerIdBits uint
13 sequenceBits uint
14 maxDatacenterId int64
15 maxWorkerId int64
16 workerIdShift uint
17 datacenterIdShift uint
18 timestampLeftShift uint
19 sequenceMask int64
20
21 datacenterId int64
22 workerId int64
23 sequence int64
24 lastTimestamp int64
25
26 lock sync.Mutex
27}
28
29func NewSnowflakeGenerator(datacenterId, workerId int64) (*SnowflakeGenerator, error) {
30 g := &SnowflakeGenerator{
31 epoch: 1288834974657,
32 datacenterIdBits: 5,
33 workerIdBits: 5,
34 sequenceBits: 12,
35 lastTimestamp: -1,
36 }
37
38 g.maxDatacenterId = -1 ^ (-1 << g.datacenterIdBits)
39 g.maxWorkerId = -1 ^ (-1 << g.workerIdBits)
40
41 g.workerIdShift = g.sequenceBits
42 g.datacenterIdShift = g.sequenceBits + g.workerIdBits
43 g.timestampLeftShift = g.sequenceBits + g.workerIdBits + g.datacenterIdBits
44 g.sequenceMask = -1 ^ (-1 << g.sequenceBits)
45
46 if datacenterId > g.maxDatacenterId || datacenterId < 0 {
47 return nil, fmt.Errorf("datasenterId kan ikke være større enn %d eller mindre enn 0", g.maxDatacenterId)
48 }
49 if workerId > g.maxWorkerId || workerId < 0 {
50 return nil, fmt.Errorf("arbeiderId kan ikke være større enn %d eller mindre enn 0", g.maxWorkerId)
51 }
52 g.datacenterId = datacenterId
53 g.workerId = workerId
54
55 return g, nil
56}
57
58func (g *SnowflakeGenerator) NextId() (int64, error) {
59 g.lock.Lock()
60 defer g.lock.Unlock()
61
62 timestamp := g.timeGen()
63
64 if timestamp < g.lastTimestamp {
65 return 0, fmt.Errorf("klokken har flyttet seg bakover, nekter å generere id")
66 }
67
68 if g.lastTimestamp == timestamp {
69 g.sequence = (g.sequence + 1) & g.sequenceMask
70 if g.sequence == 0 {
71 timestamp = g.tilNextMillis(g.lastTimestamp)
72 }
73 } else {
74 g.sequence = 0
75 }
76
77 g.lastTimestamp = timestamp
78
79 return ((timestamp - g.epoch) << g.timestampLeftShift) |
80 (g.datacenterId << g.datacenterIdShift) |
81 (g.workerId << g.workerIdShift) |
82 g.sequence, nil
83}
84
85func (g *SnowflakeGenerator) tilNextMillis(lastTimestamp int64) int64 {
86 timestamp := g.timeGen()
87 for timestamp <= lastTimestamp {
88 timestamp = g.timeGen()
89 }
90 return timestamp
91}
92
93func (g *SnowflakeGenerator) timeGen() int64 {
94 return time.Now().UnixNano() / int64(time.Millisecond)
95}
96
97func main() {
98 generator, err := NewSnowflakeGenerator(1, 1)
99 if err != nil {
100 fmt.Printf("Feil ved oppretting av generator: %v\n", err)
101 return
102 }
103
104 id, err := generator.NextId()
105 if err != nil {
106 fmt.Printf("Feil ved generering av ID: %v\n", err)
107 return
108 }
109
110 fmt.Printf("Generert Snowflake ID: %d\n", id)
111}
112
Diagram
Her er en visuell representasjon av Snowflake ID-strukturen:
Referanser
- "Kunngjøring av Snowflake." Twitter Engineering Blog, https://blog.twitter.com/engineering/en_us/a/2010/announcing-snowflake
- "Snowflake ID." Wikipedia, https://no.wikipedia.org/wiki/Snowflake_ID
- "Distribuert ID-generering i mikrotjenester." Medium, https://medium.com/swlh/distributed-id-generation-in-microservices-b6ce9a8dd93f
Tilbakemelding
Klikk på tilbakemeldings-toast for å begynne å gi tilbakemelding om dette verktøyet
Relaterte verktøy
Oppdag flere verktøy som kan være nyttige for arbeidsflyten din