Twitter用スノーフレークID生成および分析ツール
TwitterのスノーフレークIDを生成および分析します。これは、分散システムで使用されるユニークな64ビット識別子です。このツールを使用すると、新しいスノーフレークIDを作成し、既存のものを解析して、タイムスタンプ、マシンID、およびシーケンス番号のコンポーネントに関する洞察を提供します。
スノーフレークIDジェネレーター
スノーフレークIDジェネレーター
ドキュメント
スノーフレークIDジェネレーター
はじめに
スノーフレークIDは、分散システムで使用される一意の識別子で、もともとはTwitterによって開発されました。このツールを使用すると、タイムスタンプ、マシンID、およびシーケンス番号で構成される64ビット整数であるスノーフレークIDを生成および分析できます。
スノーフレークIDの仕組み
スノーフレークIDは、次のように構成された64ビット整数です:
- 41ビット:タイムスタンプ(カスタムエポックからのミリ秒)
- 10ビット:マシンID(データセンターID用の5ビット、ワーカーID用の5ビット)
- 12ビット:シーケンス番号
この構造により、マシンごとにミリ秒あたり約4,096のユニークIDを生成できます。
スノーフレークIDジェネレーターの使用方法
- (オプション)カスタムエポックを設定します(デフォルトはTwitterのエポック:2010-11-04T01:42:54.657Z)
- マシンID(0-31)とデータセンターID(0-31)を入力します
- 「生成」をクリックして新しいスノーフレークIDを作成します
- 生成されたIDとそのコンポーネントが表示されます
既存のスノーフレークIDを解析するには、「IDを解析」フィールドに入力し、「解析」をクリックします。
数式
スノーフレークIDはビット演算を使用して構築されます:
1ID = (timestamp << 22) | (datacenterId << 17) | (workerId << 12) | sequence
2
ここで:
timestamp
はエポックからのミリ秒数ですdatacenterId
は5ビット整数(0-31)ですworkerId
は5ビット整数(0-31)ですsequence
は12ビット整数(0-4095)です
計算
スノーフレークIDジェネレーターは、次の手順を実行します:
- 現在のタイムスタンプをミリ秒単位で取得します
- タイムスタンプが最後に使用されたタイムスタンプより大きいことを確認します(ユニーク性のため)
- タイムスタンプが最後のものと同じ場合、シーケンス番号をインクリメントします
- シーケンス番号がオーバーフローした場合(4096に達した場合)、次のミリ秒を待ちます
- ビット演算を使用してコンポーネントを組み合わせ、最終的なIDを作成します
使用例
スノーフレークIDは特に以下のような場合に便利です:
- 分散システム:複数のマシン間で調整なしにユニークIDを生成します
- 高ボリュームデータ:大規模データセットのためにソート可能なIDを作成します
- マイクロサービス:異なるサービス間でユニーク識別子を確保します
- データベースシャーディング:効率的なシャーディングのためにタイムスタンプまたはマシンIDコンポーネントを使用します
代替案
スノーフレークIDは強力ですが、他のID生成システムには以下が含まれます:
- UUID(ユニバーサルユニーク識別子):ソート可能性なしに分散生成が必要な場合に便利です
- 自動インクリメントデータベースID:シンプルですが、単一のデータベースインスタンスに制限されます
- ULID(ユニバーサルユニークレキシコグラフィカルソート可能識別子):スノーフレークに似ていますが、異なる構造を持っています
エッジケースと制限
-
クロック同期:スノーフレークIDはシステム時間に依存します。NTP調整やサマータイムの変更によりクロックが逆戻りすると、ID生成に問題が生じる可能性があります。
-
2038年問題:41ビットのタイムスタンプは2079年にオーバーフローします(Twitterエポックを前提とした場合)。スノーフレークIDを使用するシステムは、この将来の事態に備える必要があります。
-
マシンIDの衝突:大規模な分散システムでは、ユニークなマシンIDを確保することが難しく、追加の調整が必要になる場合があります。
-
シーケンスのオーバーフロー:非常に高スループットのシナリオでは、ミリ秒あたり4096のシーケンスを使い果たす可能性があり、遅延を引き起こす可能性があります。
-
マシン間の非単調性:IDは単一のマシン上では単調増加しますが、複数のマシン間では厳密に単調ではない場合があります。
歴史
スノーフレークIDは、分散かつ時間順序可能なユニーク識別子の必要性に対処するために、2010年にTwitterによって導入されました。それ以来、多くの他の企業やプロジェクトによって採用され、適応されています。
例
以下は、さまざまな言語でのスノーフレーク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(`生成されたスノーフレーク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"生成されたスノーフレーク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はmaxDatacenterIdより大きく、または0未満であってはなりません");
39 }
40 if (workerId > maxWorkerId || workerId < 0) {
41 throw new IllegalArgumentException("workerIdはmaxWorkerIdより大きく、または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("生成されたスノーフレーク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
23 @lock = Mutex.new
24 end
25
26 def next_id
27 @lock.synchronize do
28 timestamp = (Time.now.to_f * 1000).to_i
29
30 raise 'クロックが逆戻りしました' if timestamp < @last_timestamp
31
32 if timestamp == @last_timestamp
33 @sequence = (@sequence + 1) & @sequence_mask
34 timestamp = til_next_millis(@last_timestamp) if @sequence == 0
35 else
36 @sequence = 0
37 end
38
39 @last_timestamp = timestamp
40
41 ((timestamp - @epoch) << @timestamp_left_shift) |
42 (@datacenter_id << @datacenter_id_shift) |
43 (@worker_id << @worker_id_shift) |
44 @sequence
45 end
46 end
47
48 private
49
50 def til_next_millis(last_timestamp)
51 timestamp = (Time.now.to_f * 1000).to_i
52 timestamp = (Time.now.to_f * 1000).to_i while timestamp <= last_timestamp
53 timestamp
54 end
55end
56
57## 使用例
58generator = SnowflakeGenerator.new(1, 1)
59snowflake_id = generator.next_id
60puts "生成されたスノーフレークID: #{snowflake_id}"
61
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 "生成されたスノーフレーク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($"datacenterIdは{_maxDatacenterId}より大きく、または0未満であってはなりません");
42 }
43 if (workerId > _maxWorkerId || workerId < 0)
44 {
45 throw new ArgumentException($"workerIdは{_maxWorkerId}より大きく、または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("クロックが逆戻りしました。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// 使用例
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: {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は%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("生成されたスノーフレークID: %d\n", id)
111}
112
図
スノーフレークIDの構造を視覚的に表現したものです:
参考文献
- "スノーフレークの発表。" Twitterエンジニアリングブログ、https://blog.twitter.com/engineering/en_us/a/2010/announcing-snowflake
- "スノーフレークID。" ウィキペディア、https://en.wikipedia.org/wiki/Snowflake_ID
- "マイクロサービスにおける分散ID生成。" Medium、https://medium.com/swlh/distributed-id-generation-in-microservices-b6ce9a8dd93f
フィードバック
フィードバックトーストをクリックして、このツールについてのフィードバックを始めてください
関連ツール
ワークフローに役立つかもしれない他のツールを発見してください