Snowflake ID Generator for Twitter and Distributed Systems
Hasilkan dan analisis ID Snowflake Twitter, pengenalan unik 64-bit yang digunakan dalam sistem teragih. Alat ini membolehkan anda mencipta ID Snowflake baru dan menguraikan yang sedia ada, memberikan pandangan tentang komponen cap waktu, ID mesin, dan nombor urutan mereka.
Penjana ID Snowflake
Penjana ID Snowflake
Dokumentasi
1## Penjana ID Snowflake
2
3### Pengenalan
4
5ID Snowflake adalah pengenal unik yang digunakan dalam sistem terdistribusi, asalnya dibangunkan oleh Twitter. Alat ini membolehkan anda menjana dan menganalisis ID Snowflake, yang merupakan integer 64-bit yang terdiri daripada timestamp, ID mesin, dan nombor urutan.
6
7### Cara Kerja ID Snowflake
8
9ID Snowflake adalah integer 64-bit yang disusun seperti berikut:
10
11- 41 bit: Timestamp (milisaat sejak epoch khusus)
12- 10 bit: ID Mesin (5 bit untuk ID pusat data, 5 bit untuk ID pekerja)
13- 12 bit: Nombor urutan
14
15Struktur ini membolehkan penghasilan kira-kira 4,096 ID unik setiap milisaat bagi setiap mesin.
16
17### Menggunakan Penjana ID Snowflake
18
191. (Pilihan) Tetapkan epoch khusus (default adalah epoch Twitter: 2010-11-04T01:42:54.657Z)
202. Masukkan ID mesin (0-31) dan ID pusat data (0-31)
213. Klik "Jana" untuk mencipta ID Snowflake baru
224. ID yang dihasilkan dan komponennya akan dipaparkan
23
24Untuk mengurai ID Snowflake yang sedia ada, masukkan dalam medan "Urai ID" dan klik "Urai".
25
26### Formula
27
28ID Snowflake dibina menggunakan operasi bitwise:
29
30
plaintext ID = (timestamp << 22) | (datacenterId << 17) | (workerId << 12) | sequence
1
2Di mana:
3- `timestamp` adalah bilangan milisaat sejak epoch
4- `datacenterId` adalah integer 5-bit (0-31)
5- `workerId` adalah integer 5-bit (0-31)
6- `sequence` adalah integer 12-bit (0-4095)
7
8### Pengiraan
9
10Penjana ID Snowflake melaksanakan langkah-langkah berikut:
11
121. Dapatkan timestamp semasa dalam milisaat
132. Pastikan timestamp lebih besar daripada timestamp terakhir yang digunakan (untuk keunikan)
143. Jika timestamp sama dengan yang terakhir, tingkatkan nombor urutan
154. Jika nombor urutan melimpah (mencapai 4096), tunggu milisaat seterusnya
165. Gabungkan komponen menggunakan operasi bitwise untuk mencipta ID akhir
17
18### Kes Penggunaan
19
20ID Snowflake sangat berguna dalam:
21
221. Sistem Terdistribusi: Jana ID unik merentasi pelbagai mesin tanpa penyelarasan
232. Data Bervolume Tinggi: Cipta ID yang boleh disusun untuk set data besar
243. Mikrosrvices: Pastikan pengenal unik merentasi pelbagai perkhidmatan
254. Pembahagian Pangkalan Data: Gunakan komponen timestamp atau ID mesin untuk pembahagian yang cekap
26
27#### Alternatif
28
29Walaupun ID Snowflake sangat berkuasa, sistem penghasilan ID lain termasuk:
30
311. UUID (Pengenal Unik Sejagat): Berguna apabila penghasilan terdistribusi diperlukan tanpa kebolehsusunan
322. ID pangkalan data auto-inkremen: Mudah tetapi terhad kepada satu instans pangkalan data
333. ULID (Pengenal Unik Sejagat yang Boleh Disusun Secara Lexicographically): Serupa dengan Snowflake, tetapi dengan struktur yang berbeza
34
35### Kes-Kes Tepi dan Had
36
371. Penyelarasan Jam: ID Snowflake bergantung kepada masa sistem. Jika jam bergerak ke belakang akibat penyesuaian NTP atau perubahan waktu musim panas, ia boleh menyebabkan masalah dengan penghasilan ID.
38
392. Masalah Tahun 2038: Timestamp 41-bit akan melimpah pada tahun 2079 (dengan anggapan epoch Twitter). Sistem yang menggunakan ID Snowflake harus merancang untuk kemungkinan ini.
40
413. Perlanggaran ID Mesin: Dalam sistem terdistribusi yang besar, memastikan ID mesin yang unik boleh menjadi cabaran dan mungkin memerlukan penyelarasan tambahan.
42
434. Melimpah Nombor Urutan: Dalam senario throughput yang sangat tinggi, adalah mungkin untuk kehabisan 4096 urutan setiap milisaat, yang mungkin menyebabkan kelewatan.
44
455. Non-monotonik Merentasi Mesin: Walaupun ID adalah meningkat monotonik pada satu mesin, ia mungkin tidak secara ketat monotonik merentasi pelbagai mesin.
46
47### Sejarah
48
49ID Snowflake diperkenalkan oleh Twitter pada tahun 2010 untuk menangani keperluan pengenal unik yang boleh disusun mengikut masa dalam sistem terdistribusi. Mereka telah diambil dan disesuaikan oleh banyak syarikat dan projek lain.
50
51### Contoh
52
53Berikut adalah pelaksanaan penjana ID Snowflake dalam pelbagai bahasa:
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('Jam bergerak ke belakang. Menolak untuk menjana 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()); } }
// Penggunaan
const generator = new SnowflakeGenerator();
const id = generator.nextId(1, 1);
console.log(ID Snowflake yang dihasilkan: ${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("Jam bergerak ke belakang. Menolak untuk menjana 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
Penggunaan
generator = SnowflakeGenerator(datacenter_id=1, worker_id=1) snowflake_id = generator.next_id() print(f"ID Snowflake yang dihasilkan: {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 Lock lock = new ReentrantLock();
private long datacenterId;
private long workerId;
private long sequence = 0L;
private long lastTimestamp = -1L;
public SnowflakeGenerator(long datacenterId, long workerId) {
this.epoch = 1288834974657L;
this.datacenterIdBits = 5L;
this.workerIdBits = 5L;
this.sequenceBits = 12L;
this.maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
this.maxWorkerId = -1L ^ (-1L << workerIdBits);
this.workerIdShift = sequenceBits;
this.datacenterIdShift = sequenceBits + workerIdBits;
this.timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
this.sequenceMask = -1L ^ (-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("Jam bergerak ke belakang. Menolak untuk menjana 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("ID Snowflake yang dihasilkan: " + 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 'Jam bergerak ke belakang' 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
Penggunaan
generator = SnowflakeGenerator.new(1, 1) snowflake_id = generator.next_id puts "ID Snowflake yang dihasilkan: #{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("Jam bergerak ke belakang. Menolak untuk menjana 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); } } // Penggunaan $generator = new SnowflakeGenerator(1, 1); $id = $generator->nextId(); echo "ID Snowflake yang dihasilkan: " . $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("Jam bergerak ke belakang. Menolak untuk menjana 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();
}
}
// Penggunaan class Program { static void Main(string[] args) { var generator = new SnowflakeGenerator(1, 1); var id = generator.NextId(); Console.WriteLine($"ID Snowflake yang dihasilkan: {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("jam bergerak ke belakang, menolak untuk menjana 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("Ralat mencipta penjana: %v\n", err) return }
id, err := generator.NextId()
if err != nil {
fmt.Printf("Ralat menjana ID: %v\n", err)
return
}
fmt.Printf("ID Snowflake yang dihasilkan: %d\n", id)
}
1
2### Rajah
3
4Berikut adalah representasi visual struktur ID 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">Timestamp (41 bit)</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">ID Mesin (10 bit)</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">Nombor Urutan (12 bit)</text>
15
16 <text x="300" y="80" font-family="Arial" fontSize="16" fill="black" textAnchor="middle">Struktur ID Snowflake 64-bit</text>
17</svg>
18
19### Rujukan
20
211. "Mengumumkan Snowflake." Blog Kejuruteraan Twitter, https://blog.twitter.com/engineering/en_us/a/2010/announcing-snowflake
222. "ID Snowflake." Wikipedia, https://en.wikipedia.org/wiki/Snowflake_ID
233. "Penghasilan ID Terdistribusi dalam Mikrosrvices." Medium, https://medium.com/swlh/distributed-id-generation-in-microservices-b6ce9a8dd93f
24
Maklum Balas
Klik toast maklum balas untuk mula memberi maklum balas tentang alat ini
Alat Berkaitan
Temui lebih banyak alat yang mungkin berguna untuk aliran kerja anda