กลับไปหน้าบทความ
#PostgreSQL#JSONB#Database Design#Schema#Backend

PostgreSQL JSONB: ใช้อย่างไรให้ยืดหยุ่นโดยไม่ทำให้ schema เละ

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

29 กรกฎาคม 2569อ่านประมาณ 2 นาที

แชร์บทความ

PostgreSQL JSONB: ใช้อย่างไรให้ยืดหยุ่นโดยไม่ทำให้ schema เละ

PostgreSQL JSONB: ใช้อย่างไรให้ยืดหยุ่นโดยไม่ทำให้ schema เละ

หลายทีมเริ่มต้นออกแบบฐานข้อมูลด้วยตารางที่เป็นระเบียบ แยกคอลัมน์ชัดเจน และดูแลง่าย แต่เมื่อระบบเริ่มเติบโต ความต้องการทางธุรกิจก็มักเพิ่มฟิลด์พิเศษเข้ามาเรื่อย ๆ จนเกิดปัญหาคอลัมน์พองตัว โตเร็ว และยากต่อการดูแล

ในสถานการณ์แบบนี้ JSONB ของ PostgreSQL มักกลายเป็นตัวเลือกยอดนิยม เพราะเหมาะกับข้อมูลที่มีโครงสร้างยืดหยุ่น เช่น metadata, settings, event payload, attributes ของสินค้า หรือข้อมูลที่รับมาจากหลายแหล่ง

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

หลักคิดสำคัญ: แยกข้อมูลแกนธุรกิจกับข้อมูลเสริม

แนวคิดที่ใช้งานได้จริงคือ:

  • เก็บข้อมูลที่เป็นแกนธุรกิจไว้ในคอลัมน์ปกติ
  • เก็บข้อมูลที่เปลี่ยนบ่อย หรือมีเฉพาะบางกรณีไว้ใน JSONB

ตัวอย่างเช่นตาราง orders

  • order_id, customer_id, status, created_at ควรเป็นคอลัมน์ปกติ
  • metadata ควรเป็น JSONB สำหรับข้อมูลเสริม เช่น source campaign, shipping note หรือ device info

การแยกแบบนี้ช่วยให้:

  • ค้นหาข้อมูลได้ง่ายกว่า
  • ออกแบบ index ได้แม่นยำกว่า
  • บังคับกฎธุรกิจได้ชัดเจนกว่า
  • เขียน query เพื่อทำรายงานได้ง่ายขึ้นมาก

สิ่งที่ไม่ควรทำกับ JSONB

ข้อผิดพลาดที่พบบ่อยคือเอาทุกอย่างไปเก็บใน JSONB แม้แต่ข้อมูลสำคัญอย่าง status, price หรือ created_at

เมื่อทำแบบนั้น ปัญหาที่ตามมามักมีลักษณะนี้:

  • ชื่อ key ไม่ตรงกัน เช่น userId, user_id, uid
  • ชนิดข้อมูลไม่นิ่ง เช่น "10", 10, หรือ "สิบ"
  • ค่าที่ควรเป็นมาตรฐานเดียวกันกลับไม่สม่ำเสมอ เช่น THB กับ thb
  • วันเวลาเก็บคนละรูปแบบจน query ยุ่งยาก

JSONB จึงไม่ควรถูกใช้แทนการออกแบบ schema แต่ควรใช้เพื่อรองรับข้อมูลที่ยืดหยุ่นอย่างมีขอบเขต

วิธีคุม schema ของ JSONB ให้อยู่หมัด

1) ตั้ง contract ของ key ให้ชัด

แม้ PostgreSQL จะไม่บังคับ schema ของ JSONB แบบเต็มรูปแบบ แต่ทีมควรมีเอกสารกลางที่ระบุให้ชัดว่า:

  • ใช้ key อะไรบ้าง
  • แต่ละ key มีชนิดข้อมูลอะไร
  • field ไหนเป็น optional

ตัวอย่างเช่น:

  • metadata.source ต้องเป็น text
  • metadata.device.os ต้องเป็น text
  • metadata.tags ต้องเป็น array ของ text

การมี contract ชัดเจนช่วยลดความคลุมเครือ และทำให้ทั้งทีมเขียนข้อมูลได้สอดคล้องกัน

2) แยก version ของ payload

หากโครงสร้างข้อมูลมีโอกาสเปลี่ยน ควรเก็บ schema_version ไว้ใน JSONB หรือแยกเป็นอีกคอลัมน์หนึ่ง

ข้อดีคือ:

  • รองรับข้อมูลหลายรุ่นได้ง่าย
  • migration ข้อมูลทำได้เป็นระบบ
  • ไม่ต้องเดาโครงสร้างย้อนหลังเมื่อระบบโตขึ้น

วิธีนี้สำคัญมากในระบบที่ payload จากภายนอกเปลี่ยนแปลงบ่อย หรือมีการพัฒนาฟีเจอร์อย่างต่อเนื่อง

3) ใช้ CHECK constraint เพื่อบังคับกฎบางส่วน

แม้ JSONB จะยืดหยุ่น แต่ PostgreSQL ก็ยังช่วยตรวจสอบเงื่อนไขบางอย่างได้ผ่าน CHECK constraint

ตัวอย่างแนวคิด เช่น:

  • metadata ต้องมี key ชื่อ source
  • ถ้ามี priority ค่าต้องเป็นตัวเลข

