Python uuid7: ID เรียงตามเวลาโดยไม่เปิดเผย MAC Address
UUIDv4 เหมาะกับการสร้าง ID แบบกระจาย แต่ค่าใหม่กระโดดทั่ว index ของฐานข้อมูลครับ สำหรับ B-tree นี่คล้ายเอาเอกสารใหม่ไปเสียบสุ่มทุกลิ้นชัก ทำให้ page split และ cache locality แย่กว่าคีย์ที่เดินไปข้างหน้า UUIDv7 เก็บเวลาไว้ด้านหน้า จึงเรียงตามการสร้างได้ดีขึ้นโดยยังมีส่วนสุ่มมากพอ
Python 3.14 เพิ่ม uuid.uuid7 ตาม RFC 9562 ค่า time คืน Unix timestamp เป็นมิลลิวินาที และ implementation ใช้ counter เพื่อรักษาลำดับภายในมิลลิวินาทีบนแพลตฟอร์มที่ไม่มีความละเอียดต่ำกว่านั้น
WHY: Locality ไม่เท่ากับลำดับธุรกิจ
UUIDv7 ช่วย index locality และทำ cursor ตามเวลาได้ แต่ไม่ใช่นาฬิกากลางที่เชื่อถือได้ เครื่องสองเครื่องอาจ clock skew และ event ที่ commit ทีหลังอาจมี timestamp ก่อนหน้า จึงห้ามใช้ UUID เป็นหลักฐานว่า payment A เกิดก่อน B แบบเด็ดขาด งาน audit ยังต้องมี server timestamp และ sequence ตาม domain
ด้าน privacy UUIDv7 เปิดเผยเวลาโดยประมาณ ใครเห็น ID อาจเดาช่วงที่ record ถูกสร้างได้ ถ้าเวลานั้นเป็นข้อมูลอ่อนไหวหรือ ID ถูกใช้เป็น secret ให้ใช้ token สุ่มแยกต่างหาก UUID ไม่ใช่ authorization
import unittest
import uuid
class UUID7Tests(unittest.TestCase):
def test_uuid7_has_version_and_time_order(self):
values = [uuid.uuid7() for _ in range(50)]
self.assertTrue(all(value.version == 7 for value in values))
self.assertEqual(values, sorted(values))
self.assertTrue(all(a.time <= b.time for a, b in zip(values, values[1:])))
def test_round_trip_preserves_value(self):
value = uuid.uuid7()
self.assertEqual(uuid.UUID(str(value)), value)
if __name__ == "__main__":
unittest.main()
HOW: ย้ายโดยไม่หยุดระบบ
เพิ่ม column ใหม่และ index แบบ online ก่อน เขียนทั้ง ID เดิมกับ UUIDv7 ช่วงหนึ่ง แล้วให้ read path รองรับสองรูปแบบ อย่าเรียง text ที่เติม prefix ต่างกันและคาดหวัง binary ordering เหมือนกัน ตรวจชนิด native UUID ของฐานข้อมูลและ byte order ให้ชัด
benchmark ด้วย write concurrency และขนาดตารางจริง เปรียบเทียบ page split, index size, insert p95 และ cache hit ไม่ใช่ดูเพียง throughput ในฐานข้อมูลว่าง หาก workload ใช้ append-only time-series key อยู่แล้ว UUIDv7 อาจไม่เพิ่มคุณค่า
เมื่อใช้ cursor pagination ให้เรียงด้วย UUIDv7 และ tie-break ตาม primary keyเดียวกัน อย่า decode timestamp เพื่อ query ช่วงเวลาแทน column เวลา เพราะ clock correction, import ข้อมูลเก่า และ test fixture อาจทำให้ความหมายปนกัน
Recap
- UUIDv7 วางเวลาไว้ด้านหน้าเพื่อช่วย index locality
- Python 3.14 รักษาลำดับภายในมิลลิวินาทีด้วย counter
- ไม่ใช้ UUID เป็น secret หรือหลักฐานลำดับธุรกิจ
- ย้ายแบบ dual-write และตรวจ byte order
- benchmark index behavior บนข้อมูลและ concurrency จริง
เริ่มทดลองในตาราง event ที่เขียนหนักหนึ่งตาราง แล้วดู page split ก่อนเปลี่ยน primary key ระบบใหญ่ครับ