UUID v1ジェネレーター
Generate time-based UUID v1 with embedded timestamp
…
フォーマット
UUID v1 とは何ですか?
UUID v1 はオリジナルの UUID バージョンで、RFC 4122(2005 年)で標準化されました。高精度タイムスタンプと生成ホストの MAC アドレス、およびサブタイムスタンプ解像度を処理するための短いクロックシーケンスを組み合わせて一意の識別子を生成します。
タイムスタンプが埋め込まれているため、同じホストからの UUID v1 値は時間とともに単調増加し——自然に順序付けられます。これは各ノードが調整なしに独立して UUID を生成できる分散システム向けに設計されました。
今日、UUID v1 は主に UUID v7(ソート可能、MAC 漏洩なし)と UUID v4(完全ランダム、プライベート)に置き換えられています。Apache Cassandra やレガシー分散データベースなどのシステムでは引き続き使用されています。
UUID v1 の構造
550e8400-e29b-11d4-a716-446655440000 のような UUID v1 文字列は 6 つの異なるフィールドをエンコードします:
| フィールド | サイズ | 説明 |
|---|---|---|
| time_low | 32 bits | 60 ビットグレゴリオ暦タイムスタンプの 32 ビット下位フィールド(1582 年 10 月 15 日からの 100 ナノ秒間隔) |
| time_mid | 16 bits | 60 ビットタイムスタンプの中間 16 ビットフィールド |
| time_hi_and_version | 16 bits | 60 ビットタイムスタンプの上位 12 ビット + 4 ビットバージョン番号(常に <code>1</code>) |
| clock_seq_hi_res | 8 bits | 6 ビットクロックシーケンス上位フィールドと 2 ビット RFC 4122 バリアントマーカーの組み合わせ |
| clock_seq_low | 8 bits | クロックシーケンスの下位 8 ビット |
| node | 48 bits | 48 ビットノード識別子——通常は生成ネットワークインターフェースの MAC アドレス、または MAC が利用できない場合はランダムな 48 ビット値 |
クロックシーケンスフィールド(clock_seq_hi_res + clock_seq_low)は 14 ビットカウンターです。システムクロックが後退するとき(例:NTP 調整)またはシステムが最後の既知タイムスタンプを永続化せずに再起動するときにインクリメントされます。これにより、クロックが単調に進んでいない場合に重複した UUID が生成されるのを防ぎます。
UUID v1 タイムスタンプのデコード
60 ビットタイムスタンプは UUID の 3 つのフィールドにまたがっています。生成時間を再構築するには:
time_low(バイト 0–3)、time_mid(バイト 4–5)、time_hi(バイト 6–7、バージョンニブルを除く)を抽出する- 再組み立て:
(time_hi << 48) | (time_mid << 32) | time_low - 結果は 1582 年 10 月 15 日(グレゴリオ暦エポック)からの
100 ナノ秒間隔の 60 ビットカウント - グレゴリオ暦から Unix へのオフセットを引く:122,192,928,000,000,000(1582 年 10 月 15 日から 1970 年 1 月 1 日の間の 100 ナノ秒間隔)
10,000で割って 100 ナノ秒間隔をミリ秒に変換する- 結果を
Unix ミリ秒タイムスタンプとして使用して Date オブジェクトを構築する - 人間が読める出力のために
ISO 8601にフォーマットする
タイムスタンプ精度は 100 ナノ秒——UUID v7 のミリ秒精度よりはるかに細かいです。ただし、実際にはほとんどのオペレーティングシステムはサブミリ秒のクロック解像度を提供しないため、下位ビットはしばしばゼロまたは合成されます。
プライバシーの懸念
UUID v1 の最も重要な欠点は、ノードフィールドに生成ホストの MAC アドレスを埋め込むことです。これは、すべての UUID v1 がそれを生成したマシンの永続的でグローバルに一意のフィンガープリントを持つことを意味します。
UUID v1 を取得した攻撃者は次のことを判断できます:(1) ID が生成されたおおよその時刻、(2) 生成ホストの MAC アドレス、(3) 複数の UUID を分析することで、ID の生成レート。
このため、UUID v1 はこの情報の開示を厭わない場合を除き、公開向け識別子(例:URL や API レスポンス)として使用すべきではありません。RFC 4122 自体は、システムが MAC アドレスの代わりにランダムな 48 ビット値を使用してもよいと述べていますが、多くの実装はそうしていません。
UUID v1 がまだ適切な場合
UUID v1 vs UUID v7
UUID v7 は時間順識別子の UUID v1 の現代的な後継者です。直接比較を示します:
| 側面 | UUID v1 | UUID v7 |
|---|---|---|
| エポック / 時間ベース | グレゴリオ暦エポック(1582 年 10 月 15 日) | Unix エポック(1970 年 1 月 1 日) |
| 精度 | 100 ナノ秒 | 1 ミリ秒 |
| ノード識別子 | MAC アドレス(ホスト ID を漏洩) | ランダム(プライベート) |
| プライバシー | MAC アドレスと生成タイムスタンプを漏洩 | ホスト情報を埋め込まない |
| DB インデックスパフォーマンス | 良好——ホストごとに順次 | 優秀——すべてのジェネレーターで k ソート可能 |
| 標準 | RFC 4122(2005 年) | RFC 9562(2024 年) |
新しいプロジェクトでは、UUID v7 が UUID v1 の推奨される置き換えです。ホスト MAC アドレスを埋め込むプライバシーへの影響なしに同様の時間順序付け保証を提供します。
コード例
UUID v1 の生成はブラウザまたは Node.js でネイティブに利用できません。uuid npm パッケージを使用してください:
// Generate a UUID v1 using the Web Crypto API
function generateUuidV1() {
const buf = new Uint8Array(16)
crypto.getRandomValues(buf)
const ms = BigInt(Date.now())
const gregorianOffset = 122192928000000000n
const t = ms * 10000n + gregorianOffset
const tLow = Number(t & 0xFFFFFFFFn)
const tMid = Number((t >> 32n) & 0xFFFFn)
const tHiVer = Number((t >> 48n) & 0x0FFFn) | 0x1000 // version 1
const clockSeq = (buf[8] & 0x3F) | 0x80 // variant 10xxxxxx
const clockSeqLow = buf[9]
const hex = (n, pad) => n.toString(16).padStart(pad, '0')
const node = [...buf.slice(10)].map(b => b.toString(16).padStart(2, '0')).join('')
return `${hex(tLow,8)}-${hex(tMid,4)}-${hex(tHiVer,4)}-${hex(clockSeq,2)}${hex(clockSeqLow,2)}-${node}`
}
// Extract the embedded timestamp from a UUID v1
function extractTimestamp(uuid) {
const parts = uuid.split('-')
const tHex = parts[2].slice(1) + parts[1] + parts[0]
const t = BigInt('0x' + tHex)
const ms = (t - 122192928000000000n) / 10000n
return new Date(Number(ms))
}
const id = generateUuidV1()
console.log(id) // e.g. "1eb5e8b0-6b4d-11ee-9c45-a1f2b3c4d5e6"
console.log(extractTimestamp(id)) // e.g. 2023-10-15T12:34:56.789Zimport uuid from datetime import datetime, timezone # Generate UUID v1 (uses MAC address by default) uid = uuid.uuid1() print(uid) # Extract embedded timestamp # uuid.time is 100-ns intervals since Oct 15, 1582 GREGORIAN_OFFSET = 122192928000000000 # 100-ns intervals ts_100ns = uid.time ts_ms = (ts_100ns - GREGORIAN_OFFSET) // 10000 dt = datetime.fromtimestamp(ts_ms / 1000, tz=timezone.utc) print(dt.isoformat()) # e.g. "2023-10-15T12:34:56.789000+00:00"
package main
import (
"fmt"
"time"
"github.com/google/uuid" // go get github.com/google/uuid
)
func main() {
id, _ := uuid.NewUUID() // UUID v1
fmt.Println(id)
// Extract timestamp from UUID v1
// uuid.Time is 100-ns ticks since Oct 15, 1582
t := id.Time()
sec := int64(t)/1e7 - 12219292800 // convert to Unix seconds
nsec := (int64(t) % 1e7) * 100
ts := time.Unix(sec, nsec).UTC()
fmt.Println(ts.Format(time.RFC3339Nano))
}