วิธีนี้ช่วยกันข้อมูลผิดรูปแบบตั้งแต่ระดับฐานข้อมูล ไม่ปล่อยให้ภาระทั้งหมดไปตกอยู่ที่แอปพลิเคชัน

4) ใช้ generated column เมื่อมี field ที่ถูก query บ่อย

ถ้ามี field ใน JSONB ที่ถูกใช้ filter เป็นประจำ เช่น metadata->>'source' การแยกค่าออกมาเป็น generated column แล้วทำ index จะช่วยได้มาก

ข้อดีคือ:

  • query เร็วขึ้น
  • โค้ดอ่านง่ายขึ้น
  • ลดการดึงค่าจาก JSONB ซ้ำ ๆ ในทุก query

แนวทางนี้เหมาะมากกับข้อมูลที่ยังอยากเก็บต้นทางไว้ใน JSONB แต่มีบาง field ที่เริ่มถูกใช้งานแบบกึ่งโครงสร้างถาวรแล้ว

5) ทำ index เท่าที่จำเป็น

JSONB index มีความสามารถสูง แต่ไม่ใช่ว่ายิ่งมีมากยิ่งดี การสร้าง index ควรอ้างอิงจากรูปแบบ query ที่ใช้งานจริง เช่น:

  • ค้นหาว่ามี key ที่ต้องการหรือไม่
  • ค้นหาค่าภายในบาง field
  • filter จาก attribute ที่ใช้บ่อย

ถ้าสร้าง index แบบไม่วางแผน อาจทำให้ต้นทุนการเขียนข้อมูลสูงขึ้นโดยไม่คุ้มค่า

6) normalize เมื่อข้อมูลเริ่มมีความสัมพันธ์ชัด

เมื่อข้อมูลใน JSONB เริ่มมีลักษณะดังนี้:

  • ถูก join บ่อย
  • ถูกอัปเดตแยกส่วนบ่อย
  • กลายเป็นแกนธุรกิจของระบบ

นั่นคือสัญญาณว่าควรย้ายข้อมูลกลับมาเป็นตารางปกติ

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

สัญญาณเตือนว่า JSONB เริ่มถูกใช้เกินพอดี

หากทีมเริ่มเจอสถานการณ์ต่อไปนี้ ควรกลับมาทบทวนการออกแบบ:

  • ทีมเดียวกันเขียน query คนละแบบสำหรับข้อมูลเดียวกัน
  • ต้อง cast ค่าเดิมซ้ำ ๆ ทุกครั้ง
  • มี index เพิ่มขึ้นเรื่อย ๆ แต่ query ยังช้า
  • logic ตรวจสอบข้อมูลไปกองอยู่ในแอปทั้งหมด

สัญญาณเหล่านี้มักแปลว่า JSONB กำลังถูกใช้แทน schema ที่ควรออกแบบให้ชัดตั้งแต่ต้น

แนวทางออกแบบที่ใช้งานได้จริง

เหมาะจะใช้คอลัมน์ปกติสำหรับ

  • id
  • status
  • type
  • amount
  • created_at
  • updated_at

ข้อมูลเหล่านี้มักเป็นแกนธุรกิจ ถูกใช้อ้างอิงบ่อย และควรมีชนิดข้อมูลที่ชัดเจน

เหมาะจะใช้ JSONB สำหรับ

  • display preferences
  • raw webhook payload
  • feature flags ราย user
  • ข้อมูลเสริมที่ยังไม่คงรูป

ข้อมูลกลุ่มนี้มักเปลี่ยนแปลงง่าย มีโครงสร้างไม่แน่นอน หรือมีเฉพาะบางกรณี

JSONB ควรให้ความยืดหยุ่น ไม่ใช่เพิ่มความคลุมเครือ

อีกจุดที่หลายคนมองข้ามคือ อย่าใช้ JSONB เป็นข้ออ้างในการไม่ออกแบบ schema

JSONB เหมาะกับการรองรับความเปลี่ยนแปลง แต่ไม่ได้หมายความว่าควรปล่อยให้ข้อมูลไม่มีมาตรฐาน หากเริ่มต้นดี PostgreSQL สามารถให้ทั้งสองอย่างพร้อมกันได้ คือ:

  • ความยืดหยุ่นแบบ document
  • ความเข้มแข็งของ relational database

สรุป

การใช้ JSONB อย่างมีวินัยช่วยให้ฐานข้อมูลรองรับความยืดหยุ่นได้โดยไม่สูญเสียความเป็นระเบียบ

แนวทางสำคัญคือ:

  • เก็บข้อมูลแกนธุรกิจไว้ในคอลัมน์ปกติ
  • ใช้ JSONB สำหรับข้อมูลเสริมที่เปลี่ยนบ่อย
  • กำหนด contract ของ key ให้ชัด
  • แยก version เมื่อโครงสร้างมีโอกาสเปลี่ยน
  • ใช้ constraint และ index เท่าที่จำเป็น
  • รีบ normalize เมื่อข้อมูลเริ่มนิ่งและมีความสัมพันธ์ชัด

หากออกแบบตั้งแต่วันแรกอย่างมีหลักคิด ฐานข้อมูลจะเติบโตได้อย่างเป็นระเบียบ ไม่กลายเป็นกล่องสุ่มใน production