Генератор на уникални Snowflake ID за разпределени системи
Генерирайте и анализирайте Twitter Snowflake ID, уникални 64-битови идентификатори, използвани в разпределени системи. Този инструмент ви позволява да създавате нови Snowflake ID и да анализирате съществуващи, предоставяйки информация за техните компоненти: времеви печат, ID на машина и номер на последователност.
Генератор на Snowflake ID
Генератор на Snowflake ID
Документация
Генератор на Snowflake ID
Въведение
Snowflake ID е уникален идентификатор, използван в разпределени системи, първоначално разработен от Twitter. Този инструмент ви позволява да генерирате и анализирате Snowflake ID, които са 64-битови цели числа, съставени от времеви печат, идентификатор на машина и номер на последователност.
Как работят Snowflake ID
Snowflake ID са 64-битови цели числа, структурирани по следния начин:
- 41 бит: Времеви печат (милисекунди от собствена епоха)
- 10 бита: Идентификатор на машина (5 бита за идентификатор на дата център, 5 бита за идентификатор на работник)
- 12 бита: Номер на последователност
Тази структура позволява генерирането на приблизително 4,096 уникални ID на милисекунда на машина.
Използване на генератора на Snowflake ID
- (По избор) Задайте собствена епоха (по подразбиране е епохата на Twitter: 2010-11-04T01:42:54.657Z)
- Въведете идентификатор на машина (0-31) и идентификатор на дата център (0-31)
- Щракнете върху "Генерирай", за да създадете нов Snowflake ID
- Генерираният ID и неговите компоненти ще бъдат показани
За да анализирате съществуващ Snowflake ID, въведете го в полето "Анализиране на ID" и щракнете върху "Анализиране".
Формула
Snowflake ID се конструира с помощта на битови операции:
1ID = (timestamp << 22) | (datacenterId << 17) | (workerId << 12) | sequence
2
Където:
timestamp
е броят на милисекундите от епохатаdatacenterId
е 5-битов цяло число (0-31)workerId
е 5-битов цяло число (0-31)sequence
е 12-битов цяло число (0-4095)
Изчисление
Генераторът на Snowflake ID извършва следните стъпки:
- Вземете текущия времеви печат в милисекунди
- Уверете се, че времевият печат е по-голям от последния използван времеви печат (за уникалност)
- Ако времевият печат е същият като последния, увеличете номера на последователността
- Ако номерът на последователността прелее (достигне 4096), изчакайте следващата милисекунда
- Комбинирайте компонентите с помощта на битови операции, за да създадете окончателния ID
Случаи на употреба
Snowflake ID са особено полезни в:
- Разпределени системи: Генерирайте уникални ID на множество машини без координация
- Високобройни данни: Създайте сортиращи ID за големи набори от данни
- Микросервизи: Осигурете уникални идентификатори между различни услуги
- Разделяне на бази данни: Използвайте времевия печат или компонента на идентификатора на машината за ефективно разделяне
Алтернативи
Докато Snowflake ID са мощни, други системи за генериране на ID включват:
- UUID (Универсален уникален идентификатор): Полезен, когато е необходимо разпределено генериране без сортиране
- Автоинкрементни идентификатори на бази данни: Прости, но ограничени до единични инстанции на бази данни
- ULID (Универсален уникален лексикографски сортиран идентификатор): Подобен на Snowflake, но с различна структура
Гранични случаи и ограничения
-
Синхронизация на часовника: Snowflake ID разчитат на системното време. Ако часовникът се върне назад поради корекции на NTP или промени в лятното часово време, това може да предизвика проблеми с генерирането на ID.
-
Проблемът с годината 2038: 41-битовият времеви печат ще прелее през 2079 (при условие, че епохата на Twitter). Системите, използващи Snowflake ID, трябва да планират за това.
-
Колизии на идентификаторите на машините: В големи разпределени системи, осигуряването на уникални идентификатори на машините може да бъде предизвикателство и може да изисква допълнителна координация.
-
Преливане на последователността: В изключително високообемни сценарии е възможно да се изчерпат 4096 последователности на милисекунда, което потенциално да предизвика забавяния.
-
Ненадеждност на последователността между машините: Докато ID са монотонно нарастващи на една машина, те може да не са строго монотонни между множество машини.
История
Snowflake ID бяха въведени от Twitter през 2010 г. с цел да се задоволи нуждата от разпределени, времево-сортирани уникални идентификатори. Те оттогава са приети и адаптирани от много други компании и проекти.
Примери
Ето реализации на генератори на Snowflake ID на различни езици:
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('Часовникът се е преместил назад. Отказвам да генерирам 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// Използване
55const generator = new SnowflakeGenerator();
56const id = generator.nextId(1, 1);
57console.log(`Генериран 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("Часовникът се е преместил назад. Отказвам да генерирам 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## Използване
58generator = SnowflakeGenerator(datacenter_id=1, worker_id=1)
59snowflake_id = generator.next_id()
60print(f"Генериран 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("datacenterId can't be greater than maxDatacenterId or less than 0");
39 }
40 if (workerId > maxWorkerId || workerId < 0) {
41 throw new IllegalArgumentException("workerId can't be greater than maxWorkerId or less than 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("Часовникът се е преместил назад. Отказвам да генерирам 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("Генериран 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 'Часовникът се е преместил назад' 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## Използване
54generator = SnowflakeGenerator.new(1, 1)
55snowflake_id = generator.next_id
56puts "Генериран 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("datacenterId не може да бъде по-голям от maxDatacenterId или по-малък от 0");
36 }
37 if ($workerId > $this->maxWorkerId || $workerId < 0) {
38 throw new Exception("workerId не може да бъде по-голям от maxWorkerId или по-малък от 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("Часовникът се е преместил назад. Отказвам да генерирам 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// Използване
82$generator = new SnowflakeGenerator(1, 1);
83$id = $generator->nextId();
84echo "Генериран 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 long _workerId;
13 private long _datacenterId;
14 private long _sequence = 0L;
15 private long _lastTimestamp = -1L;
16
17 private readonly object _lock = new object();
18
19 public SnowflakeGenerator(long datacenterId, long workerId)
20 {
21 _epoch = 1288834974657L;
22 _datacenterIdBits = 5;
23 _workerIdBits = 5;
24 _sequenceBits = 12;
25
26 _maxDatacenterId = -1L ^ (-1L << _datacenterIdBits);
27 _maxWorkerId = -1L ^ (-1L << _workerIdBits);
28
29 _workerIdShift = _sequenceBits;
30 _datacenterIdShift = _sequenceBits + _workerIdBits;
31 _timestampLeftShift = _sequenceBits + _workerIdBits + _datacenterIdBits;
32 _sequenceMask = -1L ^ (-1L << _sequenceBits);
33
34 if (datacenterId > _maxDatacenterId || datacenterId < 0)
35 {
36 throw new ArgumentException($"datacenterId не може да бъде по-голям от {_maxDatacenterId} или по-малък от 0");
37 }
38 if (workerId > _maxWorkerId || workerId < 0)
39 {
40 throw new ArgumentException($"workerId не може да бъде по-голям от {_maxWorkerId} или по-малък от 0");
41 }
42 _datacenterId = datacenterId;
43 _workerId = workerId;
44 }
45
46 public long NextId()
47 {
48 lock (_lock)
49 {
50 var timestamp = TimeGen();
51
52 if (timestamp < _lastTimestamp)
53 {
54 throw new Exception("Часовникът се е преместил назад. Отказвам да генерирам ID");
55 }
56
57 if (_lastTimestamp == timestamp)
58 {
59 _sequence = (_sequence + 1) & _sequenceMask;
60 if (_sequence == 0)
61 {
62 timestamp = TilNextMillis(_lastTimestamp);
63 }
64 }
65 else
66 {
67 _sequence = 0L;
68 }
69
70 _lastTimestamp = timestamp;
71
72 return ((timestamp - _epoch) << _timestampLeftShift) |
73 (_datacenterId << _datacenterIdShift) |
74 (_workerId << _workerIdShift) |
75 _sequence;
76 }
77 }
78
79 private long TilNextMillis(long lastTimestamp)
80 {
81 var timestamp = TimeGen();
82 while (timestamp <= lastTimestamp)
83 {
84 timestamp = TimeGen();
85 }
86 return timestamp;
87 }
88
89 private long TimeGen()
90 {
91 return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
92 }
93}
94
95// Използване
96class Program
97{
98 static void Main(string[] args)
99 {
100 var generator = new SnowflakeGenerator(1, 1);
101 var id = generator.NextId();
102 Console.WriteLine($"Генериран Snowflake ID: {id}");
103 }
104}
105
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 не може да бъде по-голям от %d или по-малък от 0", g.maxDatacenterId)
48 }
49 if workerId > g.maxWorkerId || workerId < 0 {
50 return nil, fmt.Errorf("workerId не може да бъде по-голям от %d или по-малък от 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("часовникът се е преместил назад, отказвам да генерирам 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("Грешка при създаване на генератора: %v\n", err)
101 return
102 }
103
104 id, err := generator.NextId()
105 if err != nil {
106 fmt.Printf("Грешка при генериране на ID: %v\n", err)
107 return
108 }
109
110 fmt.Printf("Генериран Snowflake ID: %d\n", id)
111}
112
Диаграма
Ето визуално представяне на структурата на Snowflake ID:
Референции
- "Обявяване на Snowflake." Блог на Twitter Engineering, https://blog.twitter.com/engineering/en_us/a/2010/announcing-snowflake
- "Snowflake ID." Уикипедия, https://en.wikipedia.org/wiki/Snowflake_ID
- "Разпределено генериране на ID в микросервизи." Medium, https://medium.com/swlh/distributed-id-generation-in-microservices-b6ce9a8dd93f
Обратна връзка
Кликнете върху обратната връзка, за да започнете да давате обратна връзка за този инструмент
Свързани инструменти
Открийте още инструменти, които може да бъдат полезни за вашия работен процес