Gerador de IDs Snowflake para Sistemas Distribuídos
Gere e analise IDs Snowflake do Twitter, identificadores únicos de 64 bits usados em sistemas distribuídos. Esta ferramenta permite que você crie novos IDs Snowflake e analise os existentes, fornecendo insights sobre seus componentes de timestamp, ID da máquina e número de sequência.
Gerador de ID Snowflake
Gerador de ID Snowflake
Documentação
Gerador de ID Snowflake
Introdução
Um ID Snowflake é um identificador único usado em sistemas distribuídos, originalmente desenvolvido pelo Twitter. Esta ferramenta permite que você gere e analise IDs Snowflake, que são inteiros de 64 bits compostos por um timestamp, ID da máquina e número de sequência.
Como os IDs Snowflake Funcionam
Os IDs Snowflake são inteiros de 64 bits estruturados da seguinte forma:
- 41 bits: Timestamp (milissegundos desde uma época personalizada)
- 10 bits: ID da máquina (5 bits para ID do data center, 5 bits para ID do trabalhador)
- 12 bits: Número de sequência
Essa estrutura permite a geração de aproximadamente 4.096 IDs únicos por milissegundo por máquina.
Usando o Gerador de ID Snowflake
- (Opcional) Defina uma época personalizada (o padrão é a época do Twitter: 2010-11-04T01:42:54.657Z)
- Insira um ID de máquina (0-31) e um ID de data center (0-31)
- Clique em "Gerar" para criar um novo ID Snowflake
- O ID gerado e seus componentes serão exibidos
Para analisar um ID Snowflake existente, insira-o no campo "Analisar ID" e clique em "Analisar".
Fórmula
O ID Snowflake é construído usando operações bitwise:
1ID = (timestamp << 22) | (datacenterId << 17) | (workerId << 12) | sequence
2
Onde:
timestamp
é o número de milissegundos desde a épocadatacenterId
é um inteiro de 5 bits (0-31)workerId
é um inteiro de 5 bits (0-31)sequence
é um inteiro de 12 bits (0-4095)
Cálculo
O gerador de ID Snowflake realiza os seguintes passos:
- Obter o timestamp atual em milissegundos
- Garantir que o timestamp seja maior que o último timestamp usado (para unicidade)
- Se o timestamp for o mesmo que o último, incrementar o número de sequência
- Se o número de sequência transbordar (atingir 4096), aguardar o próximo milissegundo
- Combinar os componentes usando operações bitwise para criar o ID final
Casos de Uso
Os IDs Snowflake são particularmente úteis em:
- Sistemas Distribuídos: Gerar IDs únicos em várias máquinas sem coordenação
- Dados de Alto Volume: Criar IDs ordenáveis para grandes conjuntos de dados
- Microserviços: Garantir identificadores únicos em diferentes serviços
- Particionamento de Banco de Dados: Usar o componente de timestamp ou ID da máquina para particionamento eficiente
Alternativas
Embora os IDs Snowflake sejam poderosos, outros sistemas de geração de ID incluem:
- UUID (Identificador Único Universal): Útil quando a geração distribuída é necessária sem ordenação
- IDs de banco de dados autoincrementais: Simples, mas limitados a instâncias de banco de dados únicas
- ULID (Identificador Universamente Único Lexicograficamente Ordenável): Semelhante ao Snowflake, mas com uma estrutura diferente
Casos Limites e Limitações
-
Sincronização de Relógio: Os IDs Snowflake dependem do tempo do sistema. Se o relógio retroceder devido a ajustes de NTP ou mudanças de horário de verão, isso pode causar problemas na geração de ID.
-
Problema do Ano 2038: O timestamp de 41 bits transbordará em 2079 (assumindo a época do Twitter). Sistemas que usam IDs Snowflake devem planejar para essa eventualidade.
-
Colisões de ID de Máquina: Em grandes sistemas distribuídos, garantir IDs de máquina únicos pode ser desafiador e pode exigir coordenação adicional.
-
Transbordo de Sequência: Em cenários de alta vazão, é possível esgotar as 4096 sequências por milissegundo, potencialmente causando atrasos.
-
Não Monotonicidade Entre Máquinas: Embora os IDs sejam monotonamente crescentes em uma única máquina, eles podem não ser estritamente monotônicos entre várias máquinas.
História
Os IDs Snowflake foram introduzidos pelo Twitter em 2010 para atender à necessidade de identificadores únicos distribuídos e ordenáveis no tempo. Desde então, foram adotados e adaptados por muitas outras empresas e projetos.
Exemplos
Aqui estão implementações de geradores de ID Snowflake em várias linguagens:
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('O relógio retrocedeu. Recusando-se a gerar 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// Uso
55const generator = new SnowflakeGenerator();
56const id = generator.nextId(1, 1);
57console.log(`ID Snowflake gerado: ${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("O relógio retrocedeu. Recusando-se a gerar 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## Uso
58generator = SnowflakeGenerator(datacenter_id=1, worker_id=1)
59snowflake_id = generator.next_id()
60print(f"ID Snowflake gerado: {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("datacenterId não pode ser maior que maxDatacenterId ou menor que 0");
39 }
40 if (workerId > maxWorkerId || workerId < 0) {
41 throw new IllegalArgumentException("workerId não pode ser maior que maxWorkerId ou menor que 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("O relógio retrocedeu. Recusando-se a gerar 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("ID Snowflake gerado: " + 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 'O relógio retrocedeu' 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## Uso
54generator = SnowflakeGenerator.new(1, 1)
55snowflake_id = generator.next_id
56puts "ID Snowflake gerado: #{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
19 public function __construct($datacenterId, $workerId) {
20 $this->epoch = 1288834974657;
21 $this->datacenterIdBits = 5;
22 $this->workerIdBits = 5;
23 $this->sequenceBits = 12;
24
25 $this->maxDatacenterId = -1 ^ (-1 << $this->datacenterIdBits);
26 $this->maxWorkerId = -1 ^ (-1 << $this->workerIdBits);
27
28 $this->workerIdShift = $this->sequenceBits;
29 $this->datacenterIdShift = $this->sequenceBits + $this->workerIdBits;
30 $this->timestampLeftShift = $this->sequenceBits + $this->workerIdBits + $this->datacenterIdBits;
31 $this->sequenceMask = -1 ^ (-1 << $this->sequenceBits);
32
33 if ($datacenterId > $this->maxDatacenterId || $datacenterId < 0) {
34 throw new Exception("datacenterId não pode ser maior que maxDatacenterId ou menor que 0");
35 }
36 if ($workerId > $this->maxWorkerId || $workerId < 0) {
37 throw new Exception("workerId não pode ser maior que maxWorkerId ou menor que 0");
38 }
39 $this->datacenterId = $datacenterId;
40 $this->workerId = $workerId;
41 }
42
43 public function nextId() {
44 $timestamp = $this->timeGen();
45
46 if ($timestamp < $this->lastTimestamp) {
47 throw new Exception("O relógio retrocedeu. Recusando-se a gerar id");
48 }
49
50 if ($this->lastTimestamp == $timestamp) {
51 $this->sequence = ($this->sequence + 1) & $this->sequenceMask;
52 if ($this->sequence == 0) {
53 $timestamp = $this->tilNextMillis($this->lastTimestamp);
54 }
55 } else {
56 $this->sequence = 0;
57 }
58
59 $this->lastTimestamp = $timestamp;
60
61 return (($timestamp - $this->epoch) << $this->timestampLeftShift) |
62 ($this->datacenterId << $this->datacenterIdShift) |
63 ($this->workerId << $this->workerIdShift) |
64 $this->sequence;
65 }
66
67 private function tilNextMillis($lastTimestamp) {
68 $timestamp = $this->timeGen();
69 while ($timestamp <= $lastTimestamp) {
70 $timestamp = $this->timeGen();
71 }
72 return $timestamp;
73 }
74
75 private function timeGen() {
76 return floor(microtime(true) * 1000);
77 }
78}
79
80// Uso
81$generator = new SnowflakeGenerator(1, 1);
82$id = $generator->nextId();
83echo "ID Snowflake gerado: " . $id . "\n";
84
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($"datacenterId não pode ser maior que {_maxDatacenterId} ou menor que 0");
42 }
43 if (workerId > _maxWorkerId || workerId < 0)
44 {
45 throw new ArgumentException($"workerId não pode ser maior que {_maxWorkerId} ou menor que 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("O relógio retrocedeu. Recusando-se a gerar 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// Uso
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($"ID Snowflake gerado: {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("datacenterId não pode ser maior que %d ou menor que 0", g.maxDatacenterId)
48 }
49 if workerId > g.maxWorkerId || workerId < 0 {
50 return nil, fmt.Errorf("workerId não pode ser maior que %d ou menor que 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("o relógio retrocedeu, recusando-se a gerar 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("Erro ao criar gerador: %v\n", err)
101 return
102 }
103
104 id, err := generator.NextId()
105 if err != nil {
106 fmt.Printf("Erro ao gerar ID: %v\n", err)
107 return
108 }
109
110 fmt.Printf("ID Snowflake gerado: %d\n", id)
111}
112
Diagrama
Aqui está uma representação visual da estrutura do ID Snowflake:
Referências
- "Anunciando Snowflake." Blog de Engenharia do Twitter, https://blog.twitter.com/engineering/en_us/a/2010/announcing-snowflake
- "ID Snowflake." Wikipedia, https://en.wikipedia.org/wiki/Snowflake_ID
- "Geração de ID Distribuída em Microserviços." Medium, https://medium.com/swlh/distributed-id-generation-in-microservices-b6ce9a8dd93f
Feedback
Clique no feedback toast para começar a dar feedback sobre esta ferramenta
Ferramentas Relacionadas
Descubra mais ferramentas que podem ser úteis para o seu fluxo de trabalho