เกี่ยวกับการใช้ PostgreSQL กับ UUID เป็นคีย์หลัก
(maciejwalkowiak.com)- 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 ไบต์
- PostgreSQL มีชนิดข้อมูล
-
ผลการทดลอง
- สร้างตารางสองตารางเพื่อเปรียบเทียบ: ตารางหนึ่งใช้ชนิด
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 ใน Java จำเป็นต้องใช้ไลบรารี
ผลกระทบของ 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 ความคิดเห็น
จะหวังให้ใช้การเข้ารหัสแบบ base62 แทนรูปแบบมาตรฐานของ uuid (เลขฐานสิบหก + ไฮเฟน) นี่ถือว่าเกินไปไหม?
uuidv7 นั้นไร้เทียมทาน
และ uuidv8+ คือ "พระเจ้า"
อุปสรรคที่ใหญ่ที่สุดคือมันไม่เป็นมิตรกับมนุษย์นัก.. สำหรับผม ตอนนี้ก็ยังรู้สึกว่าหลาย ๆ ส่วนยังต้องการเรื่องนี้อยู่ครับ..
ความคิดเห็นจาก Hacker News
แนะนำให้ใช้
bigserialเป็นคีย์หลักที่เป็นมิตรกับ B-tree และพิจารณา UUID ที่เข้ารหัสเป็นสตริงเป็นตัวเลือกสำหรับตัวระบุตำแหน่งเรคคอร์ดภายนอกเวลาวางแผนสคีมาฐานข้อมูล ควรคำนึงถึงหลักการแยกความรับผิดชอบและการปรับให้สอดคล้องกับกลไกของระบบ
random ID แบบมี type ของ Stripe ไม่ได้สุ่มจริง
bigserial+HMACที่เข้ารหัสด้วย AES และเข้ารหัสเป็น base58 มากกว่าใน Postgres, UUID แบบสุ่มไม่ใช่ปัญหาใหญ่
serial(4 ไบต์) หรือbigserial(8 ไบต์) แต่ในระดับทั้งตารางแล้วไม่ใช่ประเด็นใหญ่ก่อนจะกังวลเรื่อง
serialvs. random UUID vs. ordered UUID ใน Postgres ยังมีเรื่องอื่นอีกมากที่ควรกังวลก่อนไม่นานมานี้เลือก ULID เป็น Postgres PK และบทความนี้ช่วยได้มาก: https://brandur.org/nanoglyphs/026-ids
เหตุผลที่ชอบ ULID คือเข้ากันได้กับชนิด UUID และมี timestamp ในตัว ดังนั้นถ้าเรียงตาม ID ก็จะเรียงตามลำดับเวลาไปด้วย
ถ้ารวม
int64ไว้ในการเปรียบเทียบด้วยก็น่าจะดี เพื่อจะได้เห็น overhead ของ UUID เทียบกับแนวทางดั้งเดิมประสิทธิภาพการ insert เป็นวิธีที่ไม่ดีในการประเมินประสิทธิภาพ
ใน SQLite เหตุผลที่นิยม UUID4 คือมีโอกาสชนกันใน page cache ระหว่างการล็อกทรานแซกชันน้อยกว่า
ชอบคีย์หลักแบบจำนวนเต็มเพิ่มอัตโนมัติ
benchmark เวลา insert ของ UUIDv7 รวมเวลาในการสร้าง UUID ไว้ด้วย
มีโอกาสน้อยที่ PostgreSQL 17 จะมีการรองรับ UUIDv7
เริ่มใช้ python-ulid แล้ว และคิดว่า ULID ดีกว่า UUID
ลิงก์มาตรฐาน UUID v7 เก่าแล้ว ดังนั้นควรอ้างอิง RFC 9562 แทน: https://datatracker.ietf.org/doc/html/rfc9562