ToolDeck

เครื่องสร้าง UUID v1

สร้าง UUID v1 ตามเวลาพร้อม timestamp ที่ฝังอยู่

จัดรูปแบบ

จำนวน:
Note:UUID v1 ฝัง MAC address ของ host และ timestamp ที่สร้าง ซึ่งก่อให้เกิด ข้อกังวลด้านความเป็นส่วนตัว สำหรับแอปพลิเคชันสมัยใหม่ส่วนใหญ่ สำหรับโปรเจกต์ใหม่แนะนำ UUID v4 เว้นแต่คุณต้องการ identifier ที่เรียงตามเวลาและถอดรหัสได้โดยเฉพาะ

UUID v1 คืออะไร?

UUID v1 คือ UUID version ดั้งเดิม กำหนดมาตรฐานใน RFC 4122 (2005) สร้าง identifier ที่ไม่ซ้ำโดยรวม timestamp ความแม่นยำสูงกับ MAC address ของ host ที่สร้าง บวกกับ clock sequence สั้นๆ เพื่อจัดการ sub-timestamp resolution

เนื่องจาก timestamp ฝังอยู่ ค่า UUID v1 จาก host เดิมจะเพิ่มขึ้นเรื่อยๆ ตามเวลา ทำให้เรียงลำดับได้ตามธรรมชาติ ออกแบบมาสำหรับระบบ distributed ที่แต่ละ node สร้าง UUID ได้อิสระโดยไม่ต้องประสานงาน

ปัจจุบัน UUID v1 ถูกแทนที่โดย UUID v7 (เรียงลำดับได้, ไม่รั่ว MAC) และ UUID v4 (สุ่มทั้งหมด, เป็นส่วนตัว) เป็นส่วนใหญ่ ยังคงใช้งานในระบบอย่าง Apache Cassandra และฐานข้อมูล distributed แบบ legacy

โครงสร้างของ UUID v1

UUID v1 string เช่น 550e8400-e29b-11d4-a716-446655440000 เข้ารหัสฟิลด์หกฟิลด์ที่แตกต่างกัน:

Fieldขนาดคำอธิบาย
time_low32 bitslow field 32 บิตของ Gregorian timestamp 60 บิต (ช่วง 100 นาโนวินาทีนับจาก 15 ต.ค. 1582)
time_mid16 bitsmiddle field 16 บิตของ timestamp 60 บิต
time_hi_and_version16 bits12 บิตบนของ timestamp 60 บิต บวกกับ version number 4 บิต (เสมอ <code>1</code>)
clock_seq_hi_res8 bitshigh field 6 บิตของ clock sequence รวมกับ variant marker 2 บิตตาม RFC 4122
clock_seq_low8 bits8 บิตล่างของ clock sequence
node48 bitsnode identifier 48 บิต — โดยทั่วไปคือ MAC address ของ network interface ที่สร้าง หรือค่าสุ่ม 48 บิตหากไม่มี MAC

ฟิลด์ clock sequence (clock_seq_hi_res + clock_seq_low) คือ counter 14 บิต จะเพิ่มขึ้นทุกครั้งที่ system clock เดินถอยหลัง (เช่น NTP adjustment) หรือเมื่อระบบ restart โดยไม่ได้บันทึก timestamp ล่าสุด เพื่อป้องกันการสร้าง UUID ซ้ำหาก clock ไม่เดินไปข้างหน้าอย่างต่อเนื่อง

การถอดรหัส UUID v1 Timestamp

timestamp 60 บิตกระจายอยู่ใน UUID สามฟิลด์ ในการประกอบเวลาที่สร้างใหม่:

  1. ดึง time_low (bytes 0–3), time_mid (bytes 4–5), และ time_hi (bytes 6–7 ลบ version nibble)
  2. ประกอบใหม่: (time_hi &lt;&lt; 48) | (time_mid &lt;&lt; 32) | time_low
  3. ผลลัพธ์คือ count 60 บิตของ ช่วง 100 นาโนวินาที นับจาก 15 ตุลาคม 1582 (Gregorian calendar epoch)
  4. ลบ Gregorian-to-Unix offset: 122,192,928,000,000,000 (ช่วง 100-ns ระหว่าง 15 ต.ค. 1582 และ 1 ม.ค. 1970)
  5. หารด้วย 10,000 เพื่อแปลงช่วง 100 นาโนวินาทีเป็น millisecond
  6. ใช้ผลลัพธ์เป็น Unix millisecond timestamp เพื่อสร้าง Date object
  7. Format เป็น ISO 8601 สำหรับ output ที่มนุษย์อ่านได้

