1 คะแนน โดย GN⁺ 2024-02-18 | 1 ความคิดเห็น | แชร์ทาง WhatsApp

หมายเหตุของฉันเกี่ยวกับการออกแบบสคีมา Postgres ของ GitLab (2022)

  • การศึกษาโครงสร้างสคีมา Postgres ของ GitLab เพื่อเปรียบเทียบกับสคีมาที่ฉันกำลังออกแบบ และเรียนรู้แนวปฏิบัติที่ดีจากการออกแบบสคีมาของ GitLab
  • GitLab เป็นแพลตฟอร์ม DevOps แบบโอเพ่นซอร์ส ซึ่งเป็นทางเลือกของ GitHub และสามารถโฮสต์แบบ self-hosted ได้

การใช้ประเภทคีย์หลักที่เหมาะสม

  • เมื่อฐานข้อมูลยังมีขนาดเล็กอาจไม่เห็นผลชัดเจน แต่เมื่อมีการเติบโต คีย์หลักย่อมมีผลต่อพื้นที่จัดเก็บ ความเร็วในการเขียน และความเร็วในการอ่าน
  • ในบรรดาตารางทั้ง 573 ตาราง GitLab ใช้ประเภทคีย์หลัก bigserial 380 ตาราง และใช้ 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 ความคิดเห็น

 
