63 คะแนน โดย GN⁺ 2025-04-01 | 4 ความคิดเห็น | แชร์ทาง WhatsApp
  • บทความที่รวบรวมแพตเทิร์นเชิงปฏิบัติที่ช่วยให้ใช้ Postgres ได้อย่างมีประสิทธิภาพและปลอดภัยมากขึ้น
  • แต่ละแพตเทิร์นอาจดูเล็กน้อย แต่เมื่อสะสมแล้วสร้างความแตกต่างได้มาก

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

  • UUID เป็นแบบสุ่ม จึงมีข้อเสียในแง่ของการเรียงลำดับและประสิทธิภาพของดัชนี
  • ใช้พื้นที่มากกว่า ID แบบตัวเลข
  • แต่ก็มีข้อดีดังนี้
    • สร้าง UUID ได้โดยไม่ต้องเชื่อมต่อกับ DB
    • เปิดเผยต่อภายนอกได้อย่างปลอดภัย
  • สามารถใช้ gen_random_uuid() เพื่อสร้าง UUID เป็นคีย์หลักโดยอัตโนมัติได้

เพิ่มฟิลด์ created_at และ updated_at เสมอ

  • ตอนดีบัก การรู้เวลาที่เรคคอร์ดถูกสร้างและแก้ไขนั้นมีประโยชน์มาก
  • สามารถตั้งค่าให้ updated_at อัปเดตอัตโนมัติผ่าน trigger ได้
  • ฟังก์ชันสร้างเพียงครั้งเดียวได้ แต่ต้องนำ trigger ไปใช้กับแต่ละตาราง

ตั้งค่า on update/delete restrict ให้กับ foreign key

  • เมื่อตั้งค่าเงื่อนไข foreign key ควรใช้ on update restrict on delete restrict เสมอ
  • ช่วยป้องกันไม่ให้เกิดการลบต่อเนื่องโดยไม่ตั้งใจเมื่อลบข้อมูล
  • พื้นที่จัดเก็บมีราคาถูก แต่การกู้คืนข้อมูลทำได้ยากมาก จึงควรจัดการอย่างระมัดระวัง

แนะนำให้ใช้ schema

  • schema เริ่มต้นคือ public แต่เมื่อแอปพลิเคชันใหญ่ขึ้น ควรแยกออกเป็น schema เฉพาะ
  • schema ทำงานคล้าย namespace และสามารถ join ข้าม schema กันได้
  • ยิ่งมีจำนวนตารางมาก การใช้ schema จะยิ่งดีต่อความอ่านง่ายและการบำรุงรักษา

ใช้แพตเทิร์นตาราง Enum

  • การใช้ตาราง enum แทน enum type หรือ check constraint ของ PostgreSQL มีความยืดหยุ่นมากกว่า
  • เมื่อจัดการค่า enum ในตารางแยก จะสามารถเพิ่ม metadata หรือขยายค่า enum ได้ง่าย
  • ใช้ foreign key อ้างอิงค่าจากตาราง enum เพื่อคงข้อจำกัดของข้อมูล

ตั้งชื่อตารางเป็นเอกพจน์

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

ตั้งชื่อ join table แบบเป็นกลไก

  • join table สำหรับความสัมพันธ์แบบ many-to-many ควรตั้งชื่อโดยนำชื่อตารางทั้งสองมาต่อกัน เพื่อความปลอดภัยและชัดเจน
  • ตัวอย่าง: person_pet
  • เพิ่ม unique index ให้กับคู่ค่าผสมเพื่อป้องกันข้อมูลซ้ำ

ใช้ soft delete แทนการลบจริง

  • แทนที่จะลบข้อมูลจริง ควรใช้ฟิลด์ timestamp เช่น revoked_at เพื่อระบุเวลาที่ลบ
  • ทำให้ติดตามได้ไม่ใช่แค่ว่าถูกลบหรือไม่ แต่ยังรู้ด้วยว่าถูกลบเมื่อไร
  • timestamp ให้ข้อมูลมากกว่า Boolean

แทนสถานะ (Status) ด้วยตารางล็อก

  • แทนที่จะแสดงสถานะด้วยคอลัมน์เดียว ให้เก็บประวัติการเปลี่ยนสถานะไว้ในตารางแยก
  • ระบุเวลาที่สถานะเกิดขึ้นด้วยคอลัมน์ valid_at
  • ตั้งค่าแฟล็ก latest พร้อม unique index และ trigger เพื่อให้ดึงสถานะล่าสุดได้อย่างรวดเร็ว
  • วิธีนี้ได้เปรียบในงานประมวลผลเหตุการณ์แบบอะซิงโครนัสหรือกรณีที่ลำดับเหตุการณ์อาจสลับกัน

เพิ่ม system_id ให้กับแถวพิเศษ

  • นอกจากตาราง enum แล้ว บางครั้งยังต้องมี "แถวของระบบ" โดยเฉพาะ
  • เพิ่มฟิลด์ข้อความ system_id แบบ nullable และตั้งค่า unique index
  • ใช้ system_id เพื่อค้นหาแถวเฉพาะนั้นได้อย่างชัดเจน

ใช้ View ให้น้อยที่สุด

  • View มีประโยชน์ในการทำคิวรีซับซ้อนให้เป็นนามธรรม แต่ดูแลรักษายาก
    • เมื่อลบคอลัมน์ ต้องสร้าง View ใหม่
    • ถ้าสร้าง View ซ้อนบน View จะเกิดปัญหาทั้งด้านประสิทธิภาพและความอ่านง่าย
  • จึงควรใช้อย่างระมัดระวังและเท่าที่จำเป็น

ใช้คิวรี JSON อย่างเต็มที่

  • Postgres ไม่ได้เก่งแค่การเก็บ JSON แต่ยังทรงพลังมากในการคิวรีเพื่อคืนค่าเป็น JSON
  • สามารถคืนค่าความสัมพันธ์แบบซ้อนเป็น JSON ได้ด้วยคิวรีเพียงครั้งเดียว
  • ดึงข้อมูลที่ต้องใช้ทั้งหมดได้ในครั้งเดียวโดยไม่เจอปัญหา N+1
  • ข้อเสีย: ข้อมูลชนิดสูญหาย ต้องโหลดข้อมูลทั้งหมดเข้าเมมโมรีพร้อมกัน
  • แต่ในด้านประสิทธิภาพหรือโครงสร้างแล้ว ข้อดีมีมากกว่า

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

 
jhj0517 2025-04-01

> ตั้งชื่อตาราง join แบบใช้กฎตายตัว

ผมว่าการมีหลักเกณฑ์แบบนี้ตอนตั้งชื่อก็ดีเหมือนกันครับ~

 
halfenif 2025-04-01

ถ้าพิจารณา UUID7 ก็น่าจะสามารถเรียงลำดับตามเวลาได้ไม่ใช่หรือ?

 
winterjung 2025-04-01

น่าจะลองอ่านบทความเกี่ยวกับ การใช้ UUID เป็นคีย์หลักใน PostgreSQL ด้วยเหมือนกันนะครับ

 
t7vonn 2025-04-01

วิธีใส่ timestamp ตอนทำ soft delete นี่ดีเลย
ถ้าใช้ UUID เป็นคีย์หลักก็จะเรียงตามเวลาไม่ได้ เลยคิดว่าการใช้ snowflake id หรือ ulid ก็น่าจะดีเหมือนกันครับ เพียงแต่ในกรณีนี้แต่ละเซิร์ฟเวอร์ก็ต้องถือ sequence number ไว้ด้วย