ความแม่นยำของ timestamp คือ 100 นาโนวินาที ละเอียดกว่าความแม่นยำ millisecond ของ UUID v7 มาก อย่างไรก็ตาม ในทางปฏิบัติระบบปฏิบัติการส่วนใหญ่ไม่เปิดเผย clock resolution ระดับ sub-millisecond ดังนั้นบิตล่างมักเป็นศูนย์หรือสังเคราะห์ขึ้น

ข้อกังวลด้านความเป็นส่วนตัว

ข้อเสียที่สำคัญที่สุดของ UUID v1 คือมันฝัง MAC address ของ host ที่สร้างใน node field ซึ่งหมายความว่า UUID v1 ทุกตัวมี fingerprint ถาวรและไม่ซ้ำกันทั่วโลกของเครื่องที่สร้างมัน

ผู้โจมตีที่ได้ UUID v1 สามารถระบุ: (1) เวลาโดยประมาณที่สร้าง ID, (2) MAC address ของ host ที่สร้าง, และ (3) โดยการวิเคราะห์ UUID หลายตัว อัตราการสร้าง ID

ด้วยเหตุนี้ UUID v1 ไม่ควรใช้เป็น identifier ที่เปิดสาธารณะ (เช่น ใน URL หรือ API response) เว้นแต่คุณยินดีเปิดเผยข้อมูลนี้ RFC 4122 เองระบุว่าระบบอาจใช้ค่าสุ่ม 48 บิตแทน MAC address แต่การ implementation จำนวนมากไม่ทำเช่นนั้น

เมื่อไร UUID v1 ยังเหมาะสม

Apache Cassandra primary key
Cassandra ใช้ UUID v1 (ผ่าน TimeUUID type) เป็นรูปแบบการออกแบบหลัก การเรียงลำดับตาม timestamp แมปกับ storage model ของ Cassandra ตามธรรมชาติ ช่วยให้ query ช่วงเวลาได้อย่างมีประสิทธิภาพ
ระบบ distributed แบบ legacy
ระบบที่สร้างก่อน UUID v7 มีอยู่ (ก่อน 2024) ที่ใช้ UUID เรียงตาม timestamp และไม่สามารถย้ายไปรูปแบบใหม่ได้ง่าย
Audit และ event log
เมื่อ host identity ที่สร้างเป็นที่รู้จักและเชื่อถือได้ การฝัง MAC address สามารถให้ traceability เพิ่มเติมสำหรับ audit event
Internal identifier
ID ที่ไม่ได้เปิดเผยภายนอกระบบ internal ที่ควบคุม ซึ่งการเปิดเผย MAC address ไม่ใช่ข้อกังวล
Time-range query โดยไม่มี timestamp column แยกต่างหาก
timestamp ที่ฝังอยู่สามารถถอดรหัสเพื่อ filter row ตามเวลาที่สร้าง ทำหน้าที่เป็นทั้ง ID และ timestamp รวมกัน
Interoperability กับ UUID v1 generator เก่า
เมื่อรับหรือประมวลผลค่า UUID v1 จากระบบภายนอกที่สร้างมัน การถอดรหัส timestamp ที่ฝังสำหรับการแสดงผลหรือวิเคราะห์

UUID v1 vs UUID v7

UUID v7 คือผู้สืบทอดสมัยใหม่ของ UUID v1 สำหรับ identifier ที่เรียงตามเวลา ต่อไปนี้คือการเปรียบเทียบตรงๆ:

