สร้าง UUID v4 ด้วย Python
ใช้ เครื่องสร้าง UUID v4 ฟรีโดยตรงในเบราว์เซอร์ของคุณ — ไม่ต้องติดตั้ง
ลอง เครื่องสร้าง UUID v4 ออนไลน์ →ทุกครั้งที่ต้องการ identifier ที่ป้องกันการชนกันสำหรับแถวในฐานข้อมูล API trace หรือ session token คำตอบคือ การสร้าง UUID v4 ใน Python — เพียงหนึ่งบรรทัด ไม่ต้องพึ่ง dependency ใดเลย: uuid.uuid4(). โมดูล uuid ที่มีใน Python ใช้ os.urandom() เพื่อความสุ่มที่มีความปลอดภัยเชิงการเข้ารหัส หากต้องการ UUID อย่างรวดเร็วโดยไม่ต้องเขียนโค้ด ตัวสร้าง UUID v4 ออนไลน์ ใช้งานได้ทันที คู่มือนี้ครอบคลุม attribute ของออบเจกต์ UUID การสร้างจำนวนมาก การแปลงเป็น JSON การจัดเก็บในฐานข้อมูล การตรวจสอบ uuid-utils (ทดแทนแบบ drop-in ด้วย Rust ที่เร็วกว่า ~10 เท่า) และข้อผิดพลาดที่พบบ่อย 4 ประการ — ทั้งหมดรองรับ Python 3.8+
- →
uuid.uuid4()มีใน stdlib ของ Python แล้ว — แค่import uuidก็พอ ไม่ต้องติดตั้งด้วย pip - →ค่าที่ได้คือออบเจกต์
uuid.UUIDไม่ใช่สตริง — ใช้str(),.hexหรือ.bytesเพื่อเลือกรูปแบบที่เหมาะกับ storage layer ของคุณ - →UUID v4 ใช้บิตสุ่ม 122 บิตจาก
os.urandom()— มีความปลอดภัยเชิงการเข้ารหัส ไม่เปิดเผย MAC address หรือ timestamp - →สำหรับบริการ throughput สูง
pip install uuid-utilsเป็น drop-in replacement ที่เร็วกว่า ~10 เท่า ขับเคลื่อนด้วย Rust - →อย่าส่ง
uuid.uuid4(ไม่มีวงเล็บ) เป็น default argument โดยตรงใน dataclass หรือ Pydantic model — จะทำให้ทุก instance ใช้ UUID เดียวกัน
UUID v4 คืออะไร?
UUID (Universally Unique Identifier) คือ label ขนาด 128 บิต ที่จัดรูปแบบเป็น เลขฐานสิบหก 32 หลัก แบ่งเป็น 5 กลุ่มด้วยเครื่องหมายขีด: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. เวอร์ชัน 4 เป็น variant ที่นิยมใช้มากที่สุด: 122 ใน 128 บิตถูกสร้างแบบสุ่ม และอีก 6 บิตที่เหลือเข้ารหัสเวอร์ชัน (4) และ variant (RFC 4122) ไม่มี timestamp และไม่มี host identifier — identifier นี้ทึบแสงและปลอดภัยด้านความเป็นส่วนตัวโดยสมบูรณ์ โอกาสที่ UUID v4 สองค่าที่สร้างแยกกันจะชนกันนั้นน้อยมากจนไม่มีผลในทางปฏิบัติ แม้ในระบบกระจายที่สร้าง ID หลายล้านค่าต่อวินาทีก็ตาม
event_id = "evt-" + str(random.randint(100000, 999999)) # fragile, not unique
event_id = str(uuid.uuid4()) # 3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e
uuid.uuid4() — วิธีมาตรฐานในการสร้าง UUID v4 ใน Python
โมดูล uuid เป็นส่วนหนึ่งของ standard library ของ Python การเรียก uuid.uuid4() คืนค่าออบเจกต์ uuid.UUID พร้อม attribute ครบชุดสำหรับแสดงผลในรูปแบบต่างๆ การแปลงเป็นสตริงด้วย str() จะได้รูปแบบมาตรฐานพร้อมเครื่องหมายขีดที่ API ฐานข้อมูล และ HTTP header คาดหวัง
import uuid # สร้าง UUID v4 request_id = uuid.uuid4() print(request_id) # 3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e print(type(request_id)) # <class 'uuid.UUID'> print(request_id.version) # 4 # แปลงเป็นสตริงสำหรับ JSON / HTTP headers print(str(request_id)) # "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e" print(request_id.hex) # "3b1f8a9d2c4e4f6a8b0d5e7c9f1a3d2e" (ไม่มีเครื่องหมายขีด) print(request_id.bytes) # b';...' (16 raw bytes)
รูปแบบที่พบบ่อยในการใช้งานจริงคือการแนบ UUID กับทุก API request ขาออก เพื่อให้สามารถติดตาม log ข้ามบริการได้ ต่อไปนี้เป็น requests session wrapper ขั้นต่ำที่แทรก UUID ใหม่ในทุกการเรียก:
import uuid
import requests
def call_api(endpoint: str, payload: dict) -> dict:
trace_id = str(uuid.uuid4())
headers = {
"X-Request-ID": trace_id,
"Content-Type": "application/json",
}
response = requests.post(endpoint, json=payload, headers=headers, timeout=10)
response.raise_for_status()
return {"trace_id": trace_id, "data": response.json()}
# result["trace_id"] ช่วยให้ grep หา request นั้นใน log ทุก service ได้
result = call_api("https://api.example.com/v1/orders", {"product_id": "prod_7x2k", "qty": 3})
print(result["trace_id"]) # e.g. "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e"เมื่อต้องสร้าง UUID จำนวนมาก เช่น การเตรียม batch แถวในฐานข้อมูลล่วงหน้า การใช้ list comprehension เป็นวิธีที่ดูเป็นธรรมชาติและอ่านง่าย:
import uuid
# สร้าง ID ล่วงหน้าสำหรับ 1000 telemetry event
event_ids = [str(uuid.uuid4()) for _ in range(1000)]
print(f"Generated {len(event_ids)} unique IDs")
print(event_ids[0]) # e.g. "a1c2e3f4-..."
print(event_ids[-1]) # ค่าต่างกันทุกครั้งต้องการ UUID ด่วนโดยไม่ต้องรันโค้ด? ใช้ ตัวสร้าง UUID v4 ออนไลน์ เพื่อคัดลอกค่าใหม่ในคลิกเดียว หรือสร้างหลายร้อยค่าพร้อมกัน — มีประโยชน์สำหรับ การ seed ฐานข้อมูลทดสอบหรือการใส่ข้อมูลใน fixture file
uuid.uuid4() เรียกใช้ os.urandom(16) ภายใน จากนั้นกำหนด bits 6–7 ของ byte 8 เป็น 10 (variant) และ bits 12–15 ของ byte 6 เป็น 0100 (version 4) ส่วน 122 บิตที่เหลือเป็นค่าสุ่ม นี่คือเหตุผลที่ไม่ควรไว้วางใจ version โดยไม่แปลงด้วย uuid.UUID()Attribute และรูปแบบการแสดงผลของออบเจกต์ UUID
ออบเจกต์ uuid.UUID เปิดเผยรูปแบบต่างๆ ของค่า 128 บิตเดียวกัน การเลือกรูปแบบที่เหมาะสมกับ storage layer ป้องกันการเสียหายของข้อมูลแบบเงียบและการสิ้นเปลืองพื้นที่
import uuid
u = uuid.uuid4()
print(str(u)) # "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e" (36 ตัวอักษร)
print(u.hex) # "3b1f8a9d2c4e4f6a8b0d5e7c9f1a3d2e" (32 ตัวอักษร ไม่มีเครื่องหมายขีด)
print(u.bytes) # b';...' (16 bytes, big-endian)
print(u.bytes_le) # b'...' (16 bytes, little-endian)
print(u.int) # 78823... (จำนวนเต็ม 128 บิต)
print(u.version) # 4
print(u.variant) # 'specified in RFC 4122'
# Round-trip: สร้างใหม่จากสตริง
reconstructed = uuid.UUID("3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e")
print(reconstructed == u) # True (ถ้า u มีค่านั้น)สำหรับ PostgreSQL ที่ใช้ psycopg2 หรือ asyncpg ส่งออบเจกต์ UUID ได้โดยตรง — driver จัดการการแมปกับประเภทคอลัมน์ uuid โดยอัตโนมัติ สำหรับ SQLite ใช้ str(u) (TEXT) หรือ u.bytes (BLOB 16 bytes เทียบกับ 36 bytes ของสตริง) สำหรับประสิทธิภาพการจัดเก็บในปริมาณมาก .bytes เล็กกว่าสตริงมาตรฐานถึง 55%
การตรวจสอบและแปลงสตริง UUID v4 ใน Python
เมื่อ UUID มาจาก user input, URL path parameter หรือ upstream API ควรตรวจสอบก่อนใช้เป็น database key วิธีที่เป็นธรรมชาติคือลองสร้างด้วย uuid.UUID() แล้วจับ ValueError. นอกจากนี้ยังสามารถบังคับให้ค่าที่รับเป็น version 4 โดยตรวจสอบ .versionได้ด้วย
import uuid
def parse_uuid4(raw: str) -> uuid.UUID:
"""
Parse and validate a UUID v4 string.
Raises ValueError for invalid format or wrong version.
"""
try:
u = uuid.UUID(raw)
except ValueError as exc:
raise ValueError(f"Invalid UUID format: {raw!r}") from exc
if u.version != 4:
raise ValueError(f"Expected UUID v4, got v{u.version}: {raw!r}")
return u
# ใช้งานใน FastAPI / Flask route handler
def get_order(order_id: str):
try:
uid = parse_uuid4(order_id)
except ValueError as exc:
return {"error": str(exc)}, 400
# ปลอดภัยที่จะใช้ uid ใน DB query แล้ว
return {"order_id": str(uid), "status": "processing"}uuid.UUID() รับสตริงที่มีหรือไม่มีเครื่องหมายขีด และยังรับคำนำหน้า urn:uuid: ได้ด้วย ดังนั้น "3b1f8a9d2c4e4f6a8b0d5e7c9f1a3d2e" (ไม่มีเครื่องหมายขีด) และ "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e" ทั้งสองแปลงเป็น ออบเจกต์เดียวกันUUID v4 ใน JSON Payload และ API Response
มาตรฐาน JSON ไม่มีประเภท UUID — UUID ใน JSON จึงเป็นสตริงเสมอ ซึ่งหมายความว่า ต้องแปลงออบเจกต์ uuid.UUID เป็นสตริงก่อนส่งให้ json.dumps(). วิธีที่สะอาดที่สุดคือ subclass JSONEncoder เพื่อไม่ต้องกระจาย str() calls ทั่ว codebase
import json
import uuid
from datetime import datetime
class ApiEncoder(json.JSONEncoder):
"""Serialize UUID and datetime objects in JSON responses."""
def default(self, obj):
if isinstance(obj, uuid.UUID):
return str(obj)
if isinstance(obj, datetime):
return obj.isoformat()
return super().default(obj)
# API response จริงที่มี UUID ซ้อนกัน
api_response = {
"request_id": uuid.uuid4(),
"created_at": datetime(2026, 3, 14, 9, 41, 0),
"order": {
"id": uuid.uuid4(),
"customer_id": uuid.uuid4(),
"items": [
{"product_id": uuid.uuid4(), "sku": "NVX-9000", "qty": 2},
],
},
}
print(json.dumps(api_response, indent=2, cls=ApiEncoder))
# {
# "request_id": "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e",
# "created_at": "2026-03-14T09:41:00",
# "order": {
# "id": "a1c2e3f4-...",
# ...
# }
# }สำหรับการแปลงครั้งเดียว hook default= ง่ายกว่าการ subclass:
import json, uuid
event_id = uuid.uuid4()
payload = {"event_id": event_id, "action": "checkout"}
# ส่ง callable; เรียกใช้เฉพาะประเภทที่ json จัดการไม่ได้
json_str = json.dumps(payload, default=str)
print(json_str) # {"event_id": "3b1f8a9d-...", "action": "checkout"}เมื่อรับ response จาก external API ให้แปลงสตริง UUID กลับเป็นออบเจกต์ เพื่อให้โค้ดได้รับ attribute ครบชุดและ type safety:
import json
import uuid
import requests
def fetch_shipment(shipment_id: str) -> dict:
"""Fetch a shipment and return with typed UUID fields."""
response = requests.get(
f"https://api.logistics.example.com/v2/shipments/{shipment_id}",
headers={"Accept": "application/json"},
timeout=10,
)
response.raise_for_status()
data = response.json()
# Parse the UUID fields back to uuid.UUID objects
try:
data["id"] = uuid.UUID(data["id"])
data["carrier_id"] = uuid.UUID(data["carrier_id"])
except (KeyError, ValueError) as exc:
raise RuntimeError(f"Malformed shipment response: {exc}") from exc
return dataสำหรับการอัปเดตฟิลด์ UUID ในไฟล์ JSON บนดิสก์ เช่น การเปลี่ยน correlation ID ใน config หรือ seed file — อ่าน แก้ไข และเขียนกลับอย่างปลอดภัย:
import json, uuid
def rotate_correlation_id(path: str) -> str:
"""Replace or add 'correlation_id' in a JSON file. Returns the new UUID."""
try:
with open(path) as f:
data = json.load(f)
except FileNotFoundError:
data = {}
except json.JSONDecodeError as exc:
raise ValueError(f"Invalid JSON in {path!r}: {exc}") from exc
new_id = str(uuid.uuid4())
data["correlation_id"] = new_id
with open(path, "w") as f:
json.dump(data, f, indent=2)
return new_idหากไม่ต้องการรันสคริปต์ทุกครั้งที่ต้องตรวจสอบ UUID จาก API response ให้วางใน UUID Decoder — แสดงเวอร์ชัน variant และทุกฟิลด์โดยไม่ต้องเขียนโค้ดเลย
สร้าง UUID v4 จาก Command Line ด้วย Python
โมดูล uuid ของ Python ไม่มี CLI subcommand แบบ standalone เหมือน python -m json.tool, แต่ one-liner ครอบคลุมการใช้งานเดียวกัน มีประโยชน์ใน shell script, CI pipeline และเมื่อต้องการ identifier ชั่วคราวโดยไม่ต้องเปิด REPL
# Single UUID v4 python3 -c "import uuid; print(uuid.uuid4())" # 3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e # รูปแบบ hex ไม่มีเครื่องหมายขีด — เหมาะสำหรับ filename และ env var python3 -c "import uuid; print(uuid.uuid4().hex)" # 3b1f8a9d2c4e4f6a8b0d5e7c9f1a3d2e # สร้าง 5 UUID สำหรับ batch seed script python3 -c "import uuid; [print(uuid.uuid4()) for _ in range(5)]" # ใช้ใน shell variable DEPLOY_ID=$(python3 -c "import uuid; print(uuid.uuid4())") echo "Deploying with ID: $DEPLOY_ID"
uuidgen (ยูทิลิตี C) สร้างค่า UUID v4 และเร็วกว่าสำหรับ shell script ล้วนๆ ใช้ Python one-liner เมื่ออยู่ในสภาพแวดล้อมที่เน้น Python และต้องการความสอดคล้องกับวิธีที่สร้าง UUID ในโค้ดแอปพลิเคชันUUID v4 ประสิทธิภาพสูงด้วย uuid-utils
uuid.uuid4() ของ standard library เร็วพอสำหรับแอปพลิเคชันส่วนใหญ่ — ใช้เวลาไม่กี่ไมโครวินาทีต่อการเรียก รองรับการสร้าง ID นับพัน ID ต่อวินาทีได้สบาย หากสร้าง UUID บน hot path ของบริการ throughput สูง (bulk insert, telemetry ต่อ event ขนาดใหญ่ หรือการสร้าง request ID ภายใต้โหลดหนัก) uuid-utils เป็น drop-in replacement ที่รองรับโดย Rust และ benchmark ได้ประมาณ 10 เท่าของ stdlib
pip install uuid-utils
# uuid_utils is a drop-in replacement for the stdlib uuid module import uuid_utils as uuid # Same API as stdlib request_id = uuid.uuid4() print(request_id) # 3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e print(str(request_id)) # canonical string print(request_id.hex) # no-dashes hex print(request_id.version) # 4 # Also supports v7 (time-ordered, great for DB primary keys) time_ordered_id = uuid.uuid7() print(time_ordered_id) # starts with current-timestamp prefix
isinstance(u, uuid.UUID) จาก standard library อย่างเข้มงวด ให้ใช้ compat mode: import uuid_utils.compat as uuid. Compat mode ช้ากว่าโหมดปกติเล็กน้อยแต่ยังเร็วกว่า stdlib อยู่UUID v4 ใน Dataclass และ Pydantic Model
ทั้ง Python dataclass และ Pydantic model รองรับฟิลด์ UUID โดยธรรมชาติ รูปแบบสำคัญเมื่อใช้ UUID เป็น default ที่สร้างอัตโนมัติคือการส่ง reference ของฟังก์ชัน ไม่ใช่ผลลัพธ์จากการเรียกฟังก์ชัน — มิฉะนั้นทุก instance จะใช้ UUID เดียวกัน
from dataclasses import dataclass, field
import uuid
@dataclass
class WorkerJob:
job_id: uuid.UUID = field(default_factory=uuid.uuid4)
worker_id: str = "worker-01"
payload: dict = field(default_factory=dict)
job1 = WorkerJob(payload={"task": "resize_image", "src": "uploads/img_4932.png"})
job2 = WorkerJob(payload={"task": "send_email", "to": "ops@example.com"})
print(job1.job_id) # unique per instance
print(job2.job_id) # different from job1.job_id
print(job1.job_id == job2.job_id) # Falsefrom pydantic import BaseModel, Field
import uuid
class OrderEvent(BaseModel):
event_id: uuid.UUID = Field(default_factory=uuid.uuid4)
order_id: uuid.UUID
status: str
amount_cents: int
event = OrderEvent(
order_id=uuid.UUID("a1c2e3f4-5b6d-4e7f-8c9d-0a1b2c3d4e5f"),
status="payment_confirmed",
amount_cents=4995,
)
print(event.model_dump_json(indent=2))
# {
# "event_id": "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e",
# "order_id": "a1c2e3f4-5b6d-4e7f-8c9d-0a1b2c3d4e5f",
# "status": "payment_confirmed",
# "amount_cents": 4995
# }
# Pydantic v2 serializes uuid.UUID as string automaticallyข้อผิดพลาดที่พบบ่อยในการสร้าง UUID v4 ใน Python
รูปแบบทั้งสี่นี้มักปรากฏใน code review และ production incident — พลาดได้ง่ายเพราะไม่เกิด error ทันที
ปัญหา: การส่ง uuid.uuid4 (ออบเจกต์ฟังก์ชัน) เป็น default value ใน dataclass หรือ model โดยไม่ห่อด้วย default_factory — Python ประเมิน default เพียงครั้งเดียวตอน class definition ทำให้ทุก instance ใช้ UUID เดียวกัน
วิธีแก้ไข: ใช้ default_factory=uuid.uuid4 ใน dataclass หรือ Field(default_factory=uuid.uuid4) ใน Pydantic เพื่อสร้าง UUID ใหม่ต่อ instance
@dataclass
class Session:
# ผิด: ประเมินครั้งเดียว ทุก instance ใช้ UUID นี้ร่วมกัน
session_id: uuid.UUID = uuid.uuid4()@dataclass
class Session:
# ถูกต้อง: factory ถูกเรียกต่อ instance
session_id: uuid.UUID = field(default_factory=uuid.uuid4)ปัญหา: ออบเจกต์ uuid.UUID ไม่เท่ากับสตริงธรรมดา ดังนั้น session_id == '3b1f8a9d-...' จะคืนค่า False เสมอแม้ค่าจะตรงกัน — ทำให้ lookup พังโดยไม่มี error
วิธีแก้ไข: เปรียบเทียบ UUID กับ UUID เสมอ: ห่อสตริงด้วย uuid.UUID() ก่อนเปรียบเทียบ หรือแปลงทั้งสองฝั่งเป็น str()
# คืน False แม้ค่าจะตรงกัน
if record["session_id"] == "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e":
revoke_session(record)target = uuid.UUID("3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e")
if record["session_id"] == target: # ทั้งสองเป็น uuid.UUID
revoke_session(record)
# หรือ normalize เป็นสตริงที่จุดขอบเขต:
if str(record["session_id"]) == str(target):
revoke_session(record)ปัญหา: uuid_obj.hex ให้สตริง 32 ตัวอักษรไม่มีเครื่องหมายขีด ถ้าโค้ดปลายทางคาดหวังรูปแบบมาตรฐาน 36 ตัวอักษรพร้อมเครื่องหมายขีด (ส่วนใหญ่ API และฐานข้อมูลคาดหวังแบบนี้) จะปฏิเสธหรือแปลงค่าผิดพลาดแบบเงียบ
วิธีแก้ไข: ใช้ str(uuid_obj) สำหรับรูปแบบมาตรฐาน 36 ตัวอักษร เว้นแต่มีความต้องการชัดเจนสำหรับรูปแบบ hex แบบกะทัดรัด
# จัดเก็บ "3b1f8a9d2c4e4f6a8b0d5e7c9f1a3d2e" — ไม่มีเครื่องหมายขีด
payload = {"correlation_id": request_id.hex}# จัดเก็บ "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e" — รูปแบบมาตรฐาน
payload = {"correlation_id": str(request_id)}ปัญหา: random.random() ไม่มีความปลอดภัยเชิงการเข้ารหัส และ secrets.token_hex(16) ให้สตริง hex 32 ตัวอักษรที่ไม่ใช่ UUID ที่ถูกต้อง — ตัวตรวจสอบปลายทางที่เรียก uuid.UUID() จะโยน ValueError
วิธีแก้ไข: ใช้ uuid.uuid4() เมื่อระบบที่รับคาดหวัง identifier รูปแบบ UUID ใช้ secrets.token_hex() เฉพาะเมื่อต้องการ token สุ่มที่ไม่ต้องอยู่ในรูปแบบ UUID
import random, secrets # ไม่ใช่ UUID — จะล้มเหลวเมื่อตรวจสอบด้วย uuid.UUID() request_id = secrets.token_hex(16) # "a1b2c3d4e5f6..." session_id = str(random.random()) # "0.8273..." — ไม่ใกล้เคียงเลย
import uuid request_id = str(uuid.uuid4()) # "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e" # Valid UUID v4, cryptographically secure
วิธีสร้าง UUID ใน Python — เปรียบเทียบโดยย่อ
ทุกวิธีด้านล่างสร้าง identifier ขนาด 128 บิต แต่แตกต่างกันที่แหล่งความสุ่ม คุณสมบัติด้านความเป็นส่วนตัว และความต้องการติดตั้ง third-party
ใช้ uuid.uuid4() สำหรับ identifier ทั่วไปใน web application ระบบกระจาย และ database primary key เมื่อไม่ต้องการความสามารถในการเรียงลำดับ ใช้ uuid.uuid5() (หรือ v3) สำหรับ ID ที่กำหนดได้จาก namespace และ name ที่รู้จัก เช่น การสร้าง ID ที่คงที่สำหรับ canonical URL เปลี่ยนไปใช้ uuid_utils.uuid7() เมื่อต้องการ ID เรียงตามเวลาสำหรับ database index (หลีกเลี่ยง page split ใน B-tree index ที่ insert rate สูง) และใช้ uuid_utils.uuid4() เมื่อ throughput การสร้างเป็น bottleneck
UUID v4 เทียบกับ UUID v7 — ควรใช้อันไหน?
คำถามที่พบบ่อยที่สุดในทางปฏิบัติคือจะใช้ UUID v4 หรือ UUID v7 ที่ใหม่กว่า สำหรับ database primary key คำตอบสั้นๆ คือ: ใช้ UUID v4 เป็นค่าเริ่มต้น เปลี่ยนเป็น UUID v7 เฉพาะเมื่อ index fragmentation เป็นปัญหาที่วัดได้จริง
ค่า UUID v4 เป็นสุ่มอย่างสมบูรณ์ ซึ่งหมายความว่า insert จะลงที่ตำแหน่งสุ่มใน B-tree index ที่ insert rate ปานกลาง (ไม่กี่ร้อยถึงไม่กี่พัน insert ต่อวินาที) ทำได้ดี — index อยู่ใน buffer pool และการเขียนแบบสุ่มถูก ที่ insert rate สูงมาก การวางแบบสุ่มทำให้เกิด page split และ cache miss บ่อย เพิ่ม write amplification และทำให้ query ช้า
UUID v7 ฝัง Unix timestamp ความแม่นยำ millisecond ในบิตที่มีนัยสำคัญมากที่สุด ดังนั้นแถวที่ insert ใกล้เวลากันจะอยู่ใกล้กันใน index ด้วย ทำให้ B-tree index (PostgreSQL, MySQL, SQLite) ทำงานคล้ายกับ auto-increment integer: แถวใหม่จะต่อท้าย index เสมอ ขจัด page split ข้อเสียคือ UUID v7 เข้ารหัส timestamp ซึ่งเปิดเผยเวลาสร้าง — หลีกเลี่ยงสำหรับ ID ที่ผู้ใช้เห็นเมื่อเวลาสร้างเป็นข้อมูลสำคัญ
ใน Python UUID v7 ยังไม่อยู่ใน standard library (ณ Python 3.12) สร้างได้ด้วย pip install uuid-utils แล้วเรียก uuid_utils.uuid7(). คืนค่าออบเจกต์ที่มี attribute set เดียวกับ uuid.UUID, ดังนั้นการย้ายจาก v4 เปลี่ยนเพียงบรรทัดเดียวใน ID factory
สำหรับทางเลือกที่คลิกเดียวโดยไม่ต้องตั้งค่า Python วาง UUID string ลงใน ตัวสร้างและตัวตรวจสอบ UUID v4 — สร้าง ตรวจสอบ และถอดรหัสทุกฟิลด์ในเบราว์เซอร์
คำถามที่พบบ่อย
จะสร้าง UUID v4 ใน Python ได้อย่างไร?
เรียกใช้ uuid.uuid4() จากโมดูล uuid ที่มีอยู่ใน Python แล้วได้รับออบเจกต์ UUID — แปลงเป็นสตริงด้วย str() เมื่อต้องการใช้เป็นข้อความ โมดูลนี้มาพร้อมกับ standard library จึงไม่ต้องติดตั้งเพิ่มเติมด้วย pip
import uuid session_id = uuid.uuid4() print(session_id) # e.g. 3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e print(str(session_id)) # same canonical string print(session_id.hex) # 3b1f8a9d2c4e4f6a8b0d5e7c9f1a3d2e (no dashes)
uuid.uuid4() กับ str(uuid.uuid4()) แตกต่างกันอย่างไร?
uuid.uuid4() คืนค่าออบเจกต์ UUID ที่มี attribute ต่างๆ เช่น .hex, .bytes, .int และ .version ส่วน str(uuid.uuid4()) แปลงออบเจกต์นั้นเป็นสตริงมาตรฐาน 36 ตัวอักษรทันที ทิ้งออบเจกต์ไป ควรเก็บออบเจกต์ไว้หากต้องการหลายรูปแบบ แต่แปลงเป็นสตริงที่จุดส่งออกเมื่อส่งค่าไปยัง JSON payload ฐานข้อมูล หรือ HTTP header
import uuid u = uuid.uuid4() print(type(u)) # <class 'uuid.UUID'> print(u.version) # 4 print(u.hex) # 32-char hex, no dashes print(u.bytes) # 16-byte binary print(str(u)) # canonical 36-char string with dashes
uuid.uuid4() มีความปลอดภัยเชิงการเข้ารหัสหรือไม่?
ใช่ uuid.uuid4() ของ Python ใช้ os.urandom() ภายใน ซึ่งอ่านค่าจากตัวสร้างเลขสุ่มที่มีความปลอดภัยเชิงการเข้ารหัสของระบบปฏิบัติการ (/dev/urandom บน Linux/macOS และ CryptGenRandom บน Windows) บิตสุ่ม 122 บิตทำให้โอกาสชนกันนั้นน้อยมากจนไม่มีผลต่อการใช้งานจริง อย่าสับสนกับ random.random() ซึ่งไม่มีความปลอดภัยเชิงการเข้ารหัส
import uuid, os # uuid4 เรียกใช้ os.urandom(16) ภายใน raw = os.urandom(16) # uuid4 กำหนด version และ variant bits ก่อนส่งคืนค่า u = uuid.UUID(bytes=raw, version=4) print(u) # valid v4 UUID from raw random bytes
จะตรวจสอบว่าสตริงเป็น UUID v4 ที่ถูกต้องใน Python ได้อย่างไร?
แปลงด้วย uuid.UUID() แล้วตรวจสอบ attribute .version ถ้าสตริงไม่ใช่ UUID ที่ถูกต้อง uuid.UUID() จะโยน ValueError — จับ exception เพื่อจัดการ input ที่ไม่ถูกต้อง วิธีนี้ยังตรวจสอบรูปแบบ (เครื่องหมายขีดและความยาว) ด้วย
import uuid
def is_valid_uuid4(value: str) -> bool:
try:
u = uuid.UUID(value)
return u.version == 4
except ValueError:
return False
print(is_valid_uuid4("3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e")) # True
print(is_valid_uuid4("not-a-uuid")) # False
print(is_valid_uuid4("3b1f8a9d-2c4e-1f6a-8b0d-5e7c9f1a3d2e")) # False (v1, not v4)จะจัดเก็บ UUID ใน PostgreSQL หรือ SQLite ด้วย Python ได้อย่างไร?
สำหรับ PostgreSQL (ผ่าน psycopg2 หรือ asyncpg) ส่งออบเจกต์ UUID ได้โดยตรง — driver จะแปลงไปยัง native UUID type โดยอัตโนมัติ สำหรับ SQLite ซึ่งไม่มี UUID type โดยตรง ให้จัดเก็บเป็น TEXT ด้วย str(uuid_obj) หรือเป็น BLOB ด้วย uuid_obj.bytes และ SQLAlchemy มีประเภทคอลัมน์ UUID ที่จัดการเรื่องนี้โดยอัตโนมัติทุก dialect
import uuid
import sqlite3
conn = sqlite3.connect(":memory:")
conn.execute("CREATE TABLE events (id TEXT PRIMARY KEY, name TEXT)")
event_id = uuid.uuid4()
conn.execute("INSERT INTO events VALUES (?, ?)", (str(event_id), "user_signup"))
conn.commit()
row = conn.execute("SELECT * FROM events").fetchone()
# Reconstruct UUID object from stored string
retrieved_id = uuid.UUID(row[0])
print(retrieved_id.version) # 4สามารถสร้าง UUID หลายค่าพร้อมกันใน Python ได้หรือไม่?
ได้ — ใช้ list comprehension หรือ generator โดยการเรียก uuid.uuid4() แต่ละครั้งนั้นเป็นอิสระและรับประกันว่าได้ค่าที่ต่างกัน สำหรับการสร้างจำนวนมากที่ต้องการ throughput สูง uuid-utils (ที่รองรับโดย Rust) เร็วกว่า standard library ประมาณ 10 เท่า
import uuid
# สร้าง 5 trace ID สำหรับ batch request
trace_ids = [str(uuid.uuid4()) for _ in range(5)]
for tid in trace_ids:
print(tid)
# แต่ละบรรทัดคือ UUID v4 ที่แตกต่างกันเครื่องมือที่เกี่ยวข้อง
- →UUID v4 Generator — สร้างค่า UUID v4 ในเบราว์เซอร์ทันที ไม่ต้องใช้ Python เลย คัดลอกค่าเดียวหรือสร้างจำนวนมากได้ในคลิกเดียว
- →UUID v7 Generator — สร้างค่า UUID v7 ที่เรียงลำดับตามเวลา — เหมาะสำหรับ primary key ในฐานข้อมูลที่ต้องการลด index fragmentation
- →UUID Decoder — ตรวจสอบ UUID ใดก็ได้ — เวอร์ชัน, variant, timestamp (v1/v7) และฟิลด์ node โดยไม่ต้องเขียน parser เอง
- →JWT Decoder — ถอดรหัสและตรวจสอบ JWT token ซึ่งมักบรรจุ UUID ใน claim subject (sub) หรือ jti identifier พร้อม session UUID
Maria is a backend developer specialising in Python and API integration. She has broad experience with data pipelines, serialisation formats, and building reliable server-side services. She is an active member of the Python community and enjoys writing practical, example-driven guides that help developers solve real problems without unnecessary theory.
Dmitri is a DevOps engineer who relies on Python as his primary scripting and automation language. He builds internal tooling, CI/CD pipelines, and infrastructure automation scripts that run in production across distributed teams. He writes about the Python standard library, subprocess management, file processing, encoding utilities, and the practical shell-adjacent Python that DevOps engineers use every day.