21 คะแนน โดย GN⁺ 2024-07-06 | 4 ความคิดเห็น | แชร์ทาง WhatsApp
  • UUID มักถูกใช้เป็นคีย์หลักของตารางฐานข้อมูล
    • สร้างได้ง่าย แชร์ข้ามระบบแบบกระจายได้สะดวก และรับประกันความเป็นเอกลักษณ์
    • เมื่อพิจารณาจากขนาดของ UUID ก็อาจสงสัยว่านี่เป็นตัวเลือกที่ถูกต้องหรือไม่ แต่บ่อยครั้งเราไม่ได้เป็นผู้ตัดสินใจเอง
  • บทความนี้ไม่ได้โฟกัสที่คำถามว่า "UUID เป็นรูปแบบที่เหมาะกับคีย์หรือไม่" แต่จะอธิบายวิธีใช้ UUID เป็นคีย์หลักใน PostgreSQL อย่างมีประสิทธิภาพ

การใช้ PostgreSQL กับ UUID เป็นคีย์หลัก

  • UUID คืออะไร?
    • UUID มักถูกใช้เป็นคีย์หลักของตารางฐานข้อมูล
    • แชร์ข้ามระบบแบบกระจายได้ง่าย และรับประกันความเป็นเอกลักษณ์
    • ด้วยขนาดของ UUID จึงอาจมีคำถามว่าเหมาะสมหรือไม่ แต่หลายครั้งก็ไม่มีทางเลือกมากนัก

ชนิดข้อมูล UUID ใน PostgreSQL

  • เก็บ UUID เป็นสตริง

    • PostgreSQL มีชนิดข้อมูล text สำหรับเก็บสตริง
    • แต่ชนิด text ไม่เหมาะสำหรับการเก็บ UUID
    • PostgreSQL มีชนิดข้อมูลเฉพาะสำหรับ UUID คือ uuid
    • ชนิด uuid เป็นชนิดข้อมูล 128 บิต และต้องใช้ 16 ไบต์ในการเก็บหนึ่งค่า
    • ส่วนชนิด text จะมี overhead เพิ่มอีก 1 หรือ 4 ไบต์
  • ผลการทดลอง

    • สร้างตารางสองตารางเพื่อเปรียบเทียบ: ตารางหนึ่งใช้ชนิด text อีกตารางใช้ชนิด uuid
    • หลังแทรกข้อมูล 10,000,000 แถวแล้วจึงเปรียบเทียบขนาดตารางและขนาดดัชนี
    • ตารางที่ใช้ชนิด text มีขนาดใหญ่กว่า 54% และขนาดดัชนีใหญ่กว่า 85%

UUID กับดัชนี B-Tree

  • ดัชนี B-Tree และ UUID

    • UUID แบบสุ่มไม่เหมาะกับดัชนี B-Tree
    • ดัชนี B-Tree ทำงานได้ดีกับค่าที่มีลำดับ
    • UUID.randomUUID() ของ Java จะคืนค่า UUID v4 ซึ่งเป็นค่า pseudo-random
    • UUID v7 สร้างค่าที่เรียงตามลำดับเวลา จึงเหมาะกับดัชนี B-Tree
  • การใช้ UUID v7

    • หากต้องการใช้ UUID v7 ใน Java จำเป็นต้องใช้ไลบรารี java-uuid-generator
    • การสร้าง UUID v7 อาจช่วยเพิ่มประสิทธิภาพการแทรกข้อมูลได้

ผลกระทบของ UUID v7 ต่อประสิทธิภาพของ INSERT

  • การทดลอง
    • สร้างตารางที่ใช้ UUID v7 แล้วแทรกข้อมูล 10,000 แถวจำนวน 10 ครั้งเพื่อวัดประสิทธิภาพ
    • แม้ผลลัพธ์จะค่อนข้างสุ่มอยู่บ้าง แต่การแทรก UUID v7 เร็วกว่าโดยประมาณ 2 เท่า

