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

ออกแบบ MySQL Schema ให้ดูแลง่ายเมื่อระบบเติบโต

วาง naming convention, primary key, foreign key, data type, index และ normalization จาก query pattern จริง เพื่อป้องกันหนี้ทางโครงสร้างข้อมูลในระยะยาว

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

แชร์บทความ

ออกแบบ MySQL Schema ให้ดูแลง่ายเมื่อระบบเติบโต

ภาพรวม

Database Schema ที่ดูแลง่าย ไม่ได้เริ่มจากการตั้งชื่อเท่ๆ แต่เริ่มจากคำถามง่ายมาก “อีก 6 เดือน เราจะยังเข้าใจมันไหม” 🙂

ใน MySQL ของเว็บแอปทั่วไป เช่น ระบบสมาชิก คอร์สเรียน ออเดอร์ หรือแชตบอต ตารางมักโตเร็วกว่าโค้ด ถ้าออกแบบมั่วตั้งแต่แรก ทุก feature หลังจากนั้นจะจ่ายดอกเบี้ยทันที

เริ่มจากตั้งชื่อให้มนุษย์อ่านก่อน ใช้รูปแบบเดียวกันทั้งระบบ เช่น users, courses, orders, order_items คอลัมน์ใช้ snake_case created_at, updated_at, deleted_at อย่าสลับไปมาแบบ userId, user_id, uid เพราะคนไม่พังวันนี้ แต่ migration รอบหน้าพังแน่

Primary key ควรชัด ส่วนใหญ่ใช้ id BIGINT UNSIGNED AUTO_INCREMENT ก็พอ ถ้าระบบต้อง sync หลายที่ค่อยคิดเรื่อง UUID อย่าเลือกเทคโนโลยีเพราะดูโปร เลือกเพราะปัญหาจริงต้องใช้

Foreign key คือรั้วกันข้อมูลหลุด orders.user_id ควรอ้าง users.id order_items.order_id ควรอ้าง orders.id ถ้าไม่ใส่เลย วันหนึ่งจะเจอ order ที่ไม่มี user item ที่ไม่มี order แล้ว dashboard จะเริ่มโกหกเราแบบเงียบๆ

แต่ foreign key ต้องคิด action ให้ดี ลบ user แล้ว order หายไหม ส่วนใหญ่ไม่ควร อาจใช้ RESTRICT หรือ soft delete แทน ข้อมูลธุรกรรมควรเก็บประวัติ ไม่ใช่สะอาดจน audit ไม่ได้

เรื่องชนิดข้อมูลอย่ามองข้าม เงินอย่าใช้ FLOAT ใช้ DECIMAL(10,2) สถานะใช้ ENUM ได้ถ้าค่าค่อนข้างนิ่ง เช่น pending, paid, cancelled แต่ถ้าธุรกิจเปลี่ยนบ่อย ทำตาราง statuses อาจดูแลง่ายกว่า

Index ไม่ใช่ของตกแต่ง ใส่ตาม query จริง ถ้าหน้า admin ค้น order ด้วย user_id และ created_at ก็ต้องมี index ที่ช่วยเส้นทางนั้น เช่น INDEX idx_orders_user_created (user_id, created_at) แต่อย่าใส่ทุกคอลัมน์ เพราะเขียนข้อมูลช้าลง และ schema รกขึ้น

Normalize ก่อน แล้ว denormalize เมื่อมีเหตุผล ตาราง users ไม่ควรเก็บ course_name_1, course_name_2 ให้ใช้ enrollments เชื่อม users กับ courses แต่ถ้าหน้า report ช้ามาก ค่อยสร้าง summary table หรือ cache column พร้อมเขียนเหตุผลไว้ใน migration

คอลัมน์เวลาสำคัญมาก created_at บอกว่าเกิดเมื่อไร updated_at บอกว่าแก้ล่าสุดเมื่อไร deleted_at ช่วย soft delete สำหรับงานจริง เพิ่ม created_by, updated_by ได้ โดยเฉพาะระบบหลังบ้านที่หลายคนดูแล

อย่ายัด JSON เพราะขี้เกียจออกแบบ MySQL รองรับ JSON ก็จริง แต่ถ้าต้อง query, filter, join บ่อย ควรแตกเป็นตารางปกติ JSON เหมาะกับ metadata ที่ไม่ตายตัว ไม่ใช่ที่หลบหนี้ของ schema

อีกอย่างที่ช่วยทีมมากคือ constraint email ควร UNIQUE จำนวนสินค้า CHECK (quantity > 0) แม้โค้ด validate แล้ว database ก็ควรเป็นด่านสุดท้าย เพราะ bug ไม่ได้เข้าทางหน้าเว็บเสมอไป

สุดท้าย เขียน schema เหมือนเขียน API ให้คนอื่นใช้ มี naming convention มี migration เป็นลำดับ มี ERD เบาๆ มี comment เฉพาะจุดที่แปลก และมีตัวอย่าง query สำคัญ

Database ที่ดีไม่ใช่แค่เร็ว แต่ต้องทำให้คนต่อไปกล้าแก้ ถ้าเปิด schema แล้วทีมเข้าใจ flow ธุรกิจได้ นั่นแหละคือฐานข้อมูลที่ดูแลง่ายตั้งแต่แรก 🚀

แนวทางนำไปใช้

  • นำแนวคิดจากโพสต์นี้ไปทดลองกับงานจริงของคุณ
  • แยกเป็นขั้นตอนเล็ก ๆ แล้วทำทีละส่วน
  • บันทึกผลลัพธ์และสิ่งที่เรียนรู้เพื่อต่อยอด

สรุป

วาง naming convention, primary key, foreign key, data type, index และ normalization จาก query pattern จริง เพื่อป้องกันหนี้ทางโครงสร้างข้อมูลในระยะยาว