หมายเหตุของฉันเกี่ยวกับการออกแบบสคีมา Postgres ของ GitLab (2022)
(shekhargulati.com)หมายเหตุของฉันเกี่ยวกับการออกแบบสคีมา Postgres ของ GitLab (2022)
- การศึกษาโครงสร้างสคีมา Postgres ของ GitLab เพื่อเปรียบเทียบกับสคีมาที่ฉันกำลังออกแบบ และเรียนรู้แนวปฏิบัติที่ดีจากการออกแบบสคีมาของ GitLab
- GitLab เป็นแพลตฟอร์ม DevOps แบบโอเพ่นซอร์ส ซึ่งเป็นทางเลือกของ GitHub และสามารถโฮสต์แบบ self-hosted ได้
การใช้ประเภทคีย์หลักที่เหมาะสม
- เมื่อฐานข้อมูลยังมีขนาดเล็กอาจไม่เห็นผลชัดเจน แต่เมื่อมีการเติบโต คีย์หลักย่อมมีผลต่อพื้นที่จัดเก็บ ความเร็วในการเขียน และความเร็วในการอ่าน
- ในบรรดาตารางทั้ง 573 ตาราง GitLab ใช้ประเภทคีย์หลัก
bigserial380 ตาราง และใช้serial4อีก 170 ตาราง ที่เหลือ 23 ตารางใช้คีย์หลักเชิงประกอบ
การใช้ ID ภายในและภายนอก
- การไม่เปิดเผยคีย์หลักไปยังโลกภายนอกถือเป็นแนวปฏิบัติที่ดี
- GitLab ใช้ทั้ง
idภายในและiidภายนอกในตารางอย่างเช่นissues,ci_pipelines,deployments,epics
การใช้ชนิดข้อมูล text พร้อมข้อจำกัดความยาว
- สคีมา GitLab ใช้ทั้ง
character varying(n)และtextแต่ใช้textมากกว่า - ชนิดข้อมูล
textไม่มีข้อจำกัดความยาวในตัวเอง และใช้CHECKเพื่อกำหนดข้อจำกัดความยาว
กฎการตั้งชื่อ
- ทุกตารางใช้รูปพหูพจน์ และใช้คำนำหน้าชื่อโมดูลเพื่อจัดการ namespace
- ชื่อตารางและชื่อคอลัมน์ยึดตามรูปแบบ snake_case
การใช้โซนเวลาสำหรับ timestamp
- GitLab ใช้ทั้ง
timestamp with timezoneและtimestamp without timezone - งานระบบใช้
timestamp without timezoneและงานที่ผู้ใช้ดำเนินการใช้timestamp with timezone
ข้อจำกัดคีย์ต่างประเทศ
- GitLab ใช้ข้อจำกัดคีย์ต่างประเทศในตารางส่วนใหญ่ แต่มีบางตารางที่ไม่ใช้ เช่น
audit_events,abuse_reports,web_hooks_logs,spam_logs
การแบ่งพาร์ติชันของตารางขนาดใหญ่
- GitLab แบ่งพาร์ติชันตารางที่อาจมีขนาดเติบโตขึ้นเพื่อเพิ่มประสิทธิภาพการ query
รองรับการค้นหาแบบ LIKE ด้วย Trigrams และ gin_trgm_ops
- GitLab ใช้ดัชนี GIN (Generalized Inverted Index) เพื่อให้การค้นหามีประสิทธิภาพมากขึ้น
การใช้ jsonb
- สคีมา GitLab ใช้ชนิดข้อมูล
jsonbในตารางหลายตาราง
เคล็ดลับเพิ่มเติม
- ในตารางที่สามารถแก้ไขได้ จะใช้ฟิลด์สำหรับตรวจสอบการอัปเดต เช่น
updated_atแต่ในตาราง log ที่ไม่สามารถแก้ไขได้จะไม่ใช้ - Enums ถูกเก็บในรูปแบบ
smallintแทนcharacter varyingเพื่อประหยัดพื้นที่
GN⁺ ความเห็น:
- การออกแบบสคีมา Postgres ของ GitLab ช่วยให้มีมุมมองเชิงลึกด้านการออกแบบฐานข้อมูล โดยเฉพาะบทเรียนสำคัญสำหรับการปรับแต่งสคีมาเพื่อรองรับระบบขนาดใหญ่
- เนื่องจาก GitLab เป็นโอเพ่นซอร์ส การตัดสินใจออกแบบสคีมาเหล่านี้จึงเป็นตัวอย่างที่นักพัฒนาคนอื่นๆ สามารถนำไปประยุกต์ใช้ในโปรเจกต์ของตัวเองได้
- สิ่งที่ควรเรียนรู้จากสคีมา GitLab คือ ต้องพิจารณาปัจจัยสำคัญที่มีผลต่อประสิทธิภาพและการดูแลรักษาฐานข้อมูลอย่างรอบคอบ ไม่ว่าจะเป็นการเลือกชนิดข้อมูล กลยุทธ์การทำ indexing การแบ่งพาร์ติชัน และการใช้งานข้อจำกัดคีย์ต่างประเทศ
1 ความคิดเห็น
ความคิดเห็นใน Hacker News
สงสัยว่าทำไมแนวปฏิบัติที่ว่าไม่ควรเปิดเผยคีย์หลักออกสู่ภายนอกถึงจำเป็นนัก หากคำขอต้องผ่านการยืนยันตัวตนอยู่แล้ว ก็ไม่แน่ใจว่าการป้องกันไม่ให้เดา ID ได้มีคุณค่าอะไร
ถ้าแค่เดา ID ได้ก็สามารถทำอะไรที่เป็นประโยชน์ได้โดยไม่มีการยืนยันตัวตนหรือการกำหนดสิทธิ์ แปลว่ามีส่วนอื่นที่พังหนักอยู่แล้ว และควรไปโฟกัสตรงนั้นมากกว่าจะเพิ่มความซับซ้อนที่ไม่จำเป็นให้สคีมา สิ่งที่อาจมีคุณค่าบ้างคือการซ่อน ข้อมูลเชิงแข่งขัน เพื่อไม่ให้คู่แข่งประเมินจำนวนลูกค้าได้ แต่ก็ไม่น่าเชื่อว่า GitLab จะใส่ใจเรื่องนี้มากนัก การตัดสินใจใช้
id + iidของ GitLab ดูมีแนวโน้มว่าเกิดจากความต้องการด้าน ประสิทธิภาพของคิวรี มากกว่าการป้องกันการเดา ID ภายในถ้าเปิดเผย UUID ให้ผู้ใช้ แม้จะมีช่องโหว่เดียวกัน ผู้โจมตีก็ยังต้องเดา UUID ให้ถูก ซึ่งยากกว่ามาก และอาจต้องมีแหล่งข้อมูลรองด้วย แม้จะมีข้อมูลรั่ว ก็ยังมีเวลารับมือและประเมินปริมาณข้อมูลที่รั่วได้ ในทางกลับกัน ถ้าเป็น ID แบบเรียงลำดับ ปัญหาอาจลุกลามเป็นการรั่วไหลทั้งหมดได้ทันที และกลายเป็นเหตุขนาดใหญ่ที่ต้องรายงานต่อหน่วยงานกำกับดูแลข้อมูลส่วนบุคคล นี่ควรเป็นแค่ การป้องกันหลายชั้น ที่ไม่จำเป็นต้องได้ใช้ แต่ในความเป็นจริงก็มีซอฟต์แวร์แย่ ๆ ที่ถูกเจาะด้วยวิธีแบบนี้อยู่จริง
หากเปิดเผยคีย์หลัก
idของตาราง issue เวลาสร้าง issue ในโปรเจ็กต์ มันจะไม่ได้เริ่มจาก 1 จึงเดาได้ง่ายว่า GitLab ทั้งระบบมี issue อยู่ประมาณเท่าไรแน่นอนว่าถ้าแค่เดา ID ก็ทำอะไรได้โดยไม่มีการยืนยันตัวตนหรือการกำหนดสิทธิ์ นั่นคือปัญหาใหญ่จริง แต่ถ้าถึงจุดนั้นแล้วไม่มีชั้นอื่นเหลือเลย เกมก็จบแล้ว บั๊กไม่ได้แจ้งล่วงหน้า โดยเฉพาะบั๊กที่แนบเนียนยิ่งไม่แจ้ง และเมื่อบั๊กแบบนั้นโผล่มา คุณจะรู้สึกขอบคุณที่อย่างน้อยทำให้ ID เดาไม่ได้ จนช่วยเลี่ยงสถานการณ์ที่บัญชีผู้ใช้ทั้งหมดในระบบถูกเข้าถึงได้ง่าย
แน่นอนว่าควรโฟกัสที่การทำให้ระบบควบคุมการเข้าถึงถูกต้องก่อน แต่ ID ที่เดาไม่ได้อาจเป็นตัวแบ่งระหว่างหายนะร้ายแรงกับเหตุเฉียดฉิวได้ ถ้า UUID ไม่เหมาะ ก็ยังใช้ ID ฐานข้อมูลแบบเพิ่มอัตโนมัติแล้วเข้ารหัสมันได้ และถ้ามีชั้นซอฟต์แวร์ที่เหมาะสม ID ที่เข้ารหัสก็แทบจะทำงานได้อัตโนมัติ
จากคลังสาธารณะ 128 ล้านคลัง ส่วนใหญ่น่าจะเป็น fork ของคลังอื่น และมีไว้แค่เพื่อสร้าง pull request ไปยังคลังหลัก จึงไม่น่าจะมี issue เว้นแต่จะเผลอทำผิดพลาด
โปรเจ็กต์เล็ก ๆ แบบของเล่นหรือโปรเจ็กต์ที่ถูกทิ้งอย่างรวดเร็ว ก็น่าจะไม่มี issue หรือมีน้อยมาก แน่นอนว่ามีโปรเจ็กต์ที่มี issue หลักร้อยหรือหลักพันอยู่ แต่ค่าเฉลี่ยของคลังทั้งหมด 128 ล้านคลังน่าจะค่อนข้างต่ำ จึงมีแนวโน้มว่าจะยังอยู่ต่ำกว่า ขีดจำกัด 2 พันล้าน ถึงอย่างนั้นก็เห็นด้วยว่าการใช้ชนิดข้อมูล 4 ไบต์ หรือพูดให้แม่นคือ 31 บิต ในตารางนั้น เป็นระเบิดเวลาสำหรับบางองค์กรรวมถึง github.com
https://play.clickhouse.com/play?user=play#U0VMRUNUIHVuaXEoc...
ความต้องการพื้นฐานอย่าง
PRIMARY KEY(repo_id, issue_id)กลับซับซ้อนเกินจำเป็นใน ActiveRecord และต้องอ้อมด้วยการใช้ทั้งคีย์ที่ไม่ซ้ำและคีย์หลักแยกต่างหากเพื่อให้เข้ากับ ActiveRecord ที่ต้องการคอลัมน์คีย์หลักเดี่ยว UUID เป็นคีย์หลักก็ดูเหมือนทางอ้อมอีกแบบ แต่ก็ยังต้องมีข้อจำกัดความไม่ซ้ำของคู่(repo_id, issue_id)อยู่ดี ทำให้ขนาดฐานข้อมูลและโอเวอร์เฮดเพิ่มขึ้น มองให้กว้างขึ้น โครงสร้าง MVC แบบก้อนเดียว ของ Ruby on Rails ที่อิงกับชั้น model, controller และ view ชุดเดียว ก็สร้างปัญหาเรื่องการขยายตัวและการบำรุงรักษาเมื่อแอปพลิเคชันใหญ่ขึ้น และผมคิดว่า MVC เหมาะกับโครงสร้างแบบโมดูลาร์หรือแบบคอมโพเนนต์มากกว่าผลิตภัณฑ์ที่มีทั้งแบบ self-hosted และคลาวด์ มักชอบใช้ฐานข้อมูลแยกตามลูกค้า เพราะมันทำให้ส่วนของโค้ดเบสที่ใช้ร่วมกันง่ายกว่ามาก เนื่องจากสามารถใช้คิวรีเดียวกันได้ไม่ว่าจะเป็นการโฮสต์แบบไหน ถ้าเป็นฐานข้อมูลแยกตามลูกค้า ก็แทบไม่มีทางเข้าใกล้ขีดจำกัดการใช้งานแบบนั้น และถ้าเข้าใกล้จริง การโฮสต์เองก็น่าจะเหมาะกว่า
intไปเป็นbigintทำได้ อาจต้องเตรียมการและมีโค้ดปรับแต่งอยู่บ้าง แต่ทำแบบ ไม่ต้องหยุดระบบ ได้โดยทั่วไปผมจัดการมิเกรชันขนาดใหญ่ตามขั้นตอนนี้ และปรับใช้เล็กน้อย: http://zemanta.github.io/2021/08/25/column-migration-from-in...
foreign key, index และ constraint ต่าง ๆ ทำให้กระบวนการยากขึ้น แต่ไม่ถึงกับเป็นไปไม่ได้ ในกรณีของผม การมิเกรตข้อมูลใช้เวลาหลายชั่วโมง แต่ไม่จำเป็นต้องเร็ว GitLab เองก็มีเครื่องมือสำหรับรันงานหลังอัปเกรด เพื่อให้ทำงานได้ไม่ว่าจะอยู่ช่วงไหนของการอัปเกรดเวอร์ชัน
เหตุผลเรื่องขนาดจัดเก็บของคอลัมน์ UUID ฟังไม่ค่อยน่าเชื่อถือนัก ถ้าในตารางมีคอลัมน์อื่นอีก 5 คอลัมน์ ความต่างระหว่าง 128 บิต กับ 64 บิต ก็ไม่ได้ใหญ่ขนาดนั้น
ประเด็นที่น่ากังวลกว่าคือประสิทธิภาพ UUIDv4 รองรับกันอย่างแพร่หลาย แต่เป็นค่าที่สุ่มทั้งหมดจึงไม่เหมาะกับประสิทธิภาพของดัชนีนัก UUIDv7[0] ใกล้เคียงกับ Snowflake[1] มากกว่า จึงมี locality ตามเวลา แต่มี implementation ที่ยังไม่แพร่หลายเท่าไร อีกแนวทางหนึ่งคือใช้
bigserialแล้วเข้ารหัสคีย์: https://github.com/abevoelker/gfc64แต่วิธีนี้มีปัญหาคือ 1) หมุนเวียนค่า secret ไม่ได้ และ 2) ถ้าหลุดออกไปครั้งเดียว ใครก็ตามก็สามารถประเมินขนาดของตารางแบบคร่าว ๆ ได้ด้วย Fermi estimation การแยก public ID กับ internal ID ออกจากกันก็ยุ่งยาก และถ้า public ID เป็น UUIDv4 ก็ยังต้องแลกกับประสิทธิภาพอีก ผมคิดว่า UUIDv7 เป็นทางออกที่ตอบโจทย์ได้มากที่สุด
[0]: https://uuid7.com/
[1]: https://en.wikipedia.org/wiki/Snowflake_ID
idนั้นถูกใช้เป็น foreign key และขนาดของดัชนีที่จำเป็นสำหรับคอลัมน์ foreign key เหล่านั้นด้วยลองนึกถึงค่าอย่าง user ID ที่อาจถูกอ้างอิงด้วย foreign key เป็นหลักสิบหรือหลักร้อยแห่งทั่วทั้งฐานข้อมูล
ประสิทธิภาพฐานข้อมูลมีอยู่สามระดับ: 1) ทั้งดัชนีและข้อมูลอยู่ในหน่วยความจำ 2) ดัชนีอยู่ในหน่วยความจำแต่ข้อมูลไม่อยู่ 3) ทั้งดัชนีและข้อมูลไม่อยู่ในหน่วยความจำ แบบที่ 1 ดีที่สุด แต่ถ้าไม่ใช่ ก็ต้องพยายามรักษาแบบที่ 2 ไว้ให้สุดทาง การที่ ขนาดดัชนีเพิ่มเป็นสองเท่า ทำให้เรื่องนี้ยากขึ้น
typedef void*ก็ได้ ขนาดของมันส่งผลต่อประสิทธิภาพโดยรวม ทั้งการใช้หน่วยความจำ/ดิสก์ คอขวดด้าน throughput และเวลา CPU ที่ใช้เปรียบเทียบคีย์ใน inner loop ชั้นลึกที่สุดของการ join และการ lookupตอนที่ CPU x86-64 ออกรุ่นใหม่ ๆ ผลกระทบด้านประสิทธิภาพจากการย้ายไปใช้ pointer 64 บิตนั้นสูงมากจนเกิด x32/ilp32 ขึ้นมา และนั่นก็เป็นเหตุผลที่ .NET ยังตั้งค่าเริ่มต้นเป็น “prefer 32-bit” มาจนถึงตอนนี้ การใช้ UUID 128 บิต เป็น primary key ของฐานข้อมูลถือเป็นความผิดพลาดที่เลวร้ายมาก
ตัวอย่างเช่น คุณอาจอัปโหลดวิดีโอไว้ล่วงหน้าหนึ่งเดือนก่อนเผยแพร่ แต่ไม่ต้องการให้ผู้ชมรู้ข้อเท็จจริงนั้น
และยังสามารถเข้ารหัสค่าเวลาเอาไปแสดงใน URL, อีเมล ฯลฯ ได้ด้วย: https://wiki.postgresql.org/wiki/Pseudo_encrypt
วิธีนี้ช่วยรักษาข้อดีของดัชนีแบบเรียงลำดับไว้ได้มาก พร้อมกับยังเปลี่ยนคีย์ได้ด้วย แต่ถ้าเปลี่ยนคีย์ บุ๊กมาร์กจะเสีย ลิงก์ที่ส่งไปในอีเมลก่อนหน้าจะใช้ไม่ได้ และในทางปฏิบัติก็มีผลไม่ต่างจากการเปลี่ยนชื่อทุกอย่าง
เป็นการจับผิดเล็กน้อย แต่ผมอยากพูดถึงส่วน
textเทียบกับvarcharผู้เขียนใช้คำอธิบายยืดยาวเพื่อพยายามพิสูจน์ความต่างด้านประสิทธิภาพที่ไม่มีอยู่จริง ก่อนจะสรุปว่า “สองชนิดนี้มีความต่างด้านประสิทธิภาพไม่มาก” ประเด็นนี้มีข้อสรุปมานานแล้ว และไม่ใช่แค่ “ไม่มาก” แต่คือ “ไม่มี” เลย PostgreSQL wiki[1] ระบุชัดว่าถ้าไม่มีเหตุผลที่ดีมากก็ควรใช้
textส่วนเอกสาร[2] ก็ระบุว่า “สำหรับหลายวัตถุประสงค์character varyingทำงานคล้ายโดเมนบนtext” และในกล่อง Tip สีเขียวก็เขียนไว้ว่า “ไม่มี ความต่างด้านประสิทธิภาพ ระหว่างทั้งสามชนิดนี้” ดังนั้นที่ GitLab ใช้textเป็นส่วนใหญ่ จึงดูเหมือนหมายความว่าพวกเขาอ่านเอกสารและออกแบบสคีมาให้เหมาะกับ PostgreSQL แทนที่จะทำสคีมาแบบ “พกพาได้” ที่ครึ่ง ๆ กลาง ๆ[1] https://wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use...
[2] https://www.postgresql.org/docs/current/datatype-character.h...
การเปลี่ยน
varchar(300)เป็นvarchar(200)ต้องเขียนทุกแถวใหม่ทั้งหมด แต่การอัปเดตข้อจำกัดของคอลัมน์textแทบจะฟรีโดยพื้นฐาน และต้องการเพียงการสแกนทั้งตารางเพื่อตรวจสอบว่าค่าที่มีอยู่เดิมผ่านข้อจำกัดใหม่หรือไม่ ในบทความเองก็ระบุว่าการใช้ชนิดtextร่วมกับข้อจำกัดCHECKทำให้การพัฒนาสคีมาง่ายกว่าการใช้character varyingหรือvarchar(n)เมื่อมีการตรวจสอบความยาวคำกล่าวว่า foreign key มีต้นทุนสูง ถูกพูดซ้ำบ่อยมาก แต่ไม่ค่อยมี benchmark รองรับนัก
แม้จะมีวิธีทำผิดได้หลายแบบ แต่ท้ายที่สุดแล้ว ที่ไหนสักแห่งในสแตกก็มีการบังคับใช้ integrity อยู่ดี ถ้าจะใช้ฐานข้อมูลให้เป็นประโยชน์แทนการไปเขียนเรื่องนี้ใหม่เอง ก็ต้องอาศัยความรู้และการทดลอง และโดยมากมันช่วยป้องกันอุบัติเหตุใหญ่ได้
สงสัยว่ามีใครเคยสรุปหรือสังเกต ความต่างด้านประสิทธิภาพ ระหว่าง GitLab กับ GitHub ไหม
ทั้งคู่เป็นแอปพลิเคชันที่สร้างบน Rails แต่โดยรวมแล้วรู้สึกว่าเวลาโหลดหน้าเว็บของ GitLab แย่กว่า GitHub มาก
Chrome กับ GitHub จะใช้ทุกกลเม็ดแม้มันจะทำร้ายผู้ใช้ก็ตาม ตัวอย่างเช่น เคยเปิด merge diff ใน GitHub ของบริษัทแล้วค้นหาด้วย
Ctrl Fไม่เจอผลลัพธ์ เลยกดข้ามไปเรื่อย ๆ และคุ้ยประวัติ Git ด้วยมือ จนไปถึง diff ลำดับที่ 100 ถึงได้รู้ว่าไฟล์ที่สำคัญที่สุดถูกซ่อนไว้อยู่ลึกมาก ทั้งหมดนี้ก็น่าจะเพราะมีใครสักคนทำให้ตัวชี้วัดการโหลดหน้าเพจดูดีขึ้นแล้วได้เลื่อนตำแหน่งง่ายกว่ามีโพสต์จากอดีตพนักงานที่อาจช่วยให้เข้าใจวัฒนธรรมของ GitLab และการมองข้ามเรื่องประสิทธิภาพได้ที่นี่: https://news.ycombinator.com/item?id=39303323
ฉันเองไม่ได้ใช้ GitLab มากพอจะรู้สึกถึงปัญหาด้านประสิทธิภาพ แต่คิดว่าโพสต์นี้น่าจะช่วยได้
สงสัยมาตลอดว่าตัว I ที่เพิ่มมาในตัวแปร CI
CI_PIPELINE_IIDและCI_MERGE_REQUEST_IIDหมายถึงอะไรเดาว่าน่าจะเป็นตัวเลือกที่เกี่ยวกับฐานข้อมูล และบทความนี้ก็ยืนยันให้
พอเห็นว่า “1 quintillion เท่ากับ 1,000,000,000 billion” ก็รู้สึกว่าแปลกดีที่ปกติเรามักเลือกกันแค่ระหว่าง
int32กับint64น่าจะมีชนิดจำนวนเต็ม 5 ไบต์ที่รองรับ cardinality ได้ราว 1 ล้านล้านค่าการไม่ใช้ ID แบบ auto-increment อาจสมเหตุสมผล แต่ยังไม่ค่อยเข้าใจข้อดีของการมี ID สองตัว สำหรับใช้งานภายในและภายนอก
มันเพิ่มจำนวนคอลัมน์และดัชนี ต้องคอย lookup ก่อนเสมอ และก็นึกสถานการณ์ด้านความปลอดภัยไม่ออกว่าจะเปลี่ยนคีย์ภายในแต่ไม่เปลี่ยนคีย์ภายนอกไปทำไม หรือว่าฉันพลาดอะไรไป?
มีคนบอกว่าถ้าใช้ PostgreSQL native UUID v4 แทน
bigserialขนาดตารางจะใหญ่ขึ้น 25% และอัตราการแทรกจะเหลือเพียง 25% ของbigserialเลยสงสัยว่าทำไม UUIDv4 ถึงแย่ขนาดนั้นUUID ก็เป็นแค่ ตัวเลข 128 บิต ไม่ใช่หรือ? ไม่รู้ว่าค่าใช้จ่ายในการสร้างมันแพงมากหรือจริง ๆ แล้วเกิดอะไรขึ้น
เพราะแบบนี้การสร้างดัชนีบนคอลัมน์ UUIDv4 จึงช้าลง และนี่ก็เป็นแรงผลักดันให้มีการพัฒนา UUIDv6 กับ UUIDv7
ปัญหาใหญ่กว่าคืออัตราการแทรก สำหรับ UUID อัตราการแทรกถูกจำกัดโดยปริมาณ RAM ที่มีใช้งานได้ แต่จำนวนเต็มแบบ auto-increment ไม่เป็นแบบนั้น จำนวนเต็มมีความสัมพันธ์กับเวลา ส่วน UUID4 เป็นแบบสุ่ม ดังนั้นเมื่อสเกลใหญ่ขึ้น คุณลักษณะด้านประสิทธิภาพจะเปลี่ยนไปโดยพื้นฐาน ในตารางเล็ก ๆ ค่าปรับด้านการแทรกแทบมองข้ามได้ แต่เมื่อขนาดดัชนี B-tree แตะขีดจำกัดหน่วยความจำ PostgreSQL จะไม่สามารถเก็บ UUID B-tree ทั้งหมดไว้ในหน่วยความจำได้อีกต่อไป และต้องพึ่งการสลับหน้าเพจบนดิสก์ ส่วนจำนวนเต็มแบบ auto-increment แถวที่อยู่ใกล้กันตามเวลาจะใช้หน้าเพจดัชนีเดียวกัน จึงไม่ต้องแตะดิสก์ภายใต้ภาระงานเดียวกัน เมื่อถึงสเกลนั้น ความต่างจะไม่ใช่การช้าลงคงที่ 25% แต่เป็น หน้าผาด้านประสิทธิภาพ 25 เท่า และนอกจากทำ schema migration ก็แทบไม่มีทางออกนอกจากซื้อ RAM เพิ่ม
UUID ทำให้เกิดการเขียนแบบไม่เรียงลำดับจำนวนมาก จนนำไปสู่ page bloat
locality ของการแทรกแบบ batch ก็ส่งผลเสียต่อการอ่านในภายหลังด้วย เพราะต้องไปตามหาเรคคอร์ดที่เกี่ยวข้องแบบสุ่ม สุดท้ายจึงต้องจ่ายต้นทุนทั้งตอนแทรกและตอน select ภายหลัง