อ่านเพิ่มเติม

  • มีความเป็นไปได้ว่า PostgreSQL 17 จะรองรับ UUID v7 แบบเนทีฟ
  • ข้อมูลเกี่ยวกับฟอร์แมต UUID v7
  • ผลกระทบของ UUID ต่อประสิทธิภาพเมื่อใช้เป็นคีย์หลักของฐานข้อมูล

สรุป

  • ปัญหาเรื่องความยาวของ UUID

    • แม้จะมีการปรับแต่งแล้ว UUID ก็ยังไม่ใช่ชนิดที่เหมาะที่สุดสำหรับการเป็นคีย์หลัก
    • หากมีทางเลือก ควรพิจารณาตัวเลือกอื่นอย่าง TSID
  • ความจำเป็นของการปรับแต่ง

    • หากคาดว่าจะมีชุดข้อมูลขนาดใหญ่หรือทราฟฟิกสูง ก็ควรพิจารณาการปรับแต่ง
    • การเปลี่ยนคีย์หลักเป็นงานที่ยาก จึงสำคัญที่จะตั้งค่าให้ถูกต้องตั้งแต่แรก
  • ข้อควรระวัง

    • ผู้เขียนไม่ใช่ผู้เชี่ยวชาญด้าน PostgreSQL และเพียงแบ่งปันสิ่งที่ได้เรียนรู้
    • หากเห็นว่ามีประโยชน์ ก็หวังว่าจะได้รับฟีดแบ็กผ่านคอมเมนต์หรือ Twitter

สรุปโดย GN⁺

  • บทความนี้กล่าวถึงวิธีใช้ UUID เป็นคีย์หลักใน PostgreSQL อย่างมีประสิทธิภาพ
  • แสดงผ่านการทดลองว่าการใช้ UUID v7 อาจช่วยเพิ่มประสิทธิภาพการแทรกข้อมูลได้
  • หากคาดว่าจะมีชุดข้อมูลขนาดใหญ่หรือทราฟฟิกสูง จำเป็นต้องพิจารณาการปรับแต่ง
  • ตัวเลือกอื่นอย่าง TSID ก็เป็นสิ่งที่ควรนำมาพิจารณาเช่นกัน

4 ความคิดเห็น

 
savvykang 2024-07-09

จะหวังให้ใช้การเข้ารหัสแบบ base62 แทนรูปแบบมาตรฐานของ uuid (เลขฐานสิบหก + ไฮเฟน) นี่ถือว่าเกินไปไหม?

 
qurare 2024-07-08

uuidv7 นั้นไร้เทียมทาน
และ uuidv8+ คือ "พระเจ้า"

 
bbulbum 2024-07-08