ด้านUUID v1UUID v7
Epoch / Time BaseGregorian epoch (15 ต.ค. 1582)Unix epoch (1 ม.ค. 1970)
ความแม่นยำ100 nanoseconds1 millisecond
Node IdentifierMAC address (รั่ว host identity)สุ่ม (เป็นส่วนตัว)
ความเป็นส่วนตัวรั่ว MAC address และ generation timestampไม่มีข้อมูล host ฝังอยู่
ประสิทธิภาพ DB indexดี — sequential ต่อ hostยอดเยี่ยม — k-sortable ทั่วทุก generator
มาตรฐานRFC 4122 (2005)RFC 9562 (2024)

สำหรับโปรเจกต์ใหม่ UUID v7 คือทางเลือกที่แนะนำแทน UUID v1 ให้การรับประกัน time-ordering ที่คล้ายกันโดยไม่มีผลกระทบด้านความเป็นส่วนตัวจากการฝัง host MAC address

ตัวอย่างโค้ด

การสร้าง UUID v1 ไม่มีแบบ native ในเบราว์เซอร์หรือ Node.js ใช้ uuid npm package:

JavaScript (browser)
// 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.789Z
Python
import 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"
Go
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))
}

คำถามที่พบบ่อย

สามารถถอดรหัส timestamp จาก UUID v1 ได้ไหม?
ใช่ generation timestamp สามารถกู้คืนได้ทั้งหมดจาก UUID v1 string เครื่องมือนี้ทำสิ่งนั้นโดยตรง วาง UUID v1 ใดๆ แล้วมันจะแสดง UTC timestamp ที่ถอดรหัสแล้ว ดูขั้นตอนการถอดรหัสด้านบนสำหรับ algorithm
MAC address มีอยู่ใน UUID v1 เสมอไหม?
ไม่จำเป็น RFC 4122 อนุญาตให้ implementation ใช้ค่าสุ่ม 48 บิตแทน MAC address เมื่อไม่มี network interface หรือต้องการความเป็นส่วนตัว ในทางปฏิบัติ implementation ฝั่ง server จำนวนมากฝัง MAC address จริง ค่า UUID v1 ที่สร้างในเบราว์เซอร์ใช้ค่า node แบบสุ่มเสมอเนื่องจากเบราว์เซอร์ไม่เปิดเผย MAC address
ทำไม UUID v1 timestamp ถึงใช้ปี 1582 เป็น epoch?
การปฏิรูปปฏิทิน Gregorian มีผลบังคับใช้เมื่อ 15 ตุลาคม 1582 UUID v1 timestamp กำหนดสัมพันธ์กับวันนี้เพื่อให้มีจุดอ้างอิงที่เสถียรและเป็นสากลก่อน Unix epoch (1970) ทำให้ฟิลด์ timestamp 60 บิตมีช่วงพอที่จะคงความไม่ซ้ำกันจนถึงประมาณปี 3400 AD
UUID v1 vs UUID v7 — เมื่อไรควรยังใช้ v1?
เหตุผลหลักในการใช้ UUID v1 วันนี้คือความเข้ากันได้กับระบบที่มีอยู่ โดยเฉพาะ Apache Cassandra ซึ่งใช้ v1 เป็น TimeUUID type สำหรับระบบใหม่ทั้งหมด UUID v7 ดีกว่าอย่างเด็ดขาด: ใช้ Unix epoch ที่คุ้นเคยกว่า ไม่รั่ว MAC address และให้ประสิทธิภาพ B-tree index ที่ดีกว่า
ค่า UUID v1 จะ collide ได้ไหม?
ในทางทฤษฎี UUID v1 สองตัวสามารถ collide ได้หาก MAC address เดิมสร้าง UUID สองตัวภายใน 100 นาโนวินาทีเดิมและ clock sequence เหมือนกัน clock sequence มีไว้เพื่อป้องกันสิ่งนี้โดยเพิ่มขึ้นเมื่อมีการเรียกติดกันอย่างรวดเร็ว ในทางปฏิบัติ UUID v1 collision หายากมากในระบบที่ implement ถูกต้อง

เครื่องมือที่เกี่ยวข้อง