GN⁺ 2024-02-18
ความคิดเห็นใน Hacker News
  • สงสัยว่าทำไมแนวปฏิบัติที่ว่าไม่ควรเปิดเผยคีย์หลักออกสู่ภายนอกถึงจำเป็นนัก หากคำขอต้องผ่านการยืนยันตัวตนอยู่แล้ว ก็ไม่แน่ใจว่าการป้องกันไม่ให้เดา ID ได้มีคุณค่าอะไร
    ถ้าแค่เดา ID ได้ก็สามารถทำอะไรที่เป็นประโยชน์ได้โดยไม่มีการยืนยันตัวตนหรือการกำหนดสิทธิ์ แปลว่ามีส่วนอื่นที่พังหนักอยู่แล้ว และควรไปโฟกัสตรงนั้นมากกว่าจะเพิ่มความซับซ้อนที่ไม่จำเป็นให้สคีมา สิ่งที่อาจมีคุณค่าบ้างคือการซ่อน ข้อมูลเชิงแข่งขัน เพื่อไม่ให้คู่แข่งประเมินจำนวนลูกค้าได้ แต่ก็ไม่น่าเชื่อว่า GitLab จะใส่ใจเรื่องนี้มากนัก การตัดสินใจใช้ id + iid ของ GitLab ดูมีแนวโน้มว่าเกิดจากความต้องการด้าน ประสิทธิภาพของคิวรี มากกว่าการป้องกันการเดา ID ภายใน

    • ก็จริง แต่การที่ สามารถเดา ID ได้ อาจทำให้ช่องโหว่ด้านความปลอดภัยกลายเป็นเรื่องเลวร้ายมาก หรือแย่ลงกว่าเดิมมาก
      ถ้าเปิดเผย UUID ให้ผู้ใช้ แม้จะมีช่องโหว่เดียวกัน ผู้โจมตีก็ยังต้องเดา UUID ให้ถูก ซึ่งยากกว่ามาก และอาจต้องมีแหล่งข้อมูลรองด้วย แม้จะมีข้อมูลรั่ว ก็ยังมีเวลารับมือและประเมินปริมาณข้อมูลที่รั่วได้ ในทางกลับกัน ถ้าเป็น ID แบบเรียงลำดับ ปัญหาอาจลุกลามเป็นการรั่วไหลทั้งหมดได้ทันที และกลายเป็นเหตุขนาดใหญ่ที่ต้องรายงานต่อหน่วยงานกำกับดูแลข้อมูลส่วนบุคคล นี่ควรเป็นแค่ การป้องกันหลายชั้น ที่ไม่จำเป็นต้องได้ใช้ แต่ในความเป็นจริงก็มีซอฟต์แวร์แย่ ๆ ที่ถูกเจาะด้วยวิธีแบบนี้อยู่จริง
    • อย่างที่บทความบอก เรื่องนี้ใกล้เคียงกับ ข้อมูลเชิงแข่งขัน มากกว่าความปลอดภัย ID เพิ่มอัตโนมัติแบบง่าย ๆ เผยให้เห็นจำนวนระเบียนทั้งหมดหรืออัตราการเพิ่มขึ้นของตาราง
      หากเปิดเผยคีย์หลัก id ของตาราง issue เวลาสร้าง issue ในโปรเจ็กต์ มันจะไม่ได้เริ่มจาก 1 จึงเดาได้ง่ายว่า GitLab ทั้งระบบมี issue อยู่ประมาณเท่าไร
    • คำว่า security theater ถูกใช้พร่ำเพรื่อเกินไป ความปลอดภัยสามารถมีหลายชั้นได้ และก็ควรเป็นแบบนั้น ต่อให้ชั้นหนึ่งอย่างการยืนยันตัวตนพัง ก็ไม่ควรทำให้เข้าถึงทุกอย่างที่เหลือได้ง่าย ๆ
      แน่นอนว่าถ้าแค่เดา ID ก็ทำอะไรได้โดยไม่มีการยืนยันตัวตนหรือการกำหนดสิทธิ์ นั่นคือปัญหาใหญ่จริง แต่ถ้าถึงจุดนั้นแล้วไม่มีชั้นอื่นเหลือเลย เกมก็จบแล้ว บั๊กไม่ได้แจ้งล่วงหน้า โดยเฉพาะบั๊กที่แนบเนียนยิ่งไม่แจ้ง และเมื่อบั๊กแบบนั้นโผล่มา คุณจะรู้สึกขอบคุณที่อย่างน้อยทำให้ ID เดาไม่ได้ จนช่วยเลี่ยงสถานการณ์ที่บัญชีผู้ใช้ทั้งหมดในระบบถูกเข้าถึงได้ง่าย
    • บั๊กเกิดขึ้นได้แม้แต่ในระบบควบคุมการเข้าถึง ID ที่เดาไม่ได้ ทำให้การนำบั๊กบางส่วนไปใช้ประโยชน์ทำได้ยากขึ้นมาก
      แน่นอนว่าควรโฟกัสที่การทำให้ระบบควบคุมการเข้าถึงถูกต้องก่อน แต่ ID ที่เดาไม่ได้อาจเป็นตัวแบ่งระหว่างหายนะร้ายแรงกับเหตุเฉียดฉิวได้ ถ้า UUID ไม่เหมาะ ก็ยังใช้ ID ฐานข้อมูลแบบเพิ่มอัตโนมัติแล้วเข้ารหัสมันได้ และถ้ามีชั้นซอฟต์แวร์ที่เหมาะสม ID ที่เข้ารหัสก็แทบจะทำงานได้อัตโนมัติ
    • เป็นความต่างด้านคำศัพท์เล็กน้อย แต่ดูเหมือนว่าสิ่งนี้ควรเรียกว่า ข้อมูลเชิงแข่งขัน มากกว่า “business intelligence” ซึ่งมักหมายถึงการใช้ข้อมูลภายในบริษัท https://en.wikipedia.org/wiki/Competitive_intelligence
  • จากคลังสาธารณะ 128 ล้านคลัง ส่วนใหญ่น่าจะเป็น fork ของคลังอื่น และมีไว้แค่เพื่อสร้าง pull request ไปยังคลังหลัก จึงไม่น่าจะมี issue เว้นแต่จะเผลอทำผิดพลาด
    โปรเจ็กต์เล็ก ๆ แบบของเล่นหรือโปรเจ็กต์ที่ถูกทิ้งอย่างรวดเร็ว ก็น่าจะไม่มี issue หรือมีน้อยมาก แน่นอนว่ามีโปรเจ็กต์ที่มี issue หลักร้อยหรือหลักพันอยู่ แต่ค่าเฉลี่ยของคลังทั้งหมด 128 ล้านคลังน่าจะค่อนข้างต่ำ จึงมีแนวโน้มว่าจะยังอยู่ต่ำกว่า ขีดจำกัด 2 พันล้าน ถึงอย่างนั้นก็เห็นด้วยว่าการใช้ชนิดข้อมูล 4 ไบต์ หรือพูดให้แม่นคือ 31 บิต ในตารางนั้น เป็นระเบิดเวลาสำหรับบางองค์กรรวมถึง github.com

    • ตอนนี้ก็ยังต่ำกว่าขีดจำกัดอยู่ โดยมี 362,107,148 คลัง และ issue กับ pull request แบบไม่ซ้ำกัน 818,516,506 รายการ
      https://play.clickhouse.com/play?user=play#U0VMRUNUIHVuaXEoc...
    • ผมคิดว่าการที่ GitHub ตัดสินใจค่อย ๆ ห่างจาก Rails ก็ได้รับอิทธิพลบางส่วนจากข้อบกพร่องใหญ่ของ ActiveRecord ด้วย นั่นคือการไม่รองรับ คีย์หลักแบบประกอบ
      ความต้องการพื้นฐานอย่าง PRIMARY KEY(repo_id, issue_id) กลับซับซ้อนเกินจำเป็นใน ActiveRecord และต้องอ้อมด้วยการใช้ทั้งคีย์ที่ไม่ซ้ำและคีย์หลักแยกต่างหากเพื่อให้เข้ากับ ActiveRecord ที่ต้องการคอลัมน์คีย์หลักเดี่ยว UUID เป็นคีย์หลักก็ดูเหมือนทางอ้อมอีกแบบ แต่ก็ยังต้องมีข้อจำกัดความไม่ซ้ำของคู่ (repo_id, issue_id) อยู่ดี ทำให้ขนาดฐานข้อมูลและโอเวอร์เฮดเพิ่มขึ้น มองให้กว้างขึ้น โครงสร้าง MVC แบบก้อนเดียว ของ Ruby on Rails ที่อิงกับชั้น model, controller และ view ชุดเดียว ก็สร้างปัญหาเรื่องการขยายตัวและการบำรุงรักษาเมื่อแอปพลิเคชันใหญ่ขึ้น และผมคิดว่า MVC เหมาะกับโครงสร้างแบบโมดูลาร์หรือแบบคอมโพเนนต์มากกว่า
    • สงสัยว่ามีใครรู้แน่ชัดไหมว่า GitLab Cloud ใช้ ฐานข้อมูลแบบหลายผู้เช่า หรือใช้ฐานข้อมูลแยกตามผู้ใช้/ลูกค้า/องค์กร
      ผลิตภัณฑ์ที่มีทั้งแบบ self-hosted และคลาวด์ มักชอบใช้ฐานข้อมูลแยกตามลูกค้า เพราะมันทำให้ส่วนของโค้ดเบสที่ใช้ร่วมกันง่ายกว่ามาก เนื่องจากสามารถใช้คิวรีเดียวกันได้ไม่ว่าจะเป็นการโฮสต์แบบไหน ถ้าเป็นฐานข้อมูลแยกตามลูกค้า ก็แทบไม่มีทางเข้าใกล้ขีดจำกัดการใช้งานแบบนั้น และถ้าเข้าใกล้จริง การโฮสต์เองก็น่าจะเหมาะกว่า
    • ระเบิดเวลาลูกนั้นเป็นระเบิดที่ปลดชนวนได้ด้วย มิเกรชัน 11 วินาที
    • การมิเกรตคีย์หลักจาก 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 เป็นหลักสิบหรือหลักร้อยแห่งทั่วทั้งฐานข้อมูล
    • ปัญหาคืออีก 5 คอลัมน์นั้นไม่ได้ถูกทำดัชนี
      ประสิทธิภาพฐานข้อมูลมีอยู่สามระดับ: 1) ทั้งดัชนีและข้อมูลอยู่ในหน่วยความจำ 2) ดัชนีอยู่ในหน่วยความจำแต่ข้อมูลไม่อยู่ 3) ทั้งดัชนีและข้อมูลไม่อยู่ในหน่วยความจำ แบบที่ 1 ดีที่สุด แต่ถ้าไม่ใช่ ก็ต้องพยายามรักษาแบบที่ 2 ไว้ให้สุดทาง การที่ ขนาดดัชนีเพิ่มเป็นสองเท่า ทำให้เรื่องนี้ยากขึ้น
    • คุณอาจมอง primary key ของฐานข้อมูลเป็น pointer พื้นฐาน คล้าย typedef void* ก็ได้ ขนาดของมันส่งผลต่อประสิทธิภาพโดยรวม ทั้งการใช้หน่วยความจำ/ดิสก์ คอขวดด้าน throughput และเวลา CPU ที่ใช้เปรียบเทียบคีย์ใน inner loop ชั้นลึกที่สุดของการ join และการ lookup
      ตอนที่ CPU x86-64 ออกรุ่นใหม่ ๆ ผลกระทบด้านประสิทธิภาพจากการย้ายไปใช้ pointer 64 บิตนั้นสูงมากจนเกิด x32/ilp32 ขึ้นมา และนั่นก็เป็นเหตุผลที่ .NET ยังตั้งค่าเริ่มต้นเป็น “prefer 32-bit” มาจนถึงตอนนี้ การใช้ UUID 128 บิต เป็น primary key ของฐานข้อมูลถือเป็นความผิดพลาดที่เลวร้ายมาก
    • UUIDv7 ก็ไม่ได้เป็นคำตอบสารพัดอย่าง ในหลายกรณีคุณไม่อยากให้ เวลาที่สร้าง ของ resource รั่วไหลออกไป
      ตัวอย่างเช่น คุณอาจอัปโหลดวิดีโอไว้ล่วงหน้าหนึ่งเดือนก่อนเผยแพร่ แต่ไม่ต้องการให้ผู้ชมรู้ข้อเท็จจริงนั้น
    • ยังมีแนวทางดัดแปลงอื่นของวิธีนี้ด้วย: https://pgxn.org/dist/permuteseq/
      และยังสามารถเข้ารหัสค่าเวลาเอาไปแสดงใน 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...

    • ในความเป็นจริง เมื่อถึงเวลาที่ต้องทำ schema migration ให้สอดคล้องกับการเปลี่ยนความยาวสตริงที่จัดเก็บ จะเกิดความต่างด้านประสิทธิภาพค่อนข้างมาก
      การเปลี่ยน varchar(300) เป็น varchar(200) ต้องเขียนทุกแถวใหม่ทั้งหมด แต่การอัปเดตข้อจำกัดของคอลัมน์ text แทบจะฟรีโดยพื้นฐาน และต้องการเพียงการสแกนทั้งตารางเพื่อตรวจสอบว่าค่าที่มีอยู่เดิมผ่านข้อจำกัดใหม่หรือไม่ ในบทความเองก็ระบุว่าการใช้ชนิด text ร่วมกับข้อจำกัด CHECK ทำให้การพัฒนาสคีมาง่ายกว่าการใช้ character varying หรือ varchar(n) เมื่อมีการตรวจสอบความยาว
  • คำกล่าวว่า foreign key มีต้นทุนสูง ถูกพูดซ้ำบ่อยมาก แต่ไม่ค่อยมี benchmark รองรับนัก
    แม้จะมีวิธีทำผิดได้หลายแบบ แต่ท้ายที่สุดแล้ว ที่ไหนสักแห่งในสแตกก็มีการบังคับใช้ integrity อยู่ดี ถ้าจะใช้ฐานข้อมูลให้เป็นประโยชน์แทนการไปเขียนเรื่องนี้ใหม่เอง ก็ต้องอาศัยความรู้และการทดลอง และโดยมากมันช่วยป้องกันอุบัติเหตุใหญ่ได้

  • สงสัยว่ามีใครเคยสรุปหรือสังเกต ความต่างด้านประสิทธิภาพ ระหว่าง GitLab กับ GitHub ไหม
    ทั้งคู่เป็นแอปพลิเคชันที่สร้างบน Rails แต่โดยรวมแล้วรู้สึกว่าเวลาโหลดหน้าเว็บของ GitLab แย่กว่า GitHub มาก

    • ตอนใช้ GitLab เมื่อหลายปีก่อน เคยเจอ ปัญหาประสิทธิภาพฝั่งไคลเอนต์ หนักมากกับ pull request ขนาดใหญ่ ส่วน GitHub ก็ไม่ได้สมบูรณ์แบบ แต่ยังจัดการได้ในระดับที่โอเค
    • การเทียบกับ GitHub คล้ายกับการเทียบ Chrome กับเบราว์เซอร์อื่น ๆ หรือแม้แต่เบราว์เซอร์ที่ใช้ Chromium
      Chrome กับ GitHub จะใช้ทุกกลเม็ดแม้มันจะทำร้ายผู้ใช้ก็ตาม ตัวอย่างเช่น เคยเปิด merge diff ใน GitHub ของบริษัทแล้วค้นหาด้วย Ctrl F ไม่เจอผลลัพธ์ เลยกดข้ามไปเรื่อย ๆ และคุ้ยประวัติ Git ด้วยมือ จนไปถึง diff ลำดับที่ 100 ถึงได้รู้ว่าไฟล์ที่สำคัญที่สุดถูกซ่อนไว้อยู่ลึกมาก ทั้งหมดนี้ก็น่าจะเพราะมีใครสักคนทำให้ตัวชี้วัดการโหลดหน้าเพจดูดีขึ้นแล้วได้เลื่อนตำแหน่งง่ายกว่า
    • GitHub โดยรวมเสถียรและมักจะค่อนข้างเร็ว ยกเว้นเหตุขัดข้องสองครั้งเมื่อปีที่แล้ว ถ้าไม่เป็นแบบนั้นก็คงไม่ใช้ คีย์ลัดบนคีย์บอร์ด
      มีโพสต์จากอดีตพนักงานที่อาจช่วยให้เข้าใจวัฒนธรรมของ 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 ล้านล้านค่า

    • ถ้าไม่ได้ต้องการแพ็กค่าให้เต็มจริง ๆ การเลือกขนาดที่ไม่ใช่ กำลังของ 2 ก็ดูไม่มีเหตุผล
  • การไม่ใช้ ID แบบ auto-increment อาจสมเหตุสมผล แต่ยังไม่ค่อยเข้าใจข้อดีของการมี ID สองตัว สำหรับใช้งานภายในและภายนอก
    มันเพิ่มจำนวนคอลัมน์และดัชนี ต้องคอย lookup ก่อนเสมอ และก็นึกสถานการณ์ด้านความปลอดภัยไม่ออกว่าจะเปลี่ยนคีย์ภายในแต่ไม่เปลี่ยนคีย์ภายนอกไปทำไม หรือว่าฉันพลาดอะไรไป?

    • เวลาเราทำอะไรในระดับโปรเจกต์ อย่างไรก็มีข้อมูลที่ต้องใช้อยู่แล้ว และสำหรับผู้ใช้ การให้ issue ของแต่ละโปรเจกต์เริ่มจาก 1 ก็ดูเป็นมิตรกว่าการเริ่มที่ตัวเลขอย่าง 2,703,000,571,7325
  • มีคนบอกว่าถ้าใช้ PostgreSQL native UUID v4 แทน bigserial ขนาดตารางจะใหญ่ขึ้น 25% และอัตราการแทรกจะเหลือเพียง 25% ของ bigserial เลยสงสัยว่าทำไม UUIDv4 ถึงแย่ขนาดนั้น
    UUID ก็เป็นแค่ ตัวเลข 128 บิต ไม่ใช่หรือ? ไม่รู้ว่าค่าใช้จ่ายในการสร้างมันแพงมากหรือจริง ๆ แล้วเกิดอะไรขึ้น

    • UUIDv4 เป็นแบบสุ่มล้วน และ ดัชนี B-tree คาดหวังค่าแบบ “เอนขวา” ที่มีลำดับสมเหตุสมผล
      เพราะแบบนี้การสร้างดัชนีบนคอลัมน์ UUIDv4 จึงช้าลง และนี่ก็เป็นแรงผลักดันให้มีการพัฒนา UUIDv6 กับ UUIDv7
    • การเพิ่มขนาด 25% นั้นจริง แต่เป็นการเพิ่มแบบเชิงเส้นที่เล็กและคาดเดาได้ คือ 8 ไบต์ต่อแถว เมื่อเทียบกับข้อมูลส่วนอื่นของแถวแล้วก็ไม่ใช่เรื่องที่น่ากังวลมากนัก
      ปัญหาใหญ่กว่าคืออัตราการแทรก สำหรับ UUID อัตราการแทรกถูกจำกัดโดยปริมาณ RAM ที่มีใช้งานได้ แต่จำนวนเต็มแบบ auto-increment ไม่เป็นแบบนั้น จำนวนเต็มมีความสัมพันธ์กับเวลา ส่วน UUID4 เป็นแบบสุ่ม ดังนั้นเมื่อสเกลใหญ่ขึ้น คุณลักษณะด้านประสิทธิภาพจะเปลี่ยนไปโดยพื้นฐาน ในตารางเล็ก ๆ ค่าปรับด้านการแทรกแทบมองข้ามได้ แต่เมื่อขนาดดัชนี B-tree แตะขีดจำกัดหน่วยความจำ PostgreSQL จะไม่สามารถเก็บ UUID B-tree ทั้งหมดไว้ในหน่วยความจำได้อีกต่อไป และต้องพึ่งการสลับหน้าเพจบนดิสก์ ส่วนจำนวนเต็มแบบ auto-increment แถวที่อยู่ใกล้กันตามเวลาจะใช้หน้าเพจดัชนีเดียวกัน จึงไม่ต้องแตะดิสก์ภายใต้ภาระงานเดียวกัน เมื่อถึงสเกลนั้น ความต่างจะไม่ใช่การช้าลงคงที่ 25% แต่เป็น หน้าผาด้านประสิทธิภาพ 25 เท่า และนอกจากทำ schema migration ก็แทบไม่มีทางออกนอกจากซื้อ RAM เพิ่ม
    • น่าจะเป็นเพราะ B-tree โดย B-tree กับ page จะทำงานได้ดีกว่าเมื่อมีการใช้งานหนักอยู่ที่หน้าสุดท้ายเป็นหลัก
      UUID ทำให้เกิดการเขียนแบบไม่เรียงลำดับจำนวนมาก จนนำไปสู่ page bloat
    • เมื่อการจัดเรียงกระจายแบบสุ่ม cache locality ของ B-tree ก็จะแย่ลง การแทรกจะไม่ไปลงที่หน้าสุดท้าย แต่กระจัดกระจายไปทั่ว
      locality ของการแทรกแบบ batch ก็ส่งผลเสียต่อการอ่านในภายหลังด้วย เพราะต้องไปตามหาเรคคอร์ดที่เกี่ยวข้องแบบสุ่ม สุดท้ายจึงต้องจ่ายต้นทุนทั้งตอนแทรกและตอน select ภายหลัง