อุปสรรคที่ใหญ่ที่สุดคือมันไม่เป็นมิตรกับมนุษย์นัก.. สำหรับผม ตอนนี้ก็ยังรู้สึกว่าหลาย ๆ ส่วนยังต้องการเรื่องนี้อยู่ครับ..

 
GN⁺ 2024-07-06
ความคิดเห็นจาก Hacker News
  • แนะนำให้ใช้ bigserial เป็นคีย์หลักที่เป็นมิตรกับ B-tree และพิจารณา UUID ที่เข้ารหัสเป็นสตริงเป็นตัวเลือกสำหรับตัวระบุตำแหน่งเรคคอร์ดภายนอก

    • หากผู้ใช้ที่ไม่ใช่สายเทคนิคต้องอ้างอิง ให้พิจารณาตัวเลือกที่เรียบง่ายกว่าก่อน เช่น ตัวระบุตำแหน่งสไตล์ PNR
    • อย่าผสมประเภท PK ภายในสคีมาของบริการหรือแอปพลิเคชันเดียวกัน
    • หากใช้ UUIDv7 เป็นตัวระบุที่ไม่ซ้ำกัน ให้ใช้กับข้อมูลที่มี timecode แฝงอยู่เท่านั้น
    • อย่าใช้ hashids; ไม่มีคุณภาพระดับการเข้ารหัส และไม่คุ้นเคยสำหรับคนทั่วไป
    • ตอนเข้ารหัส อย่าใช้ base64 หรือชุดอักขระที่มีเครื่องหมายขีดกลางรวมอยู่
  • เวลาวางแผนสคีมาฐานข้อมูล ควรคำนึงถึงหลักการแยกความรับผิดชอบและการปรับให้สอดคล้องกับกลไกของระบบ

  • random ID แบบมี type ของ Stripe ไม่ได้สุ่มจริง

    • มีทั้งเมทาดาทา, timestamp ที่ฝังอยู่, shard และ reference key, ข้อมูลเวอร์ชัน เป็นต้น
    • โดยส่วนตัวชอบตัวระบุตำแหน่งแบบ bigserial+HMAC ที่เข้ารหัสด้วย AES และเข้ารหัสเป็น base58 มากกว่า
  • ใน Postgres, UUID แบบสุ่มไม่ใช่ปัญหาใหญ่

    • UUID (16 ไบต์) มีขนาดใหญ่กว่า serial (4 ไบต์) หรือ bigserial (8 ไบต์) แต่ในระดับทั้งตารางแล้วไม่ใช่ประเด็นใหญ่
  • ก่อนจะกังวลเรื่อง serial vs. random UUID vs. ordered UUID ใน Postgres ยังมีเรื่องอื่นอีกมากที่ควรกังวลก่อน

  • ไม่นานมานี้เลือก ULID เป็น Postgres PK และบทความนี้ช่วยได้มาก: https://brandur.org/nanoglyphs/026-ids

  • เหตุผลที่ชอบ ULID คือเข้ากันได้กับชนิด UUID และมี timestamp ในตัว ดังนั้นถ้าเรียงตาม ID ก็จะเรียงตามลำดับเวลาไปด้วย

  • ถ้ารวม int64 ไว้ในการเปรียบเทียบด้วยก็น่าจะดี เพื่อจะได้เห็น overhead ของ UUID เทียบกับแนวทางดั้งเดิม

  • ประสิทธิภาพการ insert เป็นวิธีที่ไม่ดีในการประเมินประสิทธิภาพ

    • ประสิทธิภาพของ B-Tree อาจดีกว่าตอน insert แต่ก็สงสัยว่าในธุรกรรมขนาดใหญ่จะเป็นอย่างไร
  • ใน SQLite เหตุผลที่นิยม UUID4 คือมีโอกาสชนกันใน page cache ระหว่างการล็อกทรานแซกชันน้อยกว่า

    • อาจใช้แนวคิดเดียวกันได้กับระบบ Postgres เช่นกัน
  • ชอบคีย์หลักแบบจำนวนเต็มเพิ่มอัตโนมัติ

    • เข้าใจง่ายและจัดเรียงได้ง่าย
    • ในโปรเจ็กต์แบบแบตช์ขนาดใหญ่ สามารถเก็บคีย์หลักล่าสุดไว้แล้วดึงทุกอย่างที่มากกว่านั้นได้
  • benchmark เวลา insert ของ UUIDv7 รวมเวลาในการสร้าง UUID ไว้ด้วย

    • อยากเห็นการแยกดูเฉพาะต้นทุนของการอัปเดตดัชนี
  • มีโอกาสน้อยที่ PostgreSQL 17 จะมีการรองรับ UUIDv7

    • ในงานล่าสุด committer ถูกถอดออกไปแล้ว และเวอร์ชัน 17 ก็เข้าสู่สถานะ feature freeze แล้ว
  • เริ่มใช้ python-ulid แล้ว และคิดว่า ULID ดีกว่า UUID

  • ลิงก์มาตรฐาน UUID v7 เก่าแล้ว ดังนั้นควรอ้างอิง RFC 9562 แทน: https://datatracker.ietf.org/doc/html/